From f3ae5af63f6d58a3473b25c6b7ab3f0260e5c373 Mon Sep 17 00:00:00 2001 From: Muhammad Yousuf Date: Thu, 13 Feb 2025 16:35:27 -0500 Subject: [PATCH 1/4] Installation guide updates/fixing link to installations --- README.md | 2 +- .../getting-started/getting-started/index.html | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 62d4a0ef..209bd44c 100755 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Among other things, this project also does not provide methods for trajectory op # Installation -See the [documentation](https://cloctools.github.io/docs/getting-started). +See the [documentation](https://cloctools.github.io/lds-ctrl-est/docs/getting-started/getting-started/). # Reporting Issues If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please [open an issue on GitHub](https://github.com/cloctools/lds-ctrl-est/issues) and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes. diff --git a/docs/docs/getting-started/getting-started/index.html b/docs/docs/getting-started/getting-started/index.html index 85282b68..5f3de8bf 100644 --- a/docs/docs/getting-started/getting-started/index.html +++ b/docs/docs/getting-started/getting-started/index.html @@ -216,14 +216,14 @@

xcode-select --install
 

Homebrew is “The Missing Package Manager for macOS” which will make installing lots of things easy. Install like this:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-

You can then use it to install CMake and gfortran:

-
brew install cmake gfortran
+

You can then use it to install CMake, gfortran, and pkg-config:

+
brew install cmake gfortran pkg-config
 

Linux Pre-requisites #

You’ll need Git, CMake, GCC, gfortran, etc.

-
sudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential
+
sudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build
 

Windows Installation # @@ -234,17 +234,17 @@

#

First, clone the repository along with submodules:

-
git clone https://github.com/cloctools/lds-ctrl-est.git 
-cd lds-ctrl-est
-git submodule update --init
+
git clone https://github.com/cloctools/lds-ctrl-est.git 
+cd lds-ctrl-est
+git submodule update --init
 

Compilation + Installation #

Now generate the cache and build using your IDE or from the command line as follows.

-
mkdir build && cd build
-cmake ..
-cmake --build .
+
mkdir build && cd build
+cmake ..
+cmake --build .
 

The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.

If you want to use vcpkg set up somewhere besides this repo’s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE’s settings.

You can verify the build is working by running ctest from the build folder, which runs all the example scripts.

From a4a9733c05f8e7bf0878e90f268f68c7dd65e8a9 Mon Sep 17 00:00:00 2001 From: Muhammad Yousuf Date: Fri, 7 Mar 2025 14:42:48 -0500 Subject: [PATCH 2/4] Installation docs built with hugo --- README.md | 3 + docs/404.html | 52 - docs/acknowledgements/index.html | 241 --- docs/categories/index.html | 269 --- docs/categories/index.xml | 9 - docs/categories/page/1/index.html | 1 - ...lasslds_1_1_controller__inherit__graph.png | Bin 28819 -> 0 bytes docs/classlds_1_1_system__inherit__graph.png | Bin 7184 -> 0 bytes .../classes/classlds_1_1controller/index.html | 1158 ------------ .../api/classes/classlds_1_1em/index.html | 966 ---------- .../api/classes/classlds_1_1fit/index.html | 907 ---------- .../index.html | 560 ------ .../classlds_1_1gaussian_1_1fit/index.html | 553 ------ .../classlds_1_1gaussian_1_1fitem/index.html | 464 ----- .../index.html | 392 ----- .../index.html | 658 ------- .../classlds_1_1gaussian_1_1system/index.html | 836 --------- .../index.html | 560 ------ .../classlds_1_1poisson_1_1fit/index.html | 553 ------ .../classlds_1_1poisson_1_1fitem/index.html | 464 ----- .../classlds_1_1poisson_1_1fitssid/index.html | 392 ----- .../index.html | 658 ------- .../classlds_1_1poisson_1_1system/index.html | 714 -------- .../api/classes/classlds_1_1ssid/index.html | 712 -------- .../classlds_1_1switchedcontroller/index.html | 846 --------- .../api/classes/classlds_1_1system/index.html | 1288 -------------- .../classlds_1_1uniformmatrixlist/index.html | 519 ------ .../classlds_1_1uniformsystemlist/index.html | 512 ------ .../classlds_1_1uniformvectorlist/index.html | 503 ------ docs/docs/api/classes/index.html | 319 ---- docs/docs/api/classes/index.xml | 225 --- .../eg_glds_ctrl_8cpp-example/index.html | 501 ------ .../index.html | 502 ------ .../eg_plds_ctrl_8cpp-example/index.html | 471 ----- .../eg_plds_est_8cpp-example/index.html | 421 ----- .../index.html | 466 ----- docs/docs/api/examples/index.html | 263 --- docs/docs/api/examples/index.xml | 59 - .../index.html | 341 ---- .../index.html | 278 --- .../index.html | 278 --- .../index.html | 266 --- .../api/files/eg__glds__ctrl_8cpp/index.html | 552 ------ .../eg__glds__du__plds__ctrl_8cpp/index.html | 553 ------ .../api/files/eg__plds__ctrl_8cpp/index.html | 522 ------ .../api/files/eg__plds__est_8cpp/index.html | 484 ------ .../eg__plds__switched__ctrl_8cpp/index.html | 514 ------ docs/docs/api/files/index.html | 365 ---- docs/docs/api/files/index.xml | 372 ---- docs/docs/api/files/lds_8cpp/index.html | 405 ----- docs/docs/api/files/lds_8h/index.html | 427 ----- docs/docs/api/files/lds__ctrl_8h/index.html | 808 --------- docs/docs/api/files/lds__fit_8h/index.html | 408 ----- .../docs/api/files/lds__fit__em_8h/index.html | 891 ---------- .../api/files/lds__fit__ssid_8h/index.html | 837 --------- .../api/files/lds__gaussian_8h/index.html | 316 ---- .../files/lds__gaussian__ctrl_8h/index.html | 375 ---- .../files/lds__gaussian__fit_8h/index.html | 353 ---- .../lds__gaussian__fit__em_8h/index.html | 349 ---- .../lds__gaussian__fit__ssid_8h/index.html | 349 ---- .../files/lds__gaussian__sctrl_8h/index.html | 373 ---- .../files/lds__gaussian__sys_8cpp/index.html | 329 ---- .../files/lds__gaussian__sys_8h/index.html | 385 ---- .../docs/api/files/lds__poisson_8h/index.html | 320 ---- .../files/lds__poisson__ctrl_8h/index.html | 380 ---- .../api/files/lds__poisson__fit_8h/index.html | 361 ---- .../files/lds__poisson__fit__em_8h/index.html | 354 ---- .../lds__poisson__fit__ssid_8h/index.html | 348 ---- .../files/lds__poisson__sctrl_8h/index.html | 376 ---- .../files/lds__poisson__sys_8cpp/index.html | 325 ---- .../api/files/lds__poisson__sys_8h/index.html | 365 ---- docs/docs/api/files/lds__sctrl_8h/index.html | 532 ------ docs/docs/api/files/lds__sys_8cpp/index.html | 371 ---- docs/docs/api/files/lds__sys_8h/index.html | 441 ----- .../files/lds__uniform__mats_8h/index.html | 587 ------- .../files/lds__uniform__systems_8h/index.html | 525 ------ .../files/lds__uniform__vecs_8cpp/index.html | 350 ---- .../files/lds__uniform__vecs_8h/index.html | 449 ----- .../docs/api/files/mex__c__util_8h/index.html | 375 ---- .../api/files/mex__cpp__util_8h/index.html | 396 ----- .../modules/group__control__masks/index.html | 321 ---- .../api/modules/group__defaults/index.html | 322 ---- docs/docs/api/modules/index.html | 251 --- docs/docs/api/modules/index.xml | 29 - docs/docs/api/namespaces/index.html | 259 --- docs/docs/api/namespaces/index.xml | 54 - .../namespaces/namespacearmamexc/index.html | 373 ---- .../namespaces/namespacearmamexcpp/index.html | 452 ----- .../api/namespaces/namespacelds/index.html | 792 --------- .../namespacelds_1_1gaussian/index.html | 289 --- .../namespacelds_1_1poisson/index.html | 340 ---- docs/docs/api/pages/index.html | 241 --- docs/docs/api/pages/index.xml | 9 - .../getting-started/index.html | 397 ----- .../getting-started/linux-macos/index.html | 324 ---- docs/docs/getting-started/windows/index.html | 309 ---- docs/docs/index.html | 239 --- docs/docs/index.xml | 50 - .../terminology/control-estimation/index.html | 363 ---- docs/docs/terminology/model/index.html | 323 ---- .../docs/tutorials/eg_glds_control/index.html | 435 ----- .../eg_plds_state_estimation/index.html | 366 ---- .../eg_switched_plds_control/index.html | 408 ----- docs/docs/tutorials/index.html | 239 --- docs/docs/tutorials/index.xml | 38 - docs/eg_glds_ctrl_output.png | Bin 169071 -> 0 bytes docs/eg_plds_ctrl_output.png | Bin 217595 -> 0 bytes docs/eg_plds_est_output.png | Bin 148988 -> 0 bytes docs/eg_plds_est_output_hist.png | Bin 29070 -> 0 bytes docs/eg_plds_switched_ctrl_output.png | Bin 156078 -> 0 bytes ...bea53e75365846aca80f6f94cdec83d505cec.json | 1 - ...ae550679543dfef2bae61c8d816ffca12f5c7.json | 1 - ...d55018a6e72b6e0fd48f0583a54196c1ecb52.json | 1 - ...f83b0207389c62d2c3a11b31bc18fbe5810bb.json | 1 - ...a205c919f3fc365f221e7bb9b64945879058e.json | 1 - ...1f53d5c11f852b9340eb3cbaa440fc0fe4ceb79.js | 1 - ...1dd22ab5b97100c454eaf8b746c3f9cc7f190f1.js | 1 - ...402ebca9cbcdf5bb90e37c4cb24206214b0414e.js | 1 - ...e5a38c14b24e837def6c5f2027bc5a181d2fae0.js | 1 - ...a9e6e0c0ce7a384100570924726e75a52c21d85.js | 1 - docs/favicon.png | Bin 109 -> 0 bytes docs/favicon.svg | 1 - docs/flexsearch.min.js | 42 - docs/fonts/roboto-mono-v13-latin-regular.woff | Bin 15160 -> 0 bytes .../fonts/roboto-mono-v13-latin-regular.woff2 | Bin 12312 -> 0 bytes docs/fonts/roboto-v27-latin-700.woff | Bin 20396 -> 0 bytes docs/fonts/roboto-v27-latin-700.woff2 | Bin 15828 -> 0 bytes docs/fonts/roboto-v27-latin-regular.woff | Bin 20332 -> 0 bytes docs/fonts/roboto-v27-latin-regular.woff2 | Bin 15688 -> 0 bytes docs/index.html | 287 --- docs/index.xml | 792 --------- docs/issues-contributing/index.html | 250 --- docs/katex/auto-render.min.js | 1 - docs/katex/fonts/KaTeX_AMS-Regular.ttf | Bin 70972 -> 0 bytes docs/katex/fonts/KaTeX_AMS-Regular.woff | Bin 38868 -> 0 bytes docs/katex/fonts/KaTeX_AMS-Regular.woff2 | Bin 32944 -> 0 bytes docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf | Bin 19316 -> 0 bytes docs/katex/fonts/KaTeX_Caligraphic-Bold.woff | Bin 11696 -> 0 bytes docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 | Bin 10448 -> 0 bytes .../katex/fonts/KaTeX_Caligraphic-Regular.ttf | Bin 18684 -> 0 bytes .../fonts/KaTeX_Caligraphic-Regular.woff | Bin 11460 -> 0 bytes .../fonts/KaTeX_Caligraphic-Regular.woff2 | Bin 10240 -> 0 bytes docs/katex/fonts/KaTeX_Fraktur-Bold.ttf | Bin 35660 -> 0 bytes docs/katex/fonts/KaTeX_Fraktur-Bold.woff | Bin 22632 -> 0 bytes docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 | Bin 20360 -> 0 bytes docs/katex/fonts/KaTeX_Fraktur-Regular.ttf | Bin 34352 -> 0 bytes docs/katex/fonts/KaTeX_Fraktur-Regular.woff | Bin 22088 -> 0 bytes docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 | Bin 19784 -> 0 bytes docs/katex/fonts/KaTeX_Main-Bold.ttf | Bin 60784 -> 0 bytes docs/katex/fonts/KaTeX_Main-Bold.woff | Bin 35464 -> 0 bytes docs/katex/fonts/KaTeX_Main-Bold.woff2 | Bin 30244 -> 0 bytes docs/katex/fonts/KaTeX_Main-BoldItalic.ttf | Bin 44496 -> 0 bytes docs/katex/fonts/KaTeX_Main-BoldItalic.woff | Bin 25352 -> 0 bytes docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 | Bin 21944 -> 0 bytes docs/katex/fonts/KaTeX_Main-Italic.ttf | Bin 47640 -> 0 bytes docs/katex/fonts/KaTeX_Main-Italic.woff | Bin 26228 -> 0 bytes docs/katex/fonts/KaTeX_Main-Italic.woff2 | Bin 22748 -> 0 bytes docs/katex/fonts/KaTeX_Main-Regular.ttf | Bin 69520 -> 0 bytes docs/katex/fonts/KaTeX_Main-Regular.woff | Bin 38112 -> 0 bytes docs/katex/fonts/KaTeX_Main-Regular.woff2 | Bin 32464 -> 0 bytes docs/katex/fonts/KaTeX_Math-BoldItalic.ttf | Bin 39308 -> 0 bytes docs/katex/fonts/KaTeX_Math-BoldItalic.woff | Bin 22324 -> 0 bytes docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 | Bin 19720 -> 0 bytes docs/katex/fonts/KaTeX_Math-Italic.ttf | Bin 40992 -> 0 bytes docs/katex/fonts/KaTeX_Math-Italic.woff | Bin 22844 -> 0 bytes docs/katex/fonts/KaTeX_Math-Italic.woff2 | Bin 20096 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Bold.ttf | Bin 33688 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Bold.woff | Bin 18516 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 | Bin 15732 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Italic.ttf | Bin 30960 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Italic.woff | Bin 17572 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 | Bin 15024 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Regular.ttf | Bin 29812 -> 0 bytes docs/katex/fonts/KaTeX_SansSerif-Regular.woff | Bin 16228 -> 0 bytes .../katex/fonts/KaTeX_SansSerif-Regular.woff2 | Bin 13708 -> 0 bytes docs/katex/fonts/KaTeX_Script-Regular.ttf | Bin 24620 -> 0 bytes docs/katex/fonts/KaTeX_Script-Regular.woff | Bin 13428 -> 0 bytes docs/katex/fonts/KaTeX_Script-Regular.woff2 | Bin 12064 -> 0 bytes docs/katex/fonts/KaTeX_Size1-Regular.ttf | Bin 12916 -> 0 bytes docs/katex/fonts/KaTeX_Size1-Regular.woff | Bin 6696 -> 0 bytes docs/katex/fonts/KaTeX_Size1-Regular.woff2 | Bin 5592 -> 0 bytes docs/katex/fonts/KaTeX_Size2-Regular.ttf | Bin 12172 -> 0 bytes docs/katex/fonts/KaTeX_Size2-Regular.woff | Bin 6436 -> 0 bytes docs/katex/fonts/KaTeX_Size2-Regular.woff2 | Bin 5392 -> 0 bytes docs/katex/fonts/KaTeX_Size3-Regular.ttf | Bin 8120 -> 0 bytes docs/katex/fonts/KaTeX_Size3-Regular.woff | Bin 4568 -> 0 bytes docs/katex/fonts/KaTeX_Size3-Regular.woff2 | Bin 3728 -> 0 bytes docs/katex/fonts/KaTeX_Size4-Regular.ttf | Bin 11016 -> 0 bytes docs/katex/fonts/KaTeX_Size4-Regular.woff | Bin 6184 -> 0 bytes docs/katex/fonts/KaTeX_Size4-Regular.woff2 | Bin 5028 -> 0 bytes docs/katex/fonts/KaTeX_Typewriter-Regular.ttf | Bin 35924 -> 0 bytes .../katex/fonts/KaTeX_Typewriter-Regular.woff | Bin 20260 -> 0 bytes .../fonts/KaTeX_Typewriter-Regular.woff2 | Bin 17272 -> 0 bytes docs/katex/katex.min.css | 1 - docs/katex/katex.min.js | 1 - docs/ldsctrlest-logo.png | Bin 16128 -> 0 bytes docs/manifest.json | 15 - docs/mermaid.min.js | 32 - docs/sitemap.xml | 193 -- docs/svg/calendar.svg | 1 - docs/svg/edit.svg | 1 - docs/svg/menu.svg | 1 - docs/svg/toc.svg | 1 - docs/svg/translate.svg | 1 - docs/tags/index.html | 269 --- docs/tags/index.xml | 9 - docs/tags/page/1/index.html | 1 - misc/docs-hugo/.hugo_build.lock | 0 .../content/docs/api/Classes/_index.md | 256 +-- .../docs/api/Classes/classlds_1_1Fit.md | 561 ------ .../docs/api/Classes/classlds_1_1SSID.md | 370 ---- .../Classes/classlds_1_1SwitchedController.md | 360 ---- .../docs/api/Classes/classlds_1_1System.md | 850 --------- .../Classes/classlds_1_1UniformSystemList.md | 235 --- .../Classes/classlds_1_1UniformVectorList.md | 225 --- ...ntroller.md => classlds_1_1_controller.md} | 1548 ++++++++--------- ...{classlds_1_1EM.md => classlds_1_1_e_m.md} | 1186 ++++++------- .../docs/api/Classes/classlds_1_1_fit.md | 561 ++++++ .../docs/api/Classes/classlds_1_1_s_s_i_d.md | 370 ++++ .../classlds_1_1_switched_controller.md | 360 ++++ .../docs/api/Classes/classlds_1_1_system.md | 886 ++++++++++ ...md => classlds_1_1_uniform_matrix_list.md} | 505 +++--- .../classlds_1_1_uniform_system_list.md | 235 +++ .../classlds_1_1_uniform_vector_list.md | 225 +++ .../classlds_1_1gaussian_1_1Controller.md | 116 -- .../Classes/classlds_1_1gaussian_1_1Fit.md | 168 -- .../Classes/classlds_1_1gaussian_1_1FitEM.md | 91 - .../classlds_1_1gaussian_1_1FitSSID.md | 63 - ...sslds_1_1gaussian_1_1SwitchedController.md | 145 -- .../Classes/classlds_1_1gaussian_1_1System.md | 312 ---- .../classlds_1_1gaussian_1_1_controller.md | 116 ++ .../Classes/classlds_1_1gaussian_1_1_fit.md | 168 ++ .../classlds_1_1gaussian_1_1_fit_e_m.md | 96 + .../classlds_1_1gaussian_1_1_fit_s_s_i_d.md | 63 + ...lds_1_1gaussian_1_1_switched_controller.md | 145 ++ .../classlds_1_1gaussian_1_1_system.md | 328 ++++ .../classlds_1_1poisson_1_1Controller.md | 116 -- .../api/Classes/classlds_1_1poisson_1_1Fit.md | 168 -- .../Classes/classlds_1_1poisson_1_1FitEM.md | 91 - .../Classes/classlds_1_1poisson_1_1FitSSID.md | 63 - ...asslds_1_1poisson_1_1SwitchedController.md | 145 -- .../Classes/classlds_1_1poisson_1_1System.md | 224 --- .../classlds_1_1poisson_1_1_controller.md | 116 ++ .../Classes/classlds_1_1poisson_1_1_fit.md | 168 ++ .../classlds_1_1poisson_1_1_fit_e_m.md | 96 + .../classlds_1_1poisson_1_1_fit_s_s_i_d.md | 63 + ...slds_1_1poisson_1_1_switched_controller.md | 145 ++ .../Classes/classlds_1_1poisson_1_1_system.md | 240 +++ .../content/docs/api/Examples/_index.md | 92 +- .../api/Examples/eg_glds_ctrl_8cpp-example.md | 548 +++--- .../eg_glds_du_plds_ctrl_8cpp-example.md | 550 +++--- .../api/Examples/eg_plds_ctrl_8cpp-example.md | 488 +++--- .../api/Examples/eg_plds_est_8cpp-example.md | 388 +++-- .../eg_plds_switched_ctrl_8cpp-example.md | 478 ++--- .../content/docs/api/Files/_index.md | 368 ++-- .../dir_156a98879751e549d6939ca71a62d61f.md | 98 +- .../dir_68267d1309a1af8e8297ef4c3efbcdba.md | 56 +- .../dir_d28a4824dc47e487b107a5db32ef43c4.md | 56 +- .../dir_d44c64559bbebec7f509842c48db8b23.md | 48 +- .../docs/api/Files/eg__glds__ctrl_8cpp.md | 631 +++---- .../Files/eg__glds__du__plds__ctrl_8cpp.md | 633 +++---- .../docs/api/Files/eg__plds__ctrl_8cpp.md | 571 +++--- .../docs/api/Files/eg__plds__est_8cpp.md | 491 +++--- .../Files/eg__plds__switched__ctrl_8cpp.md | 555 +++--- .../content/docs/api/Files/lds_8cpp.md | 324 ++-- .../content/docs/api/Files/lds_8h.md | 368 ++-- .../content/docs/api/Files/lds__ctrl_8h.md | 1102 ++++++------ .../content/docs/api/Files/lds__fit_8h.md | 302 ++-- .../content/docs/api/Files/lds__fit__em_8h.md | 1268 +++++++------- .../docs/api/Files/lds__fit__ssid_8h.md | 1162 ++++++------- .../docs/api/Files/lds__gaussian_8h.md | 142 +- .../docs/api/Files/lds__gaussian__ctrl_8h.md | 232 +-- .../docs/api/Files/lds__gaussian__fit_8h.md | 188 +- .../api/Files/lds__gaussian__fit__em_8h.md | 184 +- .../api/Files/lds__gaussian__fit__ssid_8h.md | 182 +- .../docs/api/Files/lds__gaussian__sctrl_8h.md | 228 +-- .../docs/api/Files/lds__gaussian__sys_8cpp.md | 196 +-- .../docs/api/Files/lds__gaussian__sys_8h.md | 254 +-- .../content/docs/api/Files/lds__poisson_8h.md | 150 +- .../docs/api/Files/lds__poisson__ctrl_8h.md | 242 +-- .../docs/api/Files/lds__poisson__fit_8h.md | 204 +-- .../api/Files/lds__poisson__fit__em_8h.md | 196 +-- .../api/Files/lds__poisson__fit__ssid_8h.md | 180 +- .../docs/api/Files/lds__poisson__sctrl_8h.md | 234 +-- .../docs/api/Files/lds__poisson__sys_8cpp.md | 188 +- .../docs/api/Files/lds__poisson__sys_8h.md | 214 +-- .../content/docs/api/Files/lds__sctrl_8h.md | 550 +++--- .../content/docs/api/Files/lds__sys_8cpp.md | 324 ++-- .../content/docs/api/Files/lds__sys_8h.md | 375 ++-- .../docs/api/Files/lds__uniform__mats_8h.md | 673 +++---- .../api/Files/lds__uniform__systems_8h.md | 536 +++--- .../docs/api/Files/lds__uniform__vecs_8cpp.md | 214 +-- .../docs/api/Files/lds__uniform__vecs_8h.md | 384 ++-- .../content/docs/api/Files/mex__c__util_8h.md | 264 +-- .../docs/api/Files/mex__cpp__util_8h.md | 306 ++-- .../content/docs/api/Modules/_index.md | 56 +- .../docs/api/Modules/group__control__masks.md | 140 +- .../docs/api/Modules/group__defaults.md | 120 +- .../content/docs/api/Namespaces/_index.md | 80 +- .../docs/api/Namespaces/namespacearmamexc.md | 248 +-- .../api/Namespaces/namespacearmamexcpp.md | 416 ++--- .../docs/api/Namespaces/namespacelds.md | 689 ++++---- .../Namespaces/namespacelds_1_1gaussian.md | 60 +- .../api/Namespaces/namespacelds_1_1poisson.md | 112 +- .../docs/api/Namespaces/namespacestd.md | 18 + .../content/docs/api/Pages/_index.md | 32 +- .../docs/getting-started/getting-started.md | 6 +- .../docs/terminology/control-estimation.md | 5 + .../content/docs/terminology/model.md | 6 + misc/docs-hugo/content/issues-contributing.md | 9 + .../layouts/partials/docs/menu-filetree.html | 45 + ...s_b807c86e8030af4cdc30edccea379f5f.content | 0 ...scss_b807c86e8030af4cdc30edccea379f5f.json | 1 + scripts/convert-doxygen-to-md.sh | 3 + scripts/update-docs.sh | 6 +- 315 files changed, 16022 insertions(+), 60760 deletions(-) delete mode 100644 docs/404.html delete mode 100644 docs/acknowledgements/index.html delete mode 100644 docs/categories/index.html delete mode 100644 docs/categories/index.xml delete mode 100644 docs/categories/page/1/index.html delete mode 100644 docs/classlds_1_1_controller__inherit__graph.png delete mode 100644 docs/classlds_1_1_system__inherit__graph.png delete mode 100644 docs/docs/api/classes/classlds_1_1controller/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1em/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1fit/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1controller/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1fit/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1fitem/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1fitssid/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1system/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1controller/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1fit/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1fitem/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1fitssid/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1system/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1ssid/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1switchedcontroller/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1system/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1uniformmatrixlist/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1uniformsystemlist/index.html delete mode 100644 docs/docs/api/classes/classlds_1_1uniformvectorlist/index.html delete mode 100644 docs/docs/api/classes/index.html delete mode 100644 docs/docs/api/classes/index.xml delete mode 100644 docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html delete mode 100644 docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html delete mode 100644 docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html delete mode 100644 docs/docs/api/examples/eg_plds_est_8cpp-example/index.html delete mode 100644 docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html delete mode 100644 docs/docs/api/examples/index.html delete mode 100644 docs/docs/api/examples/index.xml delete mode 100644 docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html delete mode 100644 docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html delete mode 100644 docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html delete mode 100644 docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html delete mode 100644 docs/docs/api/files/eg__glds__ctrl_8cpp/index.html delete mode 100644 docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html delete mode 100644 docs/docs/api/files/eg__plds__ctrl_8cpp/index.html delete mode 100644 docs/docs/api/files/eg__plds__est_8cpp/index.html delete mode 100644 docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html delete mode 100644 docs/docs/api/files/index.html delete mode 100644 docs/docs/api/files/index.xml delete mode 100644 docs/docs/api/files/lds_8cpp/index.html delete mode 100644 docs/docs/api/files/lds_8h/index.html delete mode 100644 docs/docs/api/files/lds__ctrl_8h/index.html delete mode 100644 docs/docs/api/files/lds__fit_8h/index.html delete mode 100644 docs/docs/api/files/lds__fit__em_8h/index.html delete mode 100644 docs/docs/api/files/lds__fit__ssid_8h/index.html delete mode 100644 docs/docs/api/files/lds__gaussian_8h/index.html delete mode 100644 docs/docs/api/files/lds__gaussian__ctrl_8h/index.html delete mode 100644 docs/docs/api/files/lds__gaussian__fit_8h/index.html delete mode 100644 docs/docs/api/files/lds__gaussian__fit__em_8h/index.html delete mode 100644 docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html delete mode 100644 docs/docs/api/files/lds__gaussian__sctrl_8h/index.html delete mode 100644 docs/docs/api/files/lds__gaussian__sys_8cpp/index.html delete mode 100644 docs/docs/api/files/lds__gaussian__sys_8h/index.html delete mode 100644 docs/docs/api/files/lds__poisson_8h/index.html delete mode 100644 docs/docs/api/files/lds__poisson__ctrl_8h/index.html delete mode 100644 docs/docs/api/files/lds__poisson__fit_8h/index.html delete mode 100644 docs/docs/api/files/lds__poisson__fit__em_8h/index.html delete mode 100644 docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html delete mode 100644 docs/docs/api/files/lds__poisson__sctrl_8h/index.html delete mode 100644 docs/docs/api/files/lds__poisson__sys_8cpp/index.html delete mode 100644 docs/docs/api/files/lds__poisson__sys_8h/index.html delete mode 100644 docs/docs/api/files/lds__sctrl_8h/index.html delete mode 100644 docs/docs/api/files/lds__sys_8cpp/index.html delete mode 100644 docs/docs/api/files/lds__sys_8h/index.html delete mode 100644 docs/docs/api/files/lds__uniform__mats_8h/index.html delete mode 100644 docs/docs/api/files/lds__uniform__systems_8h/index.html delete mode 100644 docs/docs/api/files/lds__uniform__vecs_8cpp/index.html delete mode 100644 docs/docs/api/files/lds__uniform__vecs_8h/index.html delete mode 100644 docs/docs/api/files/mex__c__util_8h/index.html delete mode 100644 docs/docs/api/files/mex__cpp__util_8h/index.html delete mode 100644 docs/docs/api/modules/group__control__masks/index.html delete mode 100644 docs/docs/api/modules/group__defaults/index.html delete mode 100644 docs/docs/api/modules/index.html delete mode 100644 docs/docs/api/modules/index.xml delete mode 100644 docs/docs/api/namespaces/index.html delete mode 100644 docs/docs/api/namespaces/index.xml delete mode 100644 docs/docs/api/namespaces/namespacearmamexc/index.html delete mode 100644 docs/docs/api/namespaces/namespacearmamexcpp/index.html delete mode 100644 docs/docs/api/namespaces/namespacelds/index.html delete mode 100644 docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html delete mode 100644 docs/docs/api/namespaces/namespacelds_1_1poisson/index.html delete mode 100644 docs/docs/api/pages/index.html delete mode 100644 docs/docs/api/pages/index.xml delete mode 100644 docs/docs/getting-started/getting-started/index.html delete mode 100644 docs/docs/getting-started/linux-macos/index.html delete mode 100644 docs/docs/getting-started/windows/index.html delete mode 100644 docs/docs/index.html delete mode 100644 docs/docs/index.xml delete mode 100644 docs/docs/terminology/control-estimation/index.html delete mode 100644 docs/docs/terminology/model/index.html delete mode 100644 docs/docs/tutorials/eg_glds_control/index.html delete mode 100644 docs/docs/tutorials/eg_plds_state_estimation/index.html delete mode 100644 docs/docs/tutorials/eg_switched_plds_control/index.html delete mode 100644 docs/docs/tutorials/index.html delete mode 100644 docs/docs/tutorials/index.xml delete mode 100644 docs/eg_glds_ctrl_output.png delete mode 100644 docs/eg_plds_ctrl_output.png delete mode 100644 docs/eg_plds_est_output.png delete mode 100644 docs/eg_plds_est_output_hist.png delete mode 100644 docs/eg_plds_switched_ctrl_output.png delete mode 100644 docs/en.search-data.min.1e85f10c5ecc4f42108eae9ec00bea53e75365846aca80f6f94cdec83d505cec.json delete mode 100644 docs/en.search-data.min.799bfc19f929cf1e231669cf06cae550679543dfef2bae61c8d816ffca12f5c7.json delete mode 100644 docs/en.search-data.min.9ed82488f1147302e58d3c3e65dd55018a6e72b6e0fd48f0583a54196c1ecb52.json delete mode 100644 docs/en.search-data.min.cdba5d21934f88c8babfbddd59af83b0207389c62d2c3a11b31bc18fbe5810bb.json delete mode 100644 docs/en.search-data.min.dd6d5ec1dc04e75b58c8053ebd9a205c919f3fc365f221e7bb9b64945879058e.json delete mode 100644 docs/en.search.min.162b7aaf5cd0976fbdb91fda31f53d5c11f852b9340eb3cbaa440fc0fe4ceb79.js delete mode 100644 docs/en.search.min.2e8815e7b16be886f7411b4891dd22ab5b97100c454eaf8b746c3f9cc7f190f1.js delete mode 100644 docs/en.search.min.557bfc2f9fdca6a8b972ddb85402ebca9cbcdf5bb90e37c4cb24206214b0414e.js delete mode 100644 docs/en.search.min.8d74b29fa36f931cf38b54736e5a38c14b24e837def6c5f2027bc5a181d2fae0.js delete mode 100644 docs/en.search.min.93b2d0e7fa872893809358a01a9e6e0c0ce7a384100570924726e75a52c21d85.js delete mode 100644 docs/favicon.png delete mode 100644 docs/favicon.svg delete mode 100644 docs/flexsearch.min.js delete mode 100644 docs/fonts/roboto-mono-v13-latin-regular.woff delete mode 100644 docs/fonts/roboto-mono-v13-latin-regular.woff2 delete mode 100644 docs/fonts/roboto-v27-latin-700.woff delete mode 100644 docs/fonts/roboto-v27-latin-700.woff2 delete mode 100644 docs/fonts/roboto-v27-latin-regular.woff delete mode 100644 docs/fonts/roboto-v27-latin-regular.woff2 delete mode 100644 docs/index.html delete mode 100644 docs/index.xml delete mode 100644 docs/issues-contributing/index.html delete mode 100644 docs/katex/auto-render.min.js delete mode 100644 docs/katex/fonts/KaTeX_AMS-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_AMS-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_AMS-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf delete mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Bold.woff delete mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Fraktur-Bold.ttf delete mode 100644 docs/katex/fonts/KaTeX_Fraktur-Bold.woff delete mode 100644 docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Fraktur-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Fraktur-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Main-Bold.ttf delete mode 100644 docs/katex/fonts/KaTeX_Main-Bold.woff delete mode 100644 docs/katex/fonts/KaTeX_Main-Bold.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Main-BoldItalic.ttf delete mode 100644 docs/katex/fonts/KaTeX_Main-BoldItalic.woff delete mode 100644 docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Main-Italic.ttf delete mode 100644 docs/katex/fonts/KaTeX_Main-Italic.woff delete mode 100644 docs/katex/fonts/KaTeX_Main-Italic.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Main-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Main-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Main-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Math-BoldItalic.ttf delete mode 100644 docs/katex/fonts/KaTeX_Math-BoldItalic.woff delete mode 100644 docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Math-Italic.ttf delete mode 100644 docs/katex/fonts/KaTeX_Math-Italic.woff delete mode 100644 docs/katex/fonts/KaTeX_Math-Italic.woff2 delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Bold.ttf delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Bold.woff delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Italic.ttf delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Italic.woff delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_SansSerif-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Script-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Script-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Script-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Size1-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Size1-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Size1-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Size2-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Size2-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Size2-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Size3-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Size3-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Size3-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Size4-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Size4-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Size4-Regular.woff2 delete mode 100644 docs/katex/fonts/KaTeX_Typewriter-Regular.ttf delete mode 100644 docs/katex/fonts/KaTeX_Typewriter-Regular.woff delete mode 100644 docs/katex/fonts/KaTeX_Typewriter-Regular.woff2 delete mode 100644 docs/katex/katex.min.css delete mode 100644 docs/katex/katex.min.js delete mode 100644 docs/ldsctrlest-logo.png delete mode 100644 docs/manifest.json delete mode 100644 docs/mermaid.min.js delete mode 100644 docs/sitemap.xml delete mode 100644 docs/svg/calendar.svg delete mode 100644 docs/svg/edit.svg delete mode 100644 docs/svg/menu.svg delete mode 100644 docs/svg/toc.svg delete mode 100644 docs/svg/translate.svg delete mode 100644 docs/tags/index.html delete mode 100644 docs/tags/index.xml delete mode 100644 docs/tags/page/1/index.html create mode 100644 misc/docs-hugo/.hugo_build.lock delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1Fit.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1SSID.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1SwitchedController.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1System.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformSystemList.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformVectorList.md rename misc/docs-hugo/content/docs/api/Classes/{classlds_1_1Controller.md => classlds_1_1_controller.md} (54%) rename misc/docs-hugo/content/docs/api/Classes/{classlds_1_1EM.md => classlds_1_1_e_m.md} (52%) create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md rename misc/docs-hugo/content/docs/api/Classes/{classlds_1_1UniformMatrixList.md => classlds_1_1_uniform_matrix_list.md} (50%) create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Controller.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Fit.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitEM.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitSSID.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1SwitchedController.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1System.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Controller.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Fit.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitEM.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitSSID.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1SwitchedController.md delete mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1System.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md create mode 100644 misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md create mode 100644 misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md create mode 100644 misc/docs-hugo/layouts/partials/docs/menu-filetree.html rename docs/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css => misc/docs-hugo/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.content (100%) create mode 100644 misc/docs-hugo/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json diff --git a/README.md b/README.md index 209bd44c..9fb532cf 100755 --- a/README.md +++ b/README.md @@ -37,6 +37,9 @@ If you encounter bugs when using this library or have specific feature requests # Contributing We welcome any community contributions to this project. Please fork the repository and if possible use `clang-format` and `clang-tidy` to conform to the coding format/style of this repository. +When editing any documentation/guides, please use the markdown docs in `misc/docs-hugo` instead of directly editing the HTML docs. +Updated docs can be built by running `cd scripts` and `./update-docs.sh`. + # Acknowledgements Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02. diff --git a/docs/404.html b/docs/404.html deleted file mode 100644 index c2a01508..00000000 --- a/docs/404.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - -404 Page not found | LDS C&E - - - - - - - - - - - - -
-
-

404

-

Page Not Found

-

- LDS C&E -

-
-
- - - - - - diff --git a/docs/acknowledgements/index.html b/docs/acknowledgements/index.html deleted file mode 100644 index c51a3d6a..00000000 --- a/docs/acknowledgements/index.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - - - - - - - -Acknowledgements | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - Acknowledgements - - -
- - - - - - -
- - - -

- Acknowledgements - # -

-

Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/categories/index.html b/docs/categories/index.html deleted file mode 100644 index 8d73ea89..00000000 --- a/docs/categories/index.html +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - - - - - - -Categories | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - Categories - - -
- - - - - - -
- - - - - - - - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/categories/index.xml b/docs/categories/index.xml deleted file mode 100644 index d388090f..00000000 --- a/docs/categories/index.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - Categories on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/categories/ - Recent content in Categories on LDS C&E - Hugo -- gohugo.io - - diff --git a/docs/categories/page/1/index.html b/docs/categories/page/1/index.html deleted file mode 100644 index 23540486..00000000 --- a/docs/categories/page/1/index.html +++ /dev/null @@ -1 +0,0 @@ -https://stanley-rozell.github.io/lds-ctrl-est/categories/ \ No newline at end of file diff --git a/docs/classlds_1_1_controller__inherit__graph.png b/docs/classlds_1_1_controller__inherit__graph.png deleted file mode 100644 index 427b48c1a675780036d82e997dbfd37ee2ab36d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28819 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 wJtA{F2bb8a)MS6#7ED`&%>KW}& - - - - - - - - - - - - -lds::Controller | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::Controller - - -
- - - - - - -
- - - -

- lds::Controller - # -

-

More…

-

Inherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController< System >

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Controller() =default
Constructs a new Controller.
Controller(const System & sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller.
Controller(System && sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller by moving the system object.
const Vector &Control(const Vector & z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal (single-step)
const Vector &ControlOutputReference(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal, given previously-set (single-step)
const System &sys() const
const Matrix &Kc() const
Get state feedback controller gain.
const Matrix &Kc_inty() const
Get integral controller gain.
const Matrix &Kc_u() const
Get input feedback controller gain.
const Vector &g_design() const
Get input gain used in controller design.
const Vector &u_ref() const
Get reference input.
const Vector &x_ref() const
Get reference state.
const Vector &y_ref() const
Get reference output.
size_tcontrol_type() const
Get controller type.
data_ttau_awu() const
Get time constant of anti-integral-windup.
data_tu_lb() const
Get control lower bound.
data_tu_ub() const
Get control upper bound.
voidset_sys(const System & sys)
Set system.
voidset_g_design(const Vector & g_design)
Set input gain used in controller design (g_design)
voidset_u_ref(const Vector & u_ref)
Set reference input (u_ref)
voidset_x_ref(const Vector & x_ref)
Set reference state (x_ref)
virtual voidset_y_ref(const Vector & y_ref)
Set reference output (y_ref)
voidset_Kc(const Matrix & Kc)
Set state controller gain.
voidset_Kc_inty(const Matrix & Kc_inty)
Set integral controller gain.
voidset_Kc_u(const Matrix & Kc_u)
Set input controller gain.
voidset_tau_awu(data_t tau)
Set time constant of anti-integral-windup.
voidset_control_type(size_t control_type)
Sets the control type.
voidset_u_lb(data_t u_lb)
sets control lower bound
voidset_u_ub(data_t u_ub)
Sets control upper bound.
voidReset()
reset system and control variables.
voidPrint()
prints variables to stdout
-

- Protected Attributes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Systemsys_
underlying LDS
Vectoru_
control signal
Vectoru_return_
control signal that is returned to user
Vectorg_design_
input gain of the system used for controller design
Vectoru_ref_
reference input
Vectoru_ref_prev_
reference input at previous time step
Vectorx_ref_
reference state
Vectory_ref_
reference output
Vectorcx_ref_
MatrixKc_
state controller gain
MatrixKc_u_
input controller gain (optional when control updates )
MatrixKc_inty_
integral controller gain
Vectordu_ref_
Vectordv_ref_
Vectorv_ref_
Vectordv_
Vectorv_
Control after g inversion (e.g., control in physical units)
Vectorint_e_
integrated error
Vectorint_e_awu_adjust_
anti-windup adjustment to intE
Vectoru_sat_
control signal after saturation (for antiWindup)
booldo_control_prev_
booldo_lock_control_prev_
boolu_saturated_
whether control signal has reached saturation limits
data_tu_lb_
lower bound on control
data_tu_ub_
upper bound on control
data_ttau_awu_
antiwindup time constant
data_tk_awu_
data_tt_since_control_onset_
time since control epoch onset
size_tcontrol_type_
controller type
-

- Detailed Description - # -

-
template <typename System >
-class lds::Controller;
-

-
-

- Public Function Details - # -

-

- Controller - # -

-
Controller() =default
-

-

- Controller - # -

-
inline Controller(
-    const System & sys,
-    data_t u_lb,
-    data_t u_ub,
-    size_t control_type =0
-)
-

Parameters:

-
    -
  • sys System (derived from lds::System)
  • -
  • u_lb lower bound on control (u)
  • -
  • u_ub upper bound on control (u)
  • -
  • control_type [optional] control type bit mask
  • -
-

Template Parameters:

- -
-

- Controller - # -

-
inline Controller(
-    System && sys,
-    data_t u_lb,
-    data_t u_ub,
-    size_t control_type =0
-)
-

Parameters:

-
    -
  • sys System (derived from lds::System)
  • -
  • u_lb lower bound on control (u)
  • -
  • u_ub upper bound on control (u)
  • -
  • control_type [optional] control type bit mask
  • -
-

Template Parameters:

- -
-

- Control - # -

-
inline const Vector & Control(
-    const Vector & z,
-    bool do_control =true,
-    bool do_lock_control =false,
-    data_t sigma_soft_start =0,
-    data_t sigma_u_noise =0,
-    bool do_reset_at_control_onset =true
-)
-

Parameters:

-
    -
  • z measurement
  • -
  • do_control [optional] whether to update control (true) or simply feed through u_ref (false)
  • -
  • do_lock_control [optional] whether to lock control at its current value
  • -
  • sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true)
  • -
  • sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal
  • -
  • do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true)
  • -
-

Return: updated control signal

-

Updates the control signal (single-step). This is the most flexible option, but requires user to have set the controller’s y_ref, x_ref, and u_ref variables.

-
-

- ControlOutputReference - # -

-
inline const Vector & ControlOutputReference(
-    const Vector & z,
-    bool do_control =true,
-    bool do_estimation =true,
-    bool do_lock_control =false,
-    data_t sigma_soft_start =0,
-    data_t sigma_u_noise =0,
-    bool do_reset_at_control_onset =true
-)
-

Parameters:

-
    -
  • z measurement
  • -
  • do_control [optional] whether to update control (true) or simply feed through u_ref (false)
  • -
  • do_estimation [optional] whether to update state estimate (if false, effectively open-loop control)
  • -
  • do_lock_control [optional] whether to lock control at its current value
  • -
  • sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true)
  • -
  • sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal
  • -
  • do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true)
  • -
-

Return: updated control signal

-

Updates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.

-
-

- sys - # -

-
inline const System & sys() const
-

-

- Kc - # -

-
inline const Matrix & Kc() const
-

-

- Kc_inty - # -

-
inline const Matrix & Kc_inty() const
-

-

- Kc_u - # -

-
inline const Matrix & Kc_u() const
-

-

- g_design - # -

-
inline const Vector & g_design() const
-

-

- u_ref - # -

-
inline const Vector & u_ref() const
-

-

- x_ref - # -

-
inline const Vector & x_ref() const
-

-

- y_ref - # -

-
inline const Vector & y_ref() const
-

-

- control_type - # -

-
inline size_t control_type() const
-

-

- tau_awu - # -

-
inline data_t tau_awu() const
-

-

- u_lb - # -

-
inline data_t u_lb() const
-

-

- u_ub - # -

-
inline data_t u_ub() const
-

-

- set_sys - # -

-
inline void set_sys(
-    const System & sys
-)
-

-

- set_g_design - # -

-
inline void set_g_design(
-    const Vector & g_design
-)
-

-

- set_u_ref - # -

-
inline void set_u_ref(
-    const Vector & u_ref
-)
-

-

- set_x_ref - # -

-
inline void set_x_ref(
-    const Vector & x_ref
-)
-

-

- set_y_ref - # -

-
inline virtual void set_y_ref(
-    const Vector & y_ref
-)
-

Reimplemented by: lds::poisson::SwitchedController::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::gaussian::Controller::set_y_ref

-
-

- set_Kc - # -

-
inline void set_Kc(
-    const Matrix & Kc
-)
-

-

- set_Kc_inty - # -

-
inline void set_Kc_inty(
-    const Matrix & Kc_inty
-)
-

-

- set_Kc_u - # -

-
inline void set_Kc_u(
-    const Matrix & Kc_u
-)
-

-

- set_tau_awu - # -

-
inline void set_tau_awu(
-    data_t tau
-)
-

-

- set_control_type - # -

-
inline void set_control_type(
-    size_t control_type
-)
-

Parameters:

-
    -
  • control_type control type bit mask
  • -
-

Template Parameters:

- -
-

- set_u_lb - # -

-
inline void set_u_lb(
-    data_t u_lb
-)
-

Parameters:

-
    -
  • u_lb control lower bound
  • -
-
-

- set_u_ub - # -

-
inline void set_u_ub(
-    data_t u_ub
-)
-

Parameters:

-
    -
  • u_ub control upper bound
  • -
-
-

- Reset - # -

-
inline void Reset()
-

-

- Print - # -

-
inline void Print()
-

-

- Protected Attribute Details - # -

-

- **sys_** - # -

-
System sys_;
-

-

- **u_** - # -

-
Vector u_;
-

-

- **u_return_** - # -

-
Vector u_return_;
-

-

- **g_design_** - # -

-
Vector g_design_;
-

-

- **u_ref_** - # -

-
Vector u_ref_;
-

-

- **u_ref_prev_** - # -

-
Vector u_ref_prev_;
-

-

- **x_ref_** - # -

-
Vector x_ref_;
-

-

- **y_ref_** - # -

-
Vector y_ref_;
-

-

- **cx_ref_** - # -

-
Vector cx_ref_;
-

-

- **Kc_** - # -

-
Matrix Kc_;
-

-

- **Kc_u_** - # -

-
Matrix Kc_u_;
-

-

- **Kc_inty_** - # -

-
Matrix Kc_inty_;
-

-

- **du_ref_** - # -

-
Vector du_ref_;
-

-

- **dv_ref_** - # -

-
Vector dv_ref_;
-

-

- **v_ref_** - # -

-
Vector v_ref_;
-

-

- **dv_** - # -

-
Vector dv_;
-

-

- **v_** - # -

-
Vector v_;
-

-

- **int_e_** - # -

-
Vector int_e_;
-

-

- **int_e_awu_adjust_** - # -

-
Vector int_e_awu_adjust_;
-

-

- **u_sat_** - # -

-
Vector u_sat_;
-

-

- **do_control_prev_** - # -

-
bool do_control_prev_ = false;
-

-

- **do_lock_control_prev_** - # -

-
bool do_lock_control_prev_ = false;
-

-

- **u_saturated_** - # -

-
bool u_saturated_ =
-      false;
-

-

- **u_lb_** - # -

-
data_t u_lb_ {};
-

-

- **u_ub_** - # -

-
data_t u_ub_ {};
-

-

- **tau_awu_** - # -

-
data_t tau_awu_ {};
-

-

- **k_awu_** - # -

-
data_t k_awu_ = 0;
-

-

- **t_since_control_onset_** - # -

-
data_t t_since_control_onset_ = 0;
-

-

- **control_type_** - # -

-
size_t control_type_ {};
-

-
-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1em/index.html b/docs/docs/api/classes/classlds_1_1em/index.html deleted file mode 100644 index a4f466c9..00000000 --- a/docs/docs/api/classes/classlds_1_1em/index.html +++ /dev/null @@ -1,966 +0,0 @@ - - - - - - - - - - - - - -lds::EM | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::EM - - -
- - - - - - -
- - - -

- lds::EM - # -

-

More…

-

Inherited by lds::gaussian::FitEM, lds::poisson::FitEM

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
EM() =default
Constructs a new EMFit type.
EM(size_t n_x, data_t dt, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train)
Constructs a new EMFit type.
EM(const Fit & fit0, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train)
Constructs a new EMFit type.
virtual~EM() =default
const Fit &Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)
Runs fitting by Expectation(E)-Maximization(M)
std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > >ReturnData()
Returns the input/output data to caller.
const std::vector< Matrix > &x() const
gets estimated state (over time)
const std::vector< Matrix > &y() const
gets estimated output (over time)
const Matrix &sum_E_x_t_x_t() const
gets state-input covariance
const Matrix &sum_E_xu_tm1_xu_tm1() const
gets state-input covariance (t-minus-1)
const Matrix &sum_E_xu_t_xu_tm1() const
gets single lag state-input covariance
size_tn_t_tot()
total number of time samples
const Vector &theta() const
gets parameters updated in M step
-

- Protected Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
voidExpectation(bool force_common_initial =false)
Expectation step.
voidMaximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)
Maximization step.
voidMaximizeDynamics()
voidMaximizeQ()
voidMaximizeInitial()
virtual voidMaximizeOutput() =0
virtual voidMaximizeMeasurement() =0
voidSmooth(bool force_common_initial)
get smoothed estimates
virtual voidRecurseKe(Matrix & Ke, Cube & P_pre, Cube & P_post, size_t t) =0
recursively update estimator gain Ke
voidReset()
reset to initial conditions
voidInitVars()
Initializes the variables.
VectorUpdateTheta()
updates parameter list, theta
-

- Protected Attributes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformMatrixList< kMatFreeDim2 >u_
input training data
UniformMatrixList< kMatFreeDim2 >z_
measurement training data
std::vector< Matrix >x_
state estimate
std::vector< Cube >P_
state estimate cov
std::vector< Cube >P_t_tm1_
single-lag state covariance
std::vector< Matrix >y_
output estimate
Matrixdiag_y_
Matrixsum_E_x_t_x_t_
state covariance (current time)
Matrixsum_E_xu_tm1_xu_tm1_
state-input covariance (t-minus-1)
Matrixsum_E_xu_t_xu_tm1_
single lag state-input covariance
Fitfit_
Vectortheta_
data_tdt_
sample period
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
size_tn_trials_
number of input/output data sequences
std::vector< size_t >n_t_
number of time steps
size_tn_t_tot_
total number of time steps across trials
-

- Detailed Description - # -

-
template <typename Fit >
-class lds::EM;
-

-
-

- Public Function Details - # -

-

- EM - # -

-
EM() =default
-

-

- EM - # -

-
EM(
-    size_t n_x,
-    data_t dt,
-    UniformMatrixList< kMatFreeDim2 > && u_train,
-    UniformMatrixList< kMatFreeDim2 > && z_train
-)
-

Parameters:

-
    -
  • n_x number of states
  • -
  • dt sample period
  • -
  • u_train input training data
  • -
  • z_train measurement training data
  • -
-
-

- EM - # -

-
EM(
-    const Fit & fit0,
-    UniformMatrixList< kMatFreeDim2 > && u_train,
-    UniformMatrixList< kMatFreeDim2 > && z_train
-)
-

Parameters:

-
    -
  • fit0 initial fit
  • -
  • u_train input training data
  • -
  • z_train measurement training data
  • -
-
-

- ~EM - # -

-
virtual ~EM() =default
-

-

- Run - # -

-
const Fit & Run(
-    bool calc_dynamics =true,
-    bool calc_Q =true,
-    bool calc_init =true,
-    bool calc_output =true,
-    bool calc_measurement =true,
-    size_t max_iter =100,
-    data_t tol =1e-2
-)
-

Parameters:

-
    -
  • calc_dynamics [optional] whether to calculate dynamics (A, B)
  • -
  • calc_Q [optional] whether to calculate process noise covariance
  • -
  • calc_init [optional] whether to calculate initial conditions
  • -
  • calc_output [optional] whether to calculate output function
  • -
  • calc_measurement [optional] whether to calculate parameters for measurement/observation law
  • -
  • max_iter max number of iterations
  • -
  • tol convergence tolerance (max fractional abs change)
  • -
-

Return: Fit

-
-

- ReturnData - # -

-
inline std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > > ReturnData()
-

Return: tuple(input data, output data)

-
-

- x - # -

-
inline const std::vector< Matrix > & x() const
-

-

- y - # -

-
inline const std::vector< Matrix > & y() const
-

-

- sum_E_x_t_x_t - # -

-
inline const Matrix & sum_E_x_t_x_t() const
-

-

- sum_E_xu_tm1_xu_tm1 - # -

-
inline const Matrix & sum_E_xu_tm1_xu_tm1() const
-

-

- sum_E_xu_t_xu_tm1 - # -

-
inline const Matrix & sum_E_xu_t_xu_tm1() const
-

-

- n_t_tot - # -

-
inline size_t n_t_tot()
-

-

- theta - # -

-
inline const Vector & theta() const
-

-

- Protected Function Details - # -

-

- Expectation - # -

-
void Expectation(
-    bool force_common_initial =false
-)
-

Parameters:

-
    -
  • force_common_initial whether to force common initial condition for all trials
  • -
-
-

- Maximization - # -

-
void Maximization(
-    bool calc_dynamics =true,
-    bool calc_Q =true,
-    bool calc_init =false,
-    bool calc_output =false,
-    bool calc_measurement =false
-)
-

Parameters:

-
    -
  • calc_dynamics [optional] whether to caclulate dynamics (A, B)
  • -
  • calc_Q [optional] whether to calculate process noise covariance
  • -
  • calc_init [optional] whether to calculate initial conditions
  • -
  • calc_output [optional] whether to calculate output function
  • -
  • calc_measurement [optional] whether to calculate parameters for measurement/observation law
  • -
-
-

- MaximizeDynamics - # -

-
void MaximizeDynamics()
-

-

- MaximizeQ - # -

-
void MaximizeQ()
-

-

- MaximizeInitial - # -

-
void MaximizeInitial()
-

-

- MaximizeOutput - # -

-
virtual void MaximizeOutput() =0
-

Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput

-
-

- MaximizeMeasurement - # -

-
virtual void MaximizeMeasurement() =0
-

Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement

-
-

- Smooth - # -

-
void Smooth(
-    bool force_common_initial
-)
-

Parameters:

-
    -
  • force_common_initial whether to force common initial conditions
  • -
-
-

- RecurseKe - # -

-
virtual void RecurseKe(
-    Matrix & Ke,
-    Cube & P_pre,
-    Cube & P_post,
-    size_t t
-) =0
-

Parameters:

-
    -
  • Ke estimator gain
  • -
  • P_pre cov of predicted state est.
  • -
  • P_post cov of postior sate est.
  • -
  • t time
  • -
-

Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe

-
-

- Reset - # -

-
void Reset()
-

-

- InitVars - # -

-
void InitVars()
-

-

- UpdateTheta - # -

-
Vector UpdateTheta()
-

Return: parameter list

-
-

- Protected Attribute Details - # -

-

- **u_** - # -

-
UniformMatrixList< kMatFreeDim2 > u_;
-

-

- **z_** - # -

-
UniformMatrixList< kMatFreeDim2 > z_;
-

-

- **x_** - # -

-
std::vector< Matrix > x_;
-

-

- **P_** - # -

-
std::vector< Cube > P_;
-

-

- **P_t_tm1_** - # -

-
std::vector< Cube > P_t_tm1_;
-

-

- **y_** - # -

-
std::vector< Matrix > y_;
-

-

- **diag_y_** - # -

-
Matrix diag_y_;
-

-

- **sum_E_x_t_x_t_** - # -

-
Matrix sum_E_x_t_x_t_;
-

-

- **sum_E_xu_tm1_xu_tm1_** - # -

-
Matrix sum_E_xu_tm1_xu_tm1_;
-

-

- **sum_E_xu_t_xu_tm1_** - # -

-
Matrix sum_E_xu_t_xu_tm1_;
-

-

- **fit_** - # -

-
Fit fit_;
-

-

- **theta_** - # -

-
Vector theta_;
-

-

- **dt_** - # -

-
data_t dt_ {};
-

-

- **n_u_** - # -

-
size_t n_u_ {};
-

-

- **n_x_** - # -

-
size_t n_x_ {};
-

-

- **n_y_** - # -

-
size_t n_y_ {};
-

-

- **n_trials_** - # -

-
size_t n_trials_ {};
-

-

- **n_t_** - # -

-
std::vector< size_t > n_t_;
-

-

- **n_t_tot_** - # -

-
size_t n_t_tot_ {};
-

-
-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1fit/index.html b/docs/docs/api/classes/classlds_1_1fit/index.html deleted file mode 100644 index 03110fd4..00000000 --- a/docs/docs/api/classes/classlds_1_1fit/index.html +++ /dev/null @@ -1,907 +0,0 @@ - - - - - - - - - - - - - -lds::Fit | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::Fit - - -
- - - - - - -
- - - -

- lds::Fit - # -

-

LDS Fit Type. -
#include <lds_fit.h>

-

Inherited by lds::gaussian::Fit, lds::poisson::Fit

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Fit() =default
Constructs a new Fit.
Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)
Constructs a new Fit.
virtual~Fit() =default
size_tn_u() const
gets number of inputs
size_tn_x() const
gets number of states
size_tn_y() const
gets number of outputs
data_tdt() const
gets sample period
const Matrix &A() const
gets state matrix
const Matrix &B() const
gets input matrix
const Vector &g() const
gets input gain
const Vector &m() const
gets process disturbance
const Matrix &Q() const
gets process noise covariance
const Vector &x0() const
gets initial state estimate
const Matrix &P0() const
gets covariance of initial state estimate
const Matrix &C() const
gets output matrix
const Vector &d() const
gets output bias
virtual const Matrix &R() const =0
voidset_A(const Matrix & A)
sets state matrix
voidset_B(const Matrix & B)
sets input matrix
voidset_g(const Vector & g)
sets input gain/conversion factor
voidset_m(const Vector & m)
sets process disturbance
voidset_Q(const Matrix & Q)
sets process noise covariance
virtual voidset_R(const Matrix & R) =0
sets output noise covariance (if any)
voidset_x0(const Vector & x0)
sets initial state estimate
voidset_P0(const Matrix & P0)
sets initial state estimate covariance
voidset_C(const Matrix & C)
sets output matrix
voidset_d(const Vector & d)
sets output bias
Viewf(Matrix & x, const Matrix & u, size_t t)
system dynamics function
Viewf(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)
system dynamics function
virtual Viewh(Matrix & y, const Matrix & x, size_t t) =0
output function
-

- Protected Attributes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
data_tdt_
sample period
MatrixA_
state matrix
MatrixB_
input matrix
Vectorg_
input gain
Vectorm_
process noise mean
MatrixQ_
process noise cov
MatrixC_
output matrix
Vectord_
output bias
MatrixR_
measurement noise
Vectorx0_
initial state
MatrixP0_
initial covar
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
-
-
-

- Public Function Details - # -

-

- Fit - # -

-
Fit() =default
-

-

- Fit - # -

-
Fit(
-    size_t n_u,
-    size_t n_x,
-    size_t n_y,
-    data_t dt
-)
-

Parameters:

-
    -
  • n_u number of inputs
  • -
  • n_x number of states
  • -
  • n_y number of outputs
  • -
  • dt sample period
  • -
-
-

- ~Fit - # -

-
virtual ~Fit() =default
-

-

- n_u - # -

-
inline size_t n_u() const
-

-

- n_x - # -

-
inline size_t n_x() const
-

-

- n_y - # -

-
inline size_t n_y() const
-

-

- dt - # -

-
inline data_t dt() const
-

-

- A - # -

-
inline const Matrix & A() const
-

-

- B - # -

-
inline const Matrix & B() const
-

-

- g - # -

-
inline const Vector & g() const
-

-

- m - # -

-
inline const Vector & m() const
-

-

- Q - # -

-
inline const Matrix & Q() const
-

-

- x0 - # -

-
inline const Vector & x0() const
-

-

- P0 - # -

-
inline const Matrix & P0() const
-

-

- C - # -

-
inline const Matrix & C() const
-

-

- d - # -

-
inline const Vector & d() const
-

-

- R - # -

-
virtual const Matrix & R() const =0
-

Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R

-
-

- set_A - # -

-
inline void set_A(
-    const Matrix & A
-)
-

-

- set_B - # -

-
inline void set_B(
-    const Matrix & B
-)
-

-

- set_g - # -

-
inline void set_g(
-    const Vector & g
-)
-

-

- set_m - # -

-
inline void set_m(
-    const Vector & m
-)
-

-

- set_Q - # -

-
inline void set_Q(
-    const Matrix & Q
-)
-

-

- set_R - # -

-
virtual void set_R(
-    const Matrix & R
-) =0
-

Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R

-
-

- set_x0 - # -

-
inline void set_x0(
-    const Vector & x0
-)
-

-

- set_P0 - # -

-
inline void set_P0(
-    const Matrix & P0
-)
-

-

- set_C - # -

-
inline void set_C(
-    const Matrix & C
-)
-

-

- set_d - # -

-
inline void set_d(
-    const Vector & d
-)
-

-

- f - # -

-
inline View f(
-    Matrix & x,
-    const Matrix & u,
-    size_t t
-)
-

Parameters:

-
    -
  • x state estimate (over time)
  • -
  • u input (over time)
  • -
  • t time index
  • -
-

Return: view of updated state

-
-

- f - # -

-
inline View f(
-    Matrix & x_pre,
-    const Matrix & x_post,
-    const Matrix & u,
-    size_t t
-)
-

Parameters:

-
    -
  • x_pre predicted state est.
  • -
  • x_post posterior state est.
  • -
  • u input (over time)
  • -
  • t time index
  • -
-

Return: view of predicted state

-
-

- h - # -

-
virtual View h(
-    Matrix & y,
-    const Matrix & x,
-    size_t t
-) =0
-

Parameters:

-
    -
  • y output estimate (over time)
  • -
  • x state estimate (over time)
  • -
  • t time index
  • -
-

Return: output

-

Reimplemented by: lds::poisson::Fit::h, lds::gaussian::Fit::h

-
-

- Protected Attribute Details - # -

-

- **dt_** - # -

-
data_t dt_ {};
-

-

- **A_** - # -

-
Matrix A_;
-

-

- **B_** - # -

-
Matrix B_;
-

-

- **g_** - # -

-
Vector g_;
-

-

- **m_** - # -

-
Vector m_;
-

-

- **Q_** - # -

-
Matrix Q_;
-

-

- **C_** - # -

-
Matrix C_;
-

-

- **d_** - # -

-
Vector d_;
-

-

- **R_** - # -

-
Matrix R_;
-

-

- **x0_** - # -

-
Vector x0_;
-

-

- **P0_** - # -

-
Matrix P0_;
-

-

- **n_u_** - # -

-
size_t n_u_ {};
-

-

- **n_x_** - # -

-
size_t n_x_ {};
-

-

- **n_y_** - # -

-
size_t n_y_ {};
-

-
-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1controller/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1controller/index.html deleted file mode 100644 index 54825dac..00000000 --- a/docs/docs/api/classes/classlds_1_1gaussian_1_1controller/index.html +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - - - - - - - - -lds::gaussian::Controller | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::gaussian::Controller - - -
- - - - - - -
- - - -

- lds::gaussian::Controller - # -

-

Gaussian-observation Controller Type. -
#include <lds_gaussian_ctrl.h>

-

Inherits from lds::Controller< System >

-

- Public Functions - # -

- - - - - - - - - - - - - -
Name
virtual voidset_y_ref(const Vector & y_ref) override
sets reference output
-

- Additional inherited members - # -

-

Public Functions inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Controller() =default
Constructs a new Controller.
Controller(const System & sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller.
Controller(System && sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller by moving the system object.
const Vector &Control(const Vector & z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal (single-step)
const Vector &ControlOutputReference(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal, given previously-set (single-step)
const System &sys() const
const Matrix &Kc() const
Get state feedback controller gain.
const Matrix &Kc_inty() const
Get integral controller gain.
const Matrix &Kc_u() const
Get input feedback controller gain.
const Vector &g_design() const
Get input gain used in controller design.
const Vector &u_ref() const
Get reference input.
const Vector &x_ref() const
Get reference state.
const Vector &y_ref() const
Get reference output.
size_tcontrol_type() const
Get controller type.
data_ttau_awu() const
Get time constant of anti-integral-windup.
data_tu_lb() const
Get control lower bound.
data_tu_ub() const
Get control upper bound.
voidset_sys(const System & sys)
Set system.
voidset_g_design(const Vector & g_design)
Set input gain used in controller design (g_design)
voidset_u_ref(const Vector & u_ref)
Set reference input (u_ref)
voidset_x_ref(const Vector & x_ref)
Set reference state (x_ref)
voidset_Kc(const Matrix & Kc)
Set state controller gain.
voidset_Kc_inty(const Matrix & Kc_inty)
Set integral controller gain.
voidset_Kc_u(const Matrix & Kc_u)
Set input controller gain.
voidset_tau_awu(data_t tau)
Set time constant of anti-integral-windup.
voidset_control_type(size_t control_type)
Sets the control type.
voidset_u_lb(data_t u_lb)
sets control lower bound
voidset_u_ub(data_t u_ub)
Sets control upper bound.
voidReset()
reset system and control variables.
voidPrint()
prints variables to stdout
-

Protected Attributes inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Systemsys_
underlying LDS
Vectoru_
control signal
Vectoru_return_
control signal that is returned to user
Vectorg_design_
input gain of the system used for controller design
Vectoru_ref_
reference input
Vectoru_ref_prev_
reference input at previous time step
Vectorx_ref_
reference state
Vectory_ref_
reference output
Vectorcx_ref_
MatrixKc_
state controller gain
MatrixKc_u_
input controller gain (optional when control updates )
MatrixKc_inty_
integral controller gain
Vectordu_ref_
Vectordv_ref_
Vectorv_ref_
Vectordv_
Vectorv_
Control after g inversion (e.g., control in physical units)
Vectorint_e_
integrated error
Vectorint_e_awu_adjust_
anti-windup adjustment to intE
Vectoru_sat_
control signal after saturation (for antiWindup)
booldo_control_prev_
booldo_lock_control_prev_
boolu_saturated_
whether control signal has reached saturation limits
data_tu_lb_
lower bound on control
data_tu_ub_
upper bound on control
data_ttau_awu_
antiwindup time constant
data_tk_awu_
data_tt_since_control_onset_
time since control epoch onset
size_tcontrol_type_
controller type
-
-
-

- Public Function Details - # -

-

- set_y_ref - # -

-
inline virtual void set_y_ref(
-    const Vector & y_ref
-) override
-

Reimplements: lds::Controller::set_y_ref

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1fit/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1fit/index.html deleted file mode 100644 index 41b8a551..00000000 --- a/docs/docs/api/classes/classlds_1_1gaussian_1_1fit/index.html +++ /dev/null @@ -1,553 +0,0 @@ - - - - - - - - - - - - - -lds::gaussian::Fit | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::gaussian::Fit - - -
- - - - - - -
- - - -

- lds::gaussian::Fit - # -

-

GLDS Fit Type. -
#include <lds_gaussian_fit.h>

-

Inherits from lds::Fit

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Fit() =default
Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)
Constructs a new Fit.
virtual const Matrix &R() const override
gets measurement noise covariance
virtual voidset_R(const Matrix & R) override
sets measurement noise covariance
virtual Viewh(Matrix & y, const Matrix & x, size_t t) override
output function
-

- Additional inherited members - # -

-

Public Functions inherited from lds::Fit

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
virtual~Fit() =default
size_tn_u() const
gets number of inputs
size_tn_x() const
gets number of states
size_tn_y() const
gets number of outputs
data_tdt() const
gets sample period
const Matrix &A() const
gets state matrix
const Matrix &B() const
gets input matrix
const Vector &g() const
gets input gain
const Vector &m() const
gets process disturbance
const Matrix &Q() const
gets process noise covariance
const Vector &x0() const
gets initial state estimate
const Matrix &P0() const
gets covariance of initial state estimate
const Matrix &C() const
gets output matrix
const Vector &d() const
gets output bias
voidset_A(const Matrix & A)
sets state matrix
voidset_B(const Matrix & B)
sets input matrix
voidset_g(const Vector & g)
sets input gain/conversion factor
voidset_m(const Vector & m)
sets process disturbance
voidset_Q(const Matrix & Q)
sets process noise covariance
voidset_x0(const Vector & x0)
sets initial state estimate
voidset_P0(const Matrix & P0)
sets initial state estimate covariance
voidset_C(const Matrix & C)
sets output matrix
voidset_d(const Vector & d)
sets output bias
Viewf(Matrix & x, const Matrix & u, size_t t)
system dynamics function
Viewf(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)
system dynamics function
-

Protected Attributes inherited from lds::Fit

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
data_tdt_
sample period
MatrixA_
state matrix
MatrixB_
input matrix
Vectorg_
input gain
Vectorm_
process noise mean
MatrixQ_
process noise cov
MatrixC_
output matrix
Vectord_
output bias
MatrixR_
measurement noise
Vectorx0_
initial state
MatrixP0_
initial covar
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
-
-
-

- Public Function Details - # -

-

- Fit - # -

-
Fit() =default
-

-

- Fit - # -

-
Fit(
-    size_t n_u,
-    size_t n_x,
-    size_t n_y,
-    data_t dt
-)
-

Parameters:

-
    -
  • n_u number of inputs
  • -
  • n_x number of states
  • -
  • n_y number of outputs
  • -
  • dt sample period
  • -
-
-

- R - # -

-
inline virtual const Matrix & R() const override
-

Reimplements: lds::Fit::R

-
-

- set_R - # -

-
inline virtual void set_R(
-    const Matrix & R
-) override
-

Reimplements: lds::Fit::set_R

-
-

- h - # -

-
inline virtual View h(
-    Matrix & y,
-    const Matrix & x,
-    size_t t
-) override
-

Parameters:

-
    -
  • y output estimate (over time)
  • -
  • x state estimate (over time)
  • -
  • t time index
  • -
-

Return: output

-

Reimplements: lds::Fit::h

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1fitem/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1fitem/index.html deleted file mode 100644 index da318125..00000000 --- a/docs/docs/api/classes/classlds_1_1gaussian_1_1fitem/index.html +++ /dev/null @@ -1,464 +0,0 @@ - - - - - - - - - - - - - -lds::gaussian::FitEM | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::gaussian::FitEM - - -
- - - - - - -
- - - -

- lds::gaussian::FitEM - # -

-

GLDS E-M Fit Type. More…

-


#include <lds_gaussian_fit_em.h>

-

Inherits from lds::EM< Fit >

-

- Additional inherited members - # -

-

Public Functions inherited from lds::EM< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
EM() =default
Constructs a new EMFit type.
EM(size_t n_x, data_t dt, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train)
Constructs a new EMFit type.
EM(const Fit & fit0, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train)
Constructs a new EMFit type.
virtual~EM() =default
const Fit &Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)
Runs fitting by Expectation(E)-Maximization(M)
std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > >ReturnData()
Returns the input/output data to caller.
const std::vector< Matrix > &x() const
gets estimated state (over time)
const std::vector< Matrix > &y() const
gets estimated output (over time)
const Matrix &sum_E_x_t_x_t() const
gets state-input covariance
const Matrix &sum_E_xu_tm1_xu_tm1() const
gets state-input covariance (t-minus-1)
const Matrix &sum_E_xu_t_xu_tm1() const
gets single lag state-input covariance
size_tn_t_tot()
total number of time samples
const Vector &theta() const
gets parameters updated in M step
-

Protected Functions inherited from lds::EM< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
voidExpectation(bool force_common_initial =false)
Expectation step.
voidMaximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)
Maximization step.
voidMaximizeDynamics()
voidMaximizeQ()
voidMaximizeInitial()
voidSmooth(bool force_common_initial)
get smoothed estimates
voidReset()
reset to initial conditions
voidInitVars()
Initializes the variables.
VectorUpdateTheta()
updates parameter list, theta
-

Protected Attributes inherited from lds::EM< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformMatrixList< kMatFreeDim2 >u_
input training data
UniformMatrixList< kMatFreeDim2 >z_
measurement training data
std::vector< Matrix >x_
state estimate
std::vector< Cube >P_
state estimate cov
std::vector< Cube >P_t_tm1_
single-lag state covariance
std::vector< Matrix >y_
output estimate
Matrixdiag_y_
Matrixsum_E_x_t_x_t_
state covariance (current time)
Matrixsum_E_xu_tm1_xu_tm1_
state-input covariance (t-minus-1)
Matrixsum_E_xu_t_xu_tm1_
single lag state-input covariance
Fitfit_
Vectortheta_
data_tdt_
sample period
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
size_tn_trials_
number of input/output data sequences
std::vector< size_t >n_t_
number of time steps
size_tn_t_tot_
total number of time steps across trials
-

- Detailed Description - # -

-
class lds::gaussian::FitEM;
-

This type is used in the process of fitting GLDS models by expectation-maximization (EM).

-
-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1fitssid/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1fitssid/index.html deleted file mode 100644 index 571e3074..00000000 --- a/docs/docs/api/classes/classlds_1_1gaussian_1_1fitssid/index.html +++ /dev/null @@ -1,392 +0,0 @@ - - - - - - - - - - - - - -lds::gaussian::FitSSID | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::gaussian::FitSSID - - -
- - - - - - -
- - - -

- lds::gaussian::FitSSID - # -

-

Subspace Identification (SSID) for GLDS. -
#include <lds_gaussian_fit_ssid.h>

-

Inherits from lds::SSID< Fit >

-

- Additional inherited members - # -

-

Public Functions inherited from lds::SSID< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - -
Name
SSID() =default
Constructs a new SSIDFit type.
SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train, const Vector & d =Vector(1).fill(-kInf))
Constructs a new SSIDFit type.
std::tuple< Fit, Vector >Run(SSIDWt ssid_wt)
Runs fitting by subspace identification (SSID)
std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > >ReturnData()
Returns the I/O data to caller.
-

Protected Functions inherited from lds::SSID< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
voidCalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)
Using periods of silence in inputs (u), calculates the output \ bias (d)
voidCreateHankelDataMat()
Creates the block-hankel I/O data matrix.
voidCalcSVD(SSIDWt wt)
performs the singular value decomposition (SVD)
voidSolve(data_t wt_dc)
solves for LDS parameters
voidRecomputeExtObs()
recompute extended observability matrix from estimates of A, C
-

Protected Attributes inherited from lds::SSID< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformMatrixList< kMatFreeDim2 >u_
input training data
UniformMatrixList< kMatFreeDim2 >z_
measurement training data
MatrixD_
block-Hankel I/O data matrix
Fitfit_
fit
Matrixg_dc_
I/O gain @ DC.
data_tdt_
sample period
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
size_tn_h_
size_tn_trials_
number of input/output data sequences
std::vector< size_t >n_t_
number of time steps
size_tn_t_tot_
total number of time steps across trials
MatrixL_
lower triangle decomp of covariance matrix
Vectors_
singular values
Matrixext_obs_t_
extended observability matrix
-
-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/index.html deleted file mode 100644 index c698e8fa..00000000 --- a/docs/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/index.html +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - - - - - - - - -lds::gaussian::SwitchedController | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::gaussian::SwitchedController - - -
- - - - - - -
- - - -

- lds::gaussian::SwitchedController - # -

-

Gaussian-observation SwitchedController Type. -
#include <lds_gaussian_sctrl.h>

-

Inherits from lds::SwitchedController< System >, lds::Controller< System >

-

- Public Functions - # -

- - - - - - - - - - - - - -
Name
virtual voidset_y_ref(const Vector & y_ref) override
sets reference output
-

- Additional inherited members - # -

-

Public Functions inherited from lds::SwitchedController< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
SwitchedController() =default
Constructs a new SwitchedController.
SwitchedController(const std::vector< System > & systems, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new SwitchedController.
SwitchedController(std::vector< System > && systems, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new SwitchedController (moves systems).
voidSwitch(size_t idx, bool do_force_switch =false)
Switch to a different sub-system/controller.
voidset_Kc(const UniformMatrixList<> & Kc)
sets state feedback gains
voidset_Kc(UniformMatrixList<> && Kc)
sets state feedback gains (moving)
voidset_Kc_inty(const UniformMatrixList<> & Kc_inty)
sets integral feedback gains
voidset_Kc_inty(UniformMatrixList<> && Kc_inty)
sets integral feedback gains (moving)
voidset_Kc_u(const UniformMatrixList<> & Kc_u)
sets input feedback gains
voidset_Kc_u(UniformMatrixList<> && Kc_u)
sets input feedback gains (moving)
voidset_g_design(const UniformVectorList & g)
sets input gain used during controller design
voidset_g_design(UniformVectorList && g)
sets input gain used during controller design (moving)
-

Protected Attributes inherited from lds::SwitchedController< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
std::vector< System >systems_
underlying sub-systems which are switched between
size_tn_sys_
number of systems
size_tidx_
current system/controller index.
UniformMatrixListKc_list_
UniformMatrixListKc_inty_list_
UniformMatrixListKc_u_list_
UniformVectorListg_design_list_
-

Public Functions inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Controller() =default
Constructs a new Controller.
Controller(const System & sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller.
Controller(System && sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller by moving the system object.
const Vector &Control(const Vector & z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal (single-step)
const Vector &ControlOutputReference(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal, given previously-set (single-step)
const System &sys() const
const Matrix &Kc() const
Get state feedback controller gain.
const Matrix &Kc_inty() const
Get integral controller gain.
const Matrix &Kc_u() const
Get input feedback controller gain.
const Vector &g_design() const
Get input gain used in controller design.
const Vector &u_ref() const
Get reference input.
const Vector &x_ref() const
Get reference state.
const Vector &y_ref() const
Get reference output.
size_tcontrol_type() const
Get controller type.
data_ttau_awu() const
Get time constant of anti-integral-windup.
data_tu_lb() const
Get control lower bound.
data_tu_ub() const
Get control upper bound.
voidset_sys(const System & sys)
Set system.
voidset_g_design(const Vector & g_design)
Set input gain used in controller design (g_design)
voidset_u_ref(const Vector & u_ref)
Set reference input (u_ref)
voidset_x_ref(const Vector & x_ref)
Set reference state (x_ref)
voidset_Kc(const Matrix & Kc)
Set state controller gain.
voidset_Kc_inty(const Matrix & Kc_inty)
Set integral controller gain.
voidset_Kc_u(const Matrix & Kc_u)
Set input controller gain.
voidset_tau_awu(data_t tau)
Set time constant of anti-integral-windup.
voidset_control_type(size_t control_type)
Sets the control type.
voidset_u_lb(data_t u_lb)
sets control lower bound
voidset_u_ub(data_t u_ub)
Sets control upper bound.
voidReset()
reset system and control variables.
voidPrint()
prints variables to stdout
-

Protected Attributes inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Systemsys_
underlying LDS
Vectoru_
control signal
Vectoru_return_
control signal that is returned to user
Vectorg_design_
input gain of the system used for controller design
Vectoru_ref_
reference input
Vectoru_ref_prev_
reference input at previous time step
Vectorx_ref_
reference state
Vectory_ref_
reference output
Vectorcx_ref_
MatrixKc_
state controller gain
MatrixKc_u_
input controller gain (optional when control updates )
MatrixKc_inty_
integral controller gain
Vectordu_ref_
Vectordv_ref_
Vectorv_ref_
Vectordv_
Vectorv_
Control after g inversion (e.g., control in physical units)
Vectorint_e_
integrated error
Vectorint_e_awu_adjust_
anti-windup adjustment to intE
Vectoru_sat_
control signal after saturation (for antiWindup)
booldo_control_prev_
booldo_lock_control_prev_
boolu_saturated_
whether control signal has reached saturation limits
data_tu_lb_
lower bound on control
data_tu_ub_
upper bound on control
data_ttau_awu_
antiwindup time constant
data_tk_awu_
data_tt_since_control_onset_
time since control epoch onset
size_tcontrol_type_
controller type
-
-
-

- Public Function Details - # -

-

- set_y_ref - # -

-
inline virtual void set_y_ref(
-    const Vector & y_ref
-) override
-

Reimplements: lds::Controller::set_y_ref

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1system/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1system/index.html deleted file mode 100644 index b97e5202..00000000 --- a/docs/docs/api/classes/classlds_1_1gaussian_1_1system/index.html +++ /dev/null @@ -1,836 +0,0 @@ - - - - - - - - - - - - - -lds::gaussian::System | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::gaussian::System - - -
- - - - - - -
- - - -

- lds::gaussian::System - # -

-

Gaussian LDS Type. -
#include <lds_gaussian_sys.h>

-

Inherits from lds::System

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
System() =default
Constructs a new System.
System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)
Constructs a new Gaussian System.
virtual const Vector &Simulate(const Vector & u_tm1) override
Simulate system measurement.
const Matrix &R() const
Get output noise covariance.
voidset_Q(const Matrix & Q)
voidset_R(const Matrix & R)
Set output noise covariance.
voidset_Ke(const Matrix & Ke)
Set estimator gain.
voidset_Ke_m(const Matrix & Ke_m)
Set disturbance estimator gain.
voidPrint()
Print system variables to stdout.
-

- Protected Functions - # -

- - - - - - - - - - - - - - - - - -
Name
virtual voidh() override
System output function.
virtual voidRecurseKe() override
Recursively update estimator gain.
-

- Protected Attributes - # -

- - - - - - - - - - - - - - - - - -
Name
MatrixR_
covariance of output noise
booldo_recurse_Ke_
whether to recursively calculate estimator gain
-

- Additional inherited members - # -

-

Public Functions inherited from lds::System

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
virtual~System()
voidFilter(const Vector & u_tm1, const Vector & z)
Filter data to produce causal state estimates.
voidf(const Vector & u, bool do_add_noise =false)
system dynamics function
size_tn_u() const
Get number of inputs.
size_tn_x() const
Get number of states.
size_tn_y() const
Get number of outputs.
data_tdt() const
Get sample period.
const Vector &x() const
Get current state.
const Matrix &P() const
Get covariance of state estimate.
const Vector &m() const
Get current process disturbance/bias.
const Matrix &P_m() const
Get covariance of process disturbance estimate.
const Vector &cx() const
Get C*x.
const Vector &y() const
Get output.
const Vector &x0() const
Get initial state.
const Vector &m0() const
Get initial disturbance.
const Matrix &A() const
Get state matrix.
const Matrix &B() const
Get input matrix.
const Vector &g() const
Get input gain/conversion factor.
const Matrix &C() const
Get output matrix.
const Vector &d() const
Get output bias.
const Matrix &Ke() const
Get estimator gain.
const Matrix &Ke_m() const
Get estimator gain for process disturbance (m)
const Matrix &Q()
Get process noise covariance.
const Matrix &Q_m()
Get process noise covariance of disturbance evoluation.
const Matrix &P0()
Get covariance of initial state.
const Matrix &P0_m()
Get covariance of initial process disturbance.
voidset_A(const Matrix & A)
Set state matrix.
voidset_B(const Matrix & B)
Set input matrix.
voidset_m(const Vector & m, bool do_force_assign =false)
Set process disturbance.
voidset_g(const Vector & g)
Set input gain.
voidset_Q_m(const Matrix & Q_m)
Set process noise covariance of disturbance evoluation.
voidset_x0(const Vector & x0)
Set initial state.
voidset_P0(const Matrix & P0)
Set covariance of initial state.
voidset_P0_m(const Matrix & P0_m)
Set covariance of initial process disturbance.
voidset_C(const Matrix & C)
Set output matrix.
voidset_d(const Vector & d)
Set output bias.
voidset_x(const Vector & x)
Set state of system.
voidReset()
Reset system variables.
-

Protected Functions inherited from lds::System

- - - - - - - - - - - - - -
Name
voidInitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)
-

Public Attributes inherited from lds::System

- - - - - - - - - - - - - -
Name
booldo_adapt_m
whether to adaptively estimate disturbance m
-

Protected Attributes inherited from lds::System

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
std::size_tn_x_
number of states
std::size_tn_u_
number of inputs
std::size_tn_y_
number of outputs
data_tdt_
sample period
Vectorx_
state
MatrixP_
covariance of state estimate
Vectorm_
process disturbance
MatrixP_m_
covariance of disturbance estimate
Vectorcx_
C*x.
Vectory_
output
Vectorz_
measurement
Vectorx0_
initial state
MatrixP0_
covariance of initial state estimate
Vectorm0_
initial process disturbance
MatrixP0_m_
covariance of initial disturbance est.
MatrixA_
state matrix
MatrixB_
input matrix
Vectorg_
input gain
MatrixQ_
covariance of process noise
MatrixQ_m_
covariance of disturbance random walk
MatrixC_
output matrix
Vectord_
output bias
MatrixKe_
estimator gain
MatrixKe_m_
estimator gain for process disturbance
-
-
-

- Public Function Details - # -

-

- System - # -

-
System() =default
-

-

- System - # -

-
System(
-    std::size_t n_u,
-    std::size_t n_x,
-    std::size_t n_y,
-    data_t dt,
-    data_t p0 =kDefaultP0,
-    data_t q0 =kDefaultQ0,
-    data_t r0 =kDefaultR0
-)
-

Parameters:

-
    -
  • n_u number of inputs (u)
  • -
  • n_x number of states (x)
  • -
  • n_y number of outputs (y)
  • -
  • dt sample period
  • -
  • p0 [optional] initial diagonal elements of state estimate covariance (P)
  • -
  • q0 [optional] initial diagonal elements of process noise covariance (Q)
  • -
  • r0 [optional] initial diagonal elements of output noise covariance (R)
  • -
-
-

- Simulate - # -

-
virtual const Vector & Simulate(
-    const Vector & u_tm1
-) override
-

Parameters:

-
    -
  • u_tm1 input at t-1
  • -
-

Return: z measurement

-

Reimplements: lds::System::Simulate

-

Simulate system and produce measurement

-
-

- R - # -

-
inline const Matrix & R() const
-

-

- set_Q - # -

-
inline void set_Q(
-    const Matrix & Q
-)
-

-

- set_R - # -

-
inline void set_R(
-    const Matrix & R
-)
-

-

- set_Ke - # -

-
inline void set_Ke(
-    const Matrix & Ke
-)
-

-

- set_Ke_m - # -

-
inline void set_Ke_m(
-    const Matrix & Ke_m
-)
-

-

- Print - # -

-
void Print()
-

-

- Protected Function Details - # -

-

- h - # -

-
inline virtual void h() override
-

Reimplements: lds::System::h

-
-

- RecurseKe - # -

-
virtual void RecurseKe() override
-

Reimplements: lds::System::RecurseKe

-
-

- Protected Attribute Details - # -

-

- **R_** - # -

-
Matrix R_;
-

-

- **do_recurse_Ke_** - # -

-
bool do_recurse_Ke_ {};
-

-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1controller/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1controller/index.html deleted file mode 100644 index b38405b1..00000000 --- a/docs/docs/api/classes/classlds_1_1poisson_1_1controller/index.html +++ /dev/null @@ -1,560 +0,0 @@ - - - - - - - - - - - - - -lds::poisson::Controller | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::poisson::Controller - - -
- - - - - - -
- - - -

- lds::poisson::Controller - # -

-

PLDS Controller Type. -
#include <lds_poisson_ctrl.h>

-

Inherits from lds::Controller< System >

-

- Public Functions - # -

- - - - - - - - - - - - - -
Name
virtual voidset_y_ref(const Vector & y_ref) override
Set reference output.
-

- Additional inherited members - # -

-

Public Functions inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Controller() =default
Constructs a new Controller.
Controller(const System & sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller.
Controller(System && sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller by moving the system object.
const Vector &Control(const Vector & z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal (single-step)
const Vector &ControlOutputReference(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal, given previously-set (single-step)
const System &sys() const
const Matrix &Kc() const
Get state feedback controller gain.
const Matrix &Kc_inty() const
Get integral controller gain.
const Matrix &Kc_u() const
Get input feedback controller gain.
const Vector &g_design() const
Get input gain used in controller design.
const Vector &u_ref() const
Get reference input.
const Vector &x_ref() const
Get reference state.
const Vector &y_ref() const
Get reference output.
size_tcontrol_type() const
Get controller type.
data_ttau_awu() const
Get time constant of anti-integral-windup.
data_tu_lb() const
Get control lower bound.
data_tu_ub() const
Get control upper bound.
voidset_sys(const System & sys)
Set system.
voidset_g_design(const Vector & g_design)
Set input gain used in controller design (g_design)
voidset_u_ref(const Vector & u_ref)
Set reference input (u_ref)
voidset_x_ref(const Vector & x_ref)
Set reference state (x_ref)
voidset_Kc(const Matrix & Kc)
Set state controller gain.
voidset_Kc_inty(const Matrix & Kc_inty)
Set integral controller gain.
voidset_Kc_u(const Matrix & Kc_u)
Set input controller gain.
voidset_tau_awu(data_t tau)
Set time constant of anti-integral-windup.
voidset_control_type(size_t control_type)
Sets the control type.
voidset_u_lb(data_t u_lb)
sets control lower bound
voidset_u_ub(data_t u_ub)
Sets control upper bound.
voidReset()
reset system and control variables.
voidPrint()
prints variables to stdout
-

Protected Attributes inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Systemsys_
underlying LDS
Vectoru_
control signal
Vectoru_return_
control signal that is returned to user
Vectorg_design_
input gain of the system used for controller design
Vectoru_ref_
reference input
Vectoru_ref_prev_
reference input at previous time step
Vectorx_ref_
reference state
Vectory_ref_
reference output
Vectorcx_ref_
MatrixKc_
state controller gain
MatrixKc_u_
input controller gain (optional when control updates )
MatrixKc_inty_
integral controller gain
Vectordu_ref_
Vectordv_ref_
Vectorv_ref_
Vectordv_
Vectorv_
Control after g inversion (e.g., control in physical units)
Vectorint_e_
integrated error
Vectorint_e_awu_adjust_
anti-windup adjustment to intE
Vectoru_sat_
control signal after saturation (for antiWindup)
booldo_control_prev_
booldo_lock_control_prev_
boolu_saturated_
whether control signal has reached saturation limits
data_tu_lb_
lower bound on control
data_tu_ub_
upper bound on control
data_ttau_awu_
antiwindup time constant
data_tk_awu_
data_tt_since_control_onset_
time since control epoch onset
size_tcontrol_type_
controller type
-
-
-

- Public Function Details - # -

-

- set_y_ref - # -

-
inline virtual void set_y_ref(
-    const Vector & y_ref
-) override
-

Reimplements: lds::Controller::set_y_ref

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1fit/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1fit/index.html deleted file mode 100644 index ba9f0206..00000000 --- a/docs/docs/api/classes/classlds_1_1poisson_1_1fit/index.html +++ /dev/null @@ -1,553 +0,0 @@ - - - - - - - - - - - - - -lds::poisson::Fit | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::poisson::Fit - - -
- - - - - - -
- - - -

- lds::poisson::Fit - # -

-

PLDS Fit Type. -
#include <lds_poisson_fit.h>

-

Inherits from lds::Fit

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Fit() =default
Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)
Constructs a new Fit.
virtual Viewh(Matrix & y, const Matrix & x, size_t t) override
output function
virtual voidset_R(const Matrix & R) override
sets output noise covariance (if any)
virtual const Matrix &R() const override
-

- Additional inherited members - # -

-

Public Functions inherited from lds::Fit

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
virtual~Fit() =default
size_tn_u() const
gets number of inputs
size_tn_x() const
gets number of states
size_tn_y() const
gets number of outputs
data_tdt() const
gets sample period
const Matrix &A() const
gets state matrix
const Matrix &B() const
gets input matrix
const Vector &g() const
gets input gain
const Vector &m() const
gets process disturbance
const Matrix &Q() const
gets process noise covariance
const Vector &x0() const
gets initial state estimate
const Matrix &P0() const
gets covariance of initial state estimate
const Matrix &C() const
gets output matrix
const Vector &d() const
gets output bias
voidset_A(const Matrix & A)
sets state matrix
voidset_B(const Matrix & B)
sets input matrix
voidset_g(const Vector & g)
sets input gain/conversion factor
voidset_m(const Vector & m)
sets process disturbance
voidset_Q(const Matrix & Q)
sets process noise covariance
voidset_x0(const Vector & x0)
sets initial state estimate
voidset_P0(const Matrix & P0)
sets initial state estimate covariance
voidset_C(const Matrix & C)
sets output matrix
voidset_d(const Vector & d)
sets output bias
Viewf(Matrix & x, const Matrix & u, size_t t)
system dynamics function
Viewf(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)
system dynamics function
-

Protected Attributes inherited from lds::Fit

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
data_tdt_
sample period
MatrixA_
state matrix
MatrixB_
input matrix
Vectorg_
input gain
Vectorm_
process noise mean
MatrixQ_
process noise cov
MatrixC_
output matrix
Vectord_
output bias
MatrixR_
measurement noise
Vectorx0_
initial state
MatrixP0_
initial covar
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
-
-
-

- Public Function Details - # -

-

- Fit - # -

-
Fit() =default
-

-

- Fit - # -

-
inline Fit(
-    size_t n_u,
-    size_t n_x,
-    size_t n_y,
-    data_t dt
-)
-

Parameters:

-
    -
  • n_u number of inputs
  • -
  • n_x number of states
  • -
  • n_y number of outputs
  • -
  • dt sample period
  • -
-
-

- h - # -

-
inline virtual View h(
-    Matrix & y,
-    const Matrix & x,
-    size_t t
-) override
-

Parameters:

-
    -
  • y output estimate (over time)
  • -
  • x state estimate (over time)
  • -
  • t time index
  • -
-

Return: output

-

Reimplements: lds::Fit::h

-
-

- set_R - # -

-
inline virtual void set_R(
-    const Matrix & R
-) override
-

Reimplements: lds::Fit::set_R

-
-

- R - # -

-
inline virtual const Matrix & R() const override
-

Reimplements: lds::Fit::R

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1fitem/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1fitem/index.html deleted file mode 100644 index 26c96ca1..00000000 --- a/docs/docs/api/classes/classlds_1_1poisson_1_1fitem/index.html +++ /dev/null @@ -1,464 +0,0 @@ - - - - - - - - - - - - - -lds::poisson::FitEM | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::poisson::FitEM - - -
- - - - - - -
- - - -

- lds::poisson::FitEM - # -

-

PLDS E-M Fit Type. More…

-


#include <lds_poisson_fit_em.h>

-

Inherits from lds::EM< Fit >

-

- Additional inherited members - # -

-

Public Functions inherited from lds::EM< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
EM() =default
Constructs a new EMFit type.
EM(size_t n_x, data_t dt, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train)
Constructs a new EMFit type.
EM(const Fit & fit0, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train)
Constructs a new EMFit type.
virtual~EM() =default
const Fit &Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)
Runs fitting by Expectation(E)-Maximization(M)
std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > >ReturnData()
Returns the input/output data to caller.
const std::vector< Matrix > &x() const
gets estimated state (over time)
const std::vector< Matrix > &y() const
gets estimated output (over time)
const Matrix &sum_E_x_t_x_t() const
gets state-input covariance
const Matrix &sum_E_xu_tm1_xu_tm1() const
gets state-input covariance (t-minus-1)
const Matrix &sum_E_xu_t_xu_tm1() const
gets single lag state-input covariance
size_tn_t_tot()
total number of time samples
const Vector &theta() const
gets parameters updated in M step
-

Protected Functions inherited from lds::EM< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
voidExpectation(bool force_common_initial =false)
Expectation step.
voidMaximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)
Maximization step.
voidMaximizeDynamics()
voidMaximizeQ()
voidMaximizeInitial()
voidSmooth(bool force_common_initial)
get smoothed estimates
voidReset()
reset to initial conditions
voidInitVars()
Initializes the variables.
VectorUpdateTheta()
updates parameter list, theta
-

Protected Attributes inherited from lds::EM< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformMatrixList< kMatFreeDim2 >u_
input training data
UniformMatrixList< kMatFreeDim2 >z_
measurement training data
std::vector< Matrix >x_
state estimate
std::vector< Cube >P_
state estimate cov
std::vector< Cube >P_t_tm1_
single-lag state covariance
std::vector< Matrix >y_
output estimate
Matrixdiag_y_
Matrixsum_E_x_t_x_t_
state covariance (current time)
Matrixsum_E_xu_tm1_xu_tm1_
state-input covariance (t-minus-1)
Matrixsum_E_xu_t_xu_tm1_
single lag state-input covariance
Fitfit_
Vectortheta_
data_tdt_
sample period
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
size_tn_trials_
number of input/output data sequences
std::vector< size_t >n_t_
number of time steps
size_tn_t_tot_
total number of time steps across trials
-

- Detailed Description - # -

-
class lds::poisson::FitEM;
-

This type is used in the process of fitting PLDS models by expectation-maximization (EM).

-
-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1fitssid/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1fitssid/index.html deleted file mode 100644 index 89981c42..00000000 --- a/docs/docs/api/classes/classlds_1_1poisson_1_1fitssid/index.html +++ /dev/null @@ -1,392 +0,0 @@ - - - - - - - - - - - - - -lds::poisson::FitSSID | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::poisson::FitSSID - - -
- - - - - - -
- - - -

- lds::poisson::FitSSID - # -

-

Subspace Identification (SSID) for PLDS. -
#include <lds_poisson_fit_ssid.h>

-

Inherits from lds::SSID< Fit >

-

- Additional inherited members - # -

-

Public Functions inherited from lds::SSID< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - -
Name
SSID() =default
Constructs a new SSIDFit type.
SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train, const Vector & d =Vector(1).fill(-kInf))
Constructs a new SSIDFit type.
std::tuple< Fit, Vector >Run(SSIDWt ssid_wt)
Runs fitting by subspace identification (SSID)
std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > >ReturnData()
Returns the I/O data to caller.
-

Protected Functions inherited from lds::SSID< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
voidCalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)
Using periods of silence in inputs (u), calculates the output \ bias (d)
voidCreateHankelDataMat()
Creates the block-hankel I/O data matrix.
voidCalcSVD(SSIDWt wt)
performs the singular value decomposition (SVD)
voidSolve(data_t wt_dc)
solves for LDS parameters
voidRecomputeExtObs()
recompute extended observability matrix from estimates of A, C
-

Protected Attributes inherited from lds::SSID< Fit >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformMatrixList< kMatFreeDim2 >u_
input training data
UniformMatrixList< kMatFreeDim2 >z_
measurement training data
MatrixD_
block-Hankel I/O data matrix
Fitfit_
fit
Matrixg_dc_
I/O gain @ DC.
data_tdt_
sample period
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
size_tn_h_
size_tn_trials_
number of input/output data sequences
std::vector< size_t >n_t_
number of time steps
size_tn_t_tot_
total number of time steps across trials
MatrixL_
lower triangle decomp of covariance matrix
Vectors_
singular values
Matrixext_obs_t_
extended observability matrix
-
-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/index.html deleted file mode 100644 index 257fc93a..00000000 --- a/docs/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/index.html +++ /dev/null @@ -1,658 +0,0 @@ - - - - - - - - - - - - - -lds::poisson::SwitchedController | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::poisson::SwitchedController - - -
- - - - - - -
- - - -

- lds::poisson::SwitchedController - # -

-

Poisson-observation SwitchedController Type. -
#include <lds_poisson_sctrl.h>

-

Inherits from lds::SwitchedController< System >, lds::Controller< System >

-

- Public Functions - # -

- - - - - - - - - - - - - -
Name
virtual voidset_y_ref(const Vector & y_ref) override
Set reference output.
-

- Additional inherited members - # -

-

Public Functions inherited from lds::SwitchedController< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
SwitchedController() =default
Constructs a new SwitchedController.
SwitchedController(const std::vector< System > & systems, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new SwitchedController.
SwitchedController(std::vector< System > && systems, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new SwitchedController (moves systems).
voidSwitch(size_t idx, bool do_force_switch =false)
Switch to a different sub-system/controller.
voidset_Kc(const UniformMatrixList<> & Kc)
sets state feedback gains
voidset_Kc(UniformMatrixList<> && Kc)
sets state feedback gains (moving)
voidset_Kc_inty(const UniformMatrixList<> & Kc_inty)
sets integral feedback gains
voidset_Kc_inty(UniformMatrixList<> && Kc_inty)
sets integral feedback gains (moving)
voidset_Kc_u(const UniformMatrixList<> & Kc_u)
sets input feedback gains
voidset_Kc_u(UniformMatrixList<> && Kc_u)
sets input feedback gains (moving)
voidset_g_design(const UniformVectorList & g)
sets input gain used during controller design
voidset_g_design(UniformVectorList && g)
sets input gain used during controller design (moving)
-

Protected Attributes inherited from lds::SwitchedController< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
std::vector< System >systems_
underlying sub-systems which are switched between
size_tn_sys_
number of systems
size_tidx_
current system/controller index.
UniformMatrixListKc_list_
UniformMatrixListKc_inty_list_
UniformMatrixListKc_u_list_
UniformVectorListg_design_list_
-

Public Functions inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Controller() =default
Constructs a new Controller.
Controller(const System & sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller.
Controller(System && sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller by moving the system object.
const Vector &Control(const Vector & z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal (single-step)
const Vector &ControlOutputReference(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal, given previously-set (single-step)
const System &sys() const
const Matrix &Kc() const
Get state feedback controller gain.
const Matrix &Kc_inty() const
Get integral controller gain.
const Matrix &Kc_u() const
Get input feedback controller gain.
const Vector &g_design() const
Get input gain used in controller design.
const Vector &u_ref() const
Get reference input.
const Vector &x_ref() const
Get reference state.
const Vector &y_ref() const
Get reference output.
size_tcontrol_type() const
Get controller type.
data_ttau_awu() const
Get time constant of anti-integral-windup.
data_tu_lb() const
Get control lower bound.
data_tu_ub() const
Get control upper bound.
voidset_sys(const System & sys)
Set system.
voidset_g_design(const Vector & g_design)
Set input gain used in controller design (g_design)
voidset_u_ref(const Vector & u_ref)
Set reference input (u_ref)
voidset_x_ref(const Vector & x_ref)
Set reference state (x_ref)
voidset_Kc(const Matrix & Kc)
Set state controller gain.
voidset_Kc_inty(const Matrix & Kc_inty)
Set integral controller gain.
voidset_Kc_u(const Matrix & Kc_u)
Set input controller gain.
voidset_tau_awu(data_t tau)
Set time constant of anti-integral-windup.
voidset_control_type(size_t control_type)
Sets the control type.
voidset_u_lb(data_t u_lb)
sets control lower bound
voidset_u_ub(data_t u_ub)
Sets control upper bound.
voidReset()
reset system and control variables.
voidPrint()
prints variables to stdout
-

Protected Attributes inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Systemsys_
underlying LDS
Vectoru_
control signal
Vectoru_return_
control signal that is returned to user
Vectorg_design_
input gain of the system used for controller design
Vectoru_ref_
reference input
Vectoru_ref_prev_
reference input at previous time step
Vectorx_ref_
reference state
Vectory_ref_
reference output
Vectorcx_ref_
MatrixKc_
state controller gain
MatrixKc_u_
input controller gain (optional when control updates )
MatrixKc_inty_
integral controller gain
Vectordu_ref_
Vectordv_ref_
Vectorv_ref_
Vectordv_
Vectorv_
Control after g inversion (e.g., control in physical units)
Vectorint_e_
integrated error
Vectorint_e_awu_adjust_
anti-windup adjustment to intE
Vectoru_sat_
control signal after saturation (for antiWindup)
booldo_control_prev_
booldo_lock_control_prev_
boolu_saturated_
whether control signal has reached saturation limits
data_tu_lb_
lower bound on control
data_tu_ub_
upper bound on control
data_ttau_awu_
antiwindup time constant
data_tk_awu_
data_tt_since_control_onset_
time since control epoch onset
size_tcontrol_type_
controller type
-
-
-

- Public Function Details - # -

-

- set_y_ref - # -

-
inline virtual void set_y_ref(
-    const Vector & y_ref
-) override
-

Reimplements: lds::Controller::set_y_ref

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1system/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1system/index.html deleted file mode 100644 index acec6b54..00000000 --- a/docs/docs/api/classes/classlds_1_1poisson_1_1system/index.html +++ /dev/null @@ -1,714 +0,0 @@ - - - - - - - - - - - - - -lds::poisson::System | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::poisson::System - - -
- - - - - - -
- - - -

- lds::poisson::System - # -

-

Poisson System type. -
#include <lds_poisson_sys.h>

-

Inherits from lds::System

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - -
Name
System() =default
Constructs a new System.
System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)
Constructs a new Poisson System.
virtual const Vector &Simulate(const Vector & u_tm1) override
Simulate system measurement.
-

- Protected Functions - # -

- - - - - - - - - - - - - - - - - -
Name
virtual voidh() override
System output function.
virtual voidRecurseKe() override
Recursively recalculate estimator gain (Ke)
-

- Additional inherited members - # -

-

Public Functions inherited from lds::System

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
virtual~System()
voidFilter(const Vector & u_tm1, const Vector & z)
Filter data to produce causal state estimates.
voidf(const Vector & u, bool do_add_noise =false)
system dynamics function
size_tn_u() const
Get number of inputs.
size_tn_x() const
Get number of states.
size_tn_y() const
Get number of outputs.
data_tdt() const
Get sample period.
const Vector &x() const
Get current state.
const Matrix &P() const
Get covariance of state estimate.
const Vector &m() const
Get current process disturbance/bias.
const Matrix &P_m() const
Get covariance of process disturbance estimate.
const Vector &cx() const
Get C*x.
const Vector &y() const
Get output.
const Vector &x0() const
Get initial state.
const Vector &m0() const
Get initial disturbance.
const Matrix &A() const
Get state matrix.
const Matrix &B() const
Get input matrix.
const Vector &g() const
Get input gain/conversion factor.
const Matrix &C() const
Get output matrix.
const Vector &d() const
Get output bias.
const Matrix &Ke() const
Get estimator gain.
const Matrix &Ke_m() const
Get estimator gain for process disturbance (m)
const Matrix &Q()
Get process noise covariance.
const Matrix &Q_m()
Get process noise covariance of disturbance evoluation.
const Matrix &P0()
Get covariance of initial state.
const Matrix &P0_m()
Get covariance of initial process disturbance.
voidset_A(const Matrix & A)
Set state matrix.
voidset_B(const Matrix & B)
Set input matrix.
voidset_m(const Vector & m, bool do_force_assign =false)
Set process disturbance.
voidset_g(const Vector & g)
Set input gain.
voidset_Q(const Matrix & Q)
Set process noise covariance.
voidset_Q_m(const Matrix & Q_m)
Set process noise covariance of disturbance evoluation.
voidset_x0(const Vector & x0)
Set initial state.
voidset_P0(const Matrix & P0)
Set covariance of initial state.
voidset_P0_m(const Matrix & P0_m)
Set covariance of initial process disturbance.
voidset_C(const Matrix & C)
Set output matrix.
voidset_d(const Vector & d)
Set output bias.
voidset_x(const Vector & x)
Set state of system.
voidReset()
Reset system variables.
voidPrint()
Print system variables to stdout.
-

Protected Functions inherited from lds::System

- - - - - - - - - - - - - -
Name
voidInitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)
-

Public Attributes inherited from lds::System

- - - - - - - - - - - - - -
Name
booldo_adapt_m
whether to adaptively estimate disturbance m
-

Protected Attributes inherited from lds::System

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
std::size_tn_x_
number of states
std::size_tn_u_
number of inputs
std::size_tn_y_
number of outputs
data_tdt_
sample period
Vectorx_
state
MatrixP_
covariance of state estimate
Vectorm_
process disturbance
MatrixP_m_
covariance of disturbance estimate
Vectorcx_
C*x.
Vectory_
output
Vectorz_
measurement
Vectorx0_
initial state
MatrixP0_
covariance of initial state estimate
Vectorm0_
initial process disturbance
MatrixP0_m_
covariance of initial disturbance est.
MatrixA_
state matrix
MatrixB_
input matrix
Vectorg_
input gain
MatrixQ_
covariance of process noise
MatrixQ_m_
covariance of disturbance random walk
MatrixC_
output matrix
Vectord_
output bias
MatrixKe_
estimator gain
MatrixKe_m_
estimator gain for process disturbance
-
-
-

- Public Function Details - # -

-

- System - # -

-
System() =default
-

-

- System - # -

-
System(
-    std::size_t n_u,
-    std::size_t n_x,
-    std::size_t n_y,
-    data_t dt,
-    data_t p0 =kDefaultP0,
-    data_t q0 =kDefaultQ0
-)
-

Parameters:

-
    -
  • n_u number of inputs
  • -
  • n_x number of states
  • -
  • n_y number of outputs
  • -
  • dt sample period
  • -
  • p0 [optional] initial diagonal elements of state estimate covariance (P)
  • -
  • q0 [optional] initial diagonal elements of process noise covariance (Q)
  • -
-
-

- Simulate - # -

-
virtual const Vector & Simulate(
-    const Vector & u_tm1
-) override
-

Parameters:

-
    -
  • u_tm1 input at t-1
  • -
-

Return: z measurement

-

Reimplements: lds::System::Simulate

-

Simulate system and produce measurement

-
-

- Protected Function Details - # -

-

- h - # -

-
inline virtual void h() override
-

Reimplements: lds::System::h

-
-

- RecurseKe - # -

-
virtual void RecurseKe() override
-

Reimplements: lds::System::RecurseKe

-

Recursively recalculate estimator gain (Ke).

-

References:

-

Smith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.

-

Eden UT, …, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1ssid/index.html b/docs/docs/api/classes/classlds_1_1ssid/index.html deleted file mode 100644 index 8fec3477..00000000 --- a/docs/docs/api/classes/classlds_1_1ssid/index.html +++ /dev/null @@ -1,712 +0,0 @@ - - - - - - - - - - - - - -lds::SSID | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::SSID - - -
- - - - - - -
- - - -

- lds::SSID - # -

-

More…

-

Inherited by lds::gaussian::FitSSID, lds::poisson::FitSSID

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - -
Name
SSID() =default
Constructs a new SSIDFit type.
SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList< kMatFreeDim2 > && u_train, UniformMatrixList< kMatFreeDim2 > && z_train, const Vector & d =Vector(1).fill(-kInf))
Constructs a new SSIDFit type.
std::tuple< Fit, Vector >Run(SSIDWt ssid_wt)
Runs fitting by subspace identification (SSID)
std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > >ReturnData()
Returns the I/O data to caller.
-

- Protected Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
voidCalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)
Using periods of silence in inputs (u), calculates the output \ bias (d)
voidCreateHankelDataMat()
Creates the block-hankel I/O data matrix.
virtual voidDecomposeData() =0
Decompose data to lower-triangular matrix (used in Solve)
voidCalcSVD(SSIDWt wt)
performs the singular value decomposition (SVD)
voidSolve(data_t wt_dc)
solves for LDS parameters
voidRecomputeExtObs()
recompute extended observability matrix from estimates of A, C
-

- Protected Attributes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformMatrixList< kMatFreeDim2 >u_
input training data
UniformMatrixList< kMatFreeDim2 >z_
measurement training data
MatrixD_
block-Hankel I/O data matrix
Fitfit_
fit
Matrixg_dc_
I/O gain @ DC.
data_tdt_
sample period
size_tn_u_
number of inputs
size_tn_x_
number of states
size_tn_y_
number of outputs
size_tn_h_
size_tn_trials_
number of input/output data sequences
std::vector< size_t >n_t_
number of time steps
size_tn_t_tot_
total number of time steps across trials
MatrixL_
lower triangle decomp of covariance matrix
Vectors_
singular values
Matrixext_obs_t_
extended observability matrix
-

- Detailed Description - # -

-
template <typename Fit >
-class lds::SSID;
-

-
-

- Public Function Details - # -

-

- SSID - # -

-
SSID() =default
-

-

- SSID - # -

-
SSID(
-    size_t n_x,
-    size_t n_h,
-    data_t dt,
-    UniformMatrixList< kMatFreeDim2 > && u_train,
-    UniformMatrixList< kMatFreeDim2 > && z_train,
-    const Vector & d =Vector(1).fill(-kInf)
-)
-

Parameters:

-
    -
  • n_x number of states
  • -
  • n_h size of block-hankel data matrix
  • -
  • dt sample period
  • -
  • u_train input training data
  • -
  • z_train measurement training data
  • -
  • d output bias
  • -
-
-

- Run - # -

-
std::tuple< Fit, Vector > Run(
-    SSIDWt ssid_wt
-)
-

Parameters:

-
    -
  • ssid_wt weight for singular value decomp
  • -
-

Return: tuple (Fit, singular values)

-
-

- ReturnData - # -

-
inline std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > > ReturnData()
-

Return: tuple(input data, output data)

-
-

- Protected Function Details - # -

-

- CalcD - # -

-
void CalcD(
-    data_t t_silence =0.1,
-    data_t thresh_silence =0.001
-)
-

Parameters:

-
    -
  • t_silence threshold on period of time that qualifies as “silence”
  • -
  • thresh_silence threshold on input amplitude u that qualifies as “silence”
  • -
-
-

- CreateHankelDataMat - # -

-
void CreateHankelDataMat()
-

Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.

-
-

- DecomposeData - # -

-
virtual void DecomposeData() =0
-

Reimplemented by: lds::poisson::FitSSID::DecomposeData, lds::gaussian::FitSSID::DecomposeData

-
-

- CalcSVD - # -

-
void CalcSVD(
-    SSIDWt wt
-)
-

Parameters:

-
    -
  • ssid_wt weight for SVD
  • -
-
-

- Solve - # -

-
void Solve(
-    data_t wt_dc
-)
-

Parameters:

-
    -
  • wt_dc weight placed on getting correct DC I/O gain
  • -
-
-

- RecomputeExtObs - # -

-
void RecomputeExtObs()
-

-

- Protected Attribute Details - # -

-

- **u_** - # -

-
UniformMatrixList< kMatFreeDim2 > u_;
-

-

- **z_** - # -

-
UniformMatrixList< kMatFreeDim2 > z_;
-

-

- **D_** - # -

-
Matrix D_;
-

-

- **fit_** - # -

-
Fit fit_;
-

-

- **g_dc_** - # -

-
Matrix g_dc_;
-

-

- **dt_** - # -

-
data_t dt_ {};
-

-

- **n_u_** - # -

-
size_t n_u_ {};
-

-

- **n_x_** - # -

-
size_t n_x_ {};
-

-

- **n_y_** - # -

-
size_t n_y_ {};
-

-

- **n_h_** - # -

-
size_t n_h_ {};
-

-

- **n_trials_** - # -

-
size_t n_trials_ {};
-

-

- **n_t_** - # -

-
std::vector< size_t > n_t_;
-

-

- **n_t_tot_** - # -

-
size_t n_t_tot_ {};
-

-

- **L_** - # -

-
Matrix L_;
-

-

- **s_** - # -

-
Vector s_;
-

-

- **ext_obs_t_** - # -

-
Matrix ext_obs_t_;
-

-
-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1switchedcontroller/index.html b/docs/docs/api/classes/classlds_1_1switchedcontroller/index.html deleted file mode 100644 index 1f4727c0..00000000 --- a/docs/docs/api/classes/classlds_1_1switchedcontroller/index.html +++ /dev/null @@ -1,846 +0,0 @@ - - - - - - - - - - - - - -lds::SwitchedController | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::SwitchedController - - -
- - - - - - -
- - - -

- lds::SwitchedController - # -

-

SwitchedController Type. More…

-


#include <lds_sctrl.h>

-

Inherits from lds::Controller< System >

-

Inherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
SwitchedController() =default
Constructs a new SwitchedController.
SwitchedController(const std::vector< System > & systems, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new SwitchedController.
SwitchedController(std::vector< System > && systems, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new SwitchedController (moves systems).
voidSwitch(size_t idx, bool do_force_switch =false)
Switch to a different sub-system/controller.
voidset_Kc(const UniformMatrixList<> & Kc)
sets state feedback gains
voidset_Kc(UniformMatrixList<> && Kc)
sets state feedback gains (moving)
voidset_Kc_inty(const UniformMatrixList<> & Kc_inty)
sets integral feedback gains
voidset_Kc_inty(UniformMatrixList<> && Kc_inty)
sets integral feedback gains (moving)
voidset_Kc_u(const UniformMatrixList<> & Kc_u)
sets input feedback gains
voidset_Kc_u(UniformMatrixList<> && Kc_u)
sets input feedback gains (moving)
voidset_g_design(const UniformVectorList & g)
sets input gain used during controller design
voidset_g_design(UniformVectorList && g)
sets input gain used during controller design (moving)
-

- Protected Attributes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
std::vector< System >systems_
underlying sub-systems which are switched between
size_tn_sys_
number of systems
size_tidx_
current system/controller index.
UniformMatrixListKc_list_
UniformMatrixListKc_inty_list_
UniformMatrixListKc_u_list_
UniformVectorListg_design_list_
-

- Additional inherited members - # -

-

Public Functions inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Controller() =default
Constructs a new Controller.
Controller(const System & sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller.
Controller(System && sys, data_t u_lb, data_t u_ub, size_t control_type =0)
Constructs a new Controller by moving the system object.
const Vector &Control(const Vector & z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal (single-step)
const Vector &ControlOutputReference(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)
updates control signal, given previously-set (single-step)
const System &sys() const
const Matrix &Kc() const
Get state feedback controller gain.
const Matrix &Kc_inty() const
Get integral controller gain.
const Matrix &Kc_u() const
Get input feedback controller gain.
const Vector &g_design() const
Get input gain used in controller design.
const Vector &u_ref() const
Get reference input.
const Vector &x_ref() const
Get reference state.
const Vector &y_ref() const
Get reference output.
size_tcontrol_type() const
Get controller type.
data_ttau_awu() const
Get time constant of anti-integral-windup.
data_tu_lb() const
Get control lower bound.
data_tu_ub() const
Get control upper bound.
voidset_sys(const System & sys)
Set system.
voidset_u_ref(const Vector & u_ref)
Set reference input (u_ref)
voidset_x_ref(const Vector & x_ref)
Set reference state (x_ref)
virtual voidset_y_ref(const Vector & y_ref)
Set reference output (y_ref)
voidset_tau_awu(data_t tau)
Set time constant of anti-integral-windup.
voidset_control_type(size_t control_type)
Sets the control type.
voidset_u_lb(data_t u_lb)
sets control lower bound
voidset_u_ub(data_t u_ub)
Sets control upper bound.
voidReset()
reset system and control variables.
voidPrint()
prints variables to stdout
-

Protected Attributes inherited from lds::Controller< System >

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
Systemsys_
underlying LDS
Vectoru_
control signal
Vectoru_return_
control signal that is returned to user
Vectorg_design_
input gain of the system used for controller design
Vectoru_ref_
reference input
Vectoru_ref_prev_
reference input at previous time step
Vectorx_ref_
reference state
Vectory_ref_
reference output
Vectorcx_ref_
MatrixKc_
state controller gain
MatrixKc_u_
input controller gain (optional when control updates )
MatrixKc_inty_
integral controller gain
Vectordu_ref_
Vectordv_ref_
Vectorv_ref_
Vectordv_
Vectorv_
Control after g inversion (e.g., control in physical units)
Vectorint_e_
integrated error
Vectorint_e_awu_adjust_
anti-windup adjustment to intE
Vectoru_sat_
control signal after saturation (for antiWindup)
booldo_control_prev_
booldo_lock_control_prev_
boolu_saturated_
whether control signal has reached saturation limits
data_tu_lb_
lower bound on control
data_tu_ub_
upper bound on control
data_ttau_awu_
antiwindup time constant
data_tk_awu_
data_tt_since_control_onset_
time since control epoch onset
size_tcontrol_type_
controller type
-

- Detailed Description - # -

-
template <typename System >
-class lds::SwitchedController;
-

-
-

- Public Function Details - # -

-

- SwitchedController - # -

-
SwitchedController() =default
-

-

- SwitchedController - # -

-
inline SwitchedController(
-    const std::vector< System > & systems,
-    data_t u_lb,
-    data_t u_ub,
-    size_t control_type =0
-)
-

Parameters:

-
    -
  • systems vector of sub-systems
  • -
  • u_lb lower bound on control (u)
  • -
  • u_ub upper bound on control (u)
  • -
  • control_type [optional] control type bit mask
  • -
-
-

- SwitchedController - # -

-
inline SwitchedController(
-    std::vector< System > && systems,
-    data_t u_lb,
-    data_t u_ub,
-    size_t control_type =0
-)
-

Parameters:

-
    -
  • systems vector of sub-systems
  • -
  • u_lb lower bound on control (u)
  • -
  • u_ub upper bound on control (u)
  • -
  • control_type [optional] control type bit mask
  • -
-
-

- Switch - # -

-
inline void Switch(
-    size_t idx,
-    bool do_force_switch =false
-)
-

Parameters:

-
    -
  • idx index
  • -
  • do_force_switch whether to force a system switch even if already there.
  • -
-
-

- set_Kc - # -

-
inline void set_Kc(
-    const UniformMatrixList<> & Kc
-)
-

-

- set_Kc - # -

-
inline void set_Kc(
-    UniformMatrixList<> && Kc
-)
-

-

- set_Kc_inty - # -

-
inline void set_Kc_inty(
-    const UniformMatrixList<> & Kc_inty
-)
-

-

- set_Kc_inty - # -

-
inline void set_Kc_inty(
-    UniformMatrixList<> && Kc_inty
-)
-

-

- set_Kc_u - # -

-
inline void set_Kc_u(
-    const UniformMatrixList<> & Kc_u
-)
-

-

- set_Kc_u - # -

-
inline void set_Kc_u(
-    UniformMatrixList<> && Kc_u
-)
-

-

- set_g_design - # -

-
inline void set_g_design(
-    const UniformVectorList & g
-)
-

-

- set_g_design - # -

-
inline void set_g_design(
-    UniformVectorList && g
-)
-

-

- Protected Attribute Details - # -

-

- **systems_** - # -

-
std::vector< System > systems_;
-

-

- **n_sys_** - # -

-
size_t n_sys_ {};
-

-

- **idx_** - # -

-
size_t idx_ {};
-

-

- **Kc_list_** - # -

-
UniformMatrixList Kc_list_;
-

-

- **Kc_inty_list_** - # -

-
UniformMatrixList Kc_inty_list_;
-

-

- **Kc_u_list_** - # -

-
UniformMatrixList Kc_u_list_;
-

-

- **g_design_list_** - # -

-
UniformVectorList g_design_list_;
-

-
-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1system/index.html b/docs/docs/api/classes/classlds_1_1system/index.html deleted file mode 100644 index f73428e3..00000000 --- a/docs/docs/api/classes/classlds_1_1system/index.html +++ /dev/null @@ -1,1288 +0,0 @@ - - - - - - - - - - - - - -lds::System | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::System - - -
- - - - - - -
- - - -

- lds::System - # -

-

Linear Dynamical System Type. -
#include <lds_sys.h>

-

Inherited by lds::gaussian::System, lds::poisson::System

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
System() =default
Constructs a new System.
System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)
constructs a new System
virtual~System()
voidFilter(const Vector & u_tm1, const Vector & z)
Filter data to produce causal state estimates.
virtual const Vector &Simulate(const Vector & u_tm1) =0
simulates system (single time step)
voidf(const Vector & u, bool do_add_noise =false)
system dynamics function
virtual voidh() =0
system output function
size_tn_u() const
Get number of inputs.
size_tn_x() const
Get number of states.
size_tn_y() const
Get number of outputs.
data_tdt() const
Get sample period.
const Vector &x() const
Get current state.
const Matrix &P() const
Get covariance of state estimate.
const Vector &m() const
Get current process disturbance/bias.
const Matrix &P_m() const
Get covariance of process disturbance estimate.
const Vector &cx() const
Get C*x.
const Vector &y() const
Get output.
const Vector &x0() const
Get initial state.
const Vector &m0() const
Get initial disturbance.
const Matrix &A() const
Get state matrix.
const Matrix &B() const
Get input matrix.
const Vector &g() const
Get input gain/conversion factor.
const Matrix &C() const
Get output matrix.
const Vector &d() const
Get output bias.
const Matrix &Ke() const
Get estimator gain.
const Matrix &Ke_m() const
Get estimator gain for process disturbance (m)
const Matrix &Q()
Get process noise covariance.
const Matrix &Q_m()
Get process noise covariance of disturbance evoluation.
const Matrix &P0()
Get covariance of initial state.
const Matrix &P0_m()
Get covariance of initial process disturbance.
voidset_A(const Matrix & A)
Set state matrix.
voidset_B(const Matrix & B)
Set input matrix.
voidset_m(const Vector & m, bool do_force_assign =false)
Set process disturbance.
voidset_g(const Vector & g)
Set input gain.
voidset_Q(const Matrix & Q)
Set process noise covariance.
voidset_Q_m(const Matrix & Q_m)
Set process noise covariance of disturbance evoluation.
voidset_x0(const Vector & x0)
Set initial state.
voidset_P0(const Matrix & P0)
Set covariance of initial state.
voidset_P0_m(const Matrix & P0_m)
Set covariance of initial process disturbance.
voidset_C(const Matrix & C)
Set output matrix.
voidset_d(const Vector & d)
Set output bias.
voidset_x(const Vector & x)
Set state of system.
voidReset()
Reset system variables.
voidPrint()
Print system variables to stdout.
-

- Protected Functions - # -

- - - - - - - - - - - - - - - - - -
Name
virtual voidRecurseKe() =0
Recursively recalculate estimator gain (Ke)
voidInitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)
-

- Public Attributes - # -

- - - - - - - - - - - - - -
Name
booldo_adapt_m
whether to adaptively estimate disturbance m
-

- Protected Attributes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
std::size_tn_x_
number of states
std::size_tn_u_
number of inputs
std::size_tn_y_
number of outputs
data_tdt_
sample period
Vectorx_
state
MatrixP_
covariance of state estimate
Vectorm_
process disturbance
MatrixP_m_
covariance of disturbance estimate
Vectorcx_
C*x.
Vectory_
output
Vectorz_
measurement
Vectorx0_
initial state
MatrixP0_
covariance of initial state estimate
Vectorm0_
initial process disturbance
MatrixP0_m_
covariance of initial disturbance est.
MatrixA_
state matrix
MatrixB_
input matrix
Vectorg_
input gain
MatrixQ_
covariance of process noise
MatrixQ_m_
covariance of disturbance random walk
MatrixC_
output matrix
Vectord_
output bias
MatrixKe_
estimator gain
MatrixKe_m_
estimator gain for process disturbance
-
-
-

- Public Function Details - # -

-

- System - # -

-
System() =default
-

-

- System - # -

-
System(
-    size_t n_u,
-    size_t n_x,
-    size_t n_y,
-    data_t dt,
-    data_t p0 =kDefaultP0,
-    data_t q0 =kDefaultQ0
-)
-

Parameters:

-
    -
  • n_u number of inputs
  • -
  • n_x number of states
  • -
  • n_y number of outputs
  • -
  • dt sample period
  • -
  • p0 diagonal elements for state estimate covariance
  • -
  • q0 diagonal elements for process noise covariance
  • -
-
-

- ~System - # -

-
inline virtual ~System()
-

-

- Filter - # -

-
void Filter(
-    const Vector & u_tm1,
-    const Vector & z
-)
-

Parameters:

-
    -
  • u_tm1 input at t-minus-1
  • -
  • z_t current measurement
  • -
-

Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.

-
-

- Simulate - # -

-
virtual const Vector & Simulate(
-    const Vector & u_tm1
-) =0
-

Parameters:

-
    -
  • u_tm1 input at time t-1
  • -
-

Return: simulated measurement at time t

-

Reimplemented by: lds::poisson::System::Simulate, lds::gaussian::System::Simulate

-
-

- f - # -

-
inline void f(
-    const Vector & u,
-    bool do_add_noise =false
-)
-

Parameters:

-
    -
  • u input
  • -
  • do_add_noise whether to add simulated process noise
  • -
-
-

- h - # -

-
virtual void h() =0
-

Reimplemented by: lds::poisson::System::h, lds::gaussian::System::h

-
-

- n_u - # -

-
inline size_t n_u() const
-

-

- n_x - # -

-
inline size_t n_x() const
-

-

- n_y - # -

-
inline size_t n_y() const
-

-

- dt - # -

-
inline data_t dt() const
-

-

- x - # -

-
inline const Vector & x() const
-

-

- P - # -

-
inline const Matrix & P() const
-

-

- m - # -

-
inline const Vector & m() const
-

-

- P_m - # -

-
inline const Matrix & P_m() const
-

-

- cx - # -

-
inline const Vector & cx() const
-

-

- y - # -

-
inline const Vector & y() const
-

-

- x0 - # -

-
inline const Vector & x0() const
-

-

- m0 - # -

-
inline const Vector & m0() const
-

-

- A - # -

-
inline const Matrix & A() const
-

-

- B - # -

-
inline const Matrix & B() const
-

-

- g - # -

-
inline const Vector & g() const
-

-

- C - # -

-
inline const Matrix & C() const
-

-

- d - # -

-
inline const Vector & d() const
-

-

- Ke - # -

-
inline const Matrix & Ke() const
-

-

- Ke_m - # -

-
inline const Matrix & Ke_m() const
-

-

- Q - # -

-
inline const Matrix & Q()
-

-

- Q_m - # -

-
inline const Matrix & Q_m()
-

-

- P0 - # -

-
inline const Matrix & P0()
-

-

- P0_m - # -

-
inline const Matrix & P0_m()
-

-

- set_A - # -

-
inline void set_A(
-    const Matrix & A
-)
-

-

- set_B - # -

-
inline void set_B(
-    const Matrix & B
-)
-

-

- set_m - # -

-
inline void set_m(
-    const Vector & m,
-    bool do_force_assign =false
-)
-

-

- set_g - # -

-
inline void set_g(
-    const Vector & g
-)
-

-

- set_Q - # -

-
inline void set_Q(
-    const Matrix & Q
-)
-

-

- set_Q_m - # -

-
inline void set_Q_m(
-    const Matrix & Q_m
-)
-

-

- set_x0 - # -

-
inline void set_x0(
-    const Vector & x0
-)
-

-

- set_P0 - # -

-
inline void set_P0(
-    const Matrix & P0
-)
-

-

- set_P0_m - # -

-
inline void set_P0_m(
-    const Matrix & P0_m
-)
-

-

- set_C - # -

-
inline void set_C(
-    const Matrix & C
-)
-

-

- set_d - # -

-
inline void set_d(
-    const Vector & d
-)
-

-

- set_x - # -

-
inline void set_x(
-    const Vector & x
-)
-

-

- Reset - # -

-
void Reset()
-

-

- Print - # -

-
void Print()
-

-

- Protected Function Details - # -

-

- RecurseKe - # -

-
virtual void RecurseKe() =0
-

Reimplemented by: lds::poisson::System::RecurseKe, lds::gaussian::System::RecurseKe

-
-

- InitVars - # -

-
void InitVars(
-    data_t p0 =kDefaultP0,
-    data_t q0 =kDefaultQ0
-)
-

-

- Public Attribute Details - # -

-

- do_adapt_m - # -

-
bool do_adapt_m {};
-

-

- Protected Attribute Details - # -

-

- **n_x_** - # -

-
std::size_t n_x_ {};
-

-

- **n_u_** - # -

-
std::size_t n_u_ {};
-

-

- **n_y_** - # -

-
std::size_t n_y_ {};
-

-

- **dt_** - # -

-
data_t dt_ {};
-

-

- **x_** - # -

-
Vector x_;
-

-

- **P_** - # -

-
Matrix P_;
-

-

- **m_** - # -

-
Vector m_;
-

-

- **P_m_** - # -

-
Matrix P_m_;
-

-

- **cx_** - # -

-
Vector cx_;
-

-

- **y_** - # -

-
Vector y_;
-

-

- **z_** - # -

-
Vector z_;
-

-

- **x0_** - # -

-
Vector x0_;
-

-

- **P0_** - # -

-
Matrix P0_;
-

-

- **m0_** - # -

-
Vector m0_;
-

-

- **P0_m_** - # -

-
Matrix P0_m_;
-

-

- **A_** - # -

-
Matrix A_;
-

-

- **B_** - # -

-
Matrix B_;
-

-

- **g_** - # -

-
Vector g_;
-

-

- **Q_** - # -

-
Matrix Q_;
-

-

- **Q_m_** - # -

-
Matrix Q_m_;
-

-

- **C_** - # -

-
Matrix C_;
-

-

- **d_** - # -

-
Vector d_;
-

-

- **Ke_** - # -

-
Matrix Ke_;
-

-

- **Ke_m_** - # -

-
Matrix Ke_m_;
-

-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1uniformmatrixlist/index.html b/docs/docs/api/classes/classlds_1_1uniformmatrixlist/index.html deleted file mode 100644 index cabd53ec..00000000 --- a/docs/docs/api/classes/classlds_1_1uniformmatrixlist/index.html +++ /dev/null @@ -1,519 +0,0 @@ - - - - - - - - - - - - - -lds::UniformMatrixList | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::UniformMatrixList - - -
- - - - - - -
- - - -

- lds::UniformMatrixList - # -

-

More…

-

Inherits from std::vector< Matrix >

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformMatrixList() =default
Constructs a new UniformMatrixList.
UniformMatrixList(const std::vector< Matrix > & mats, std::array< size_t, 2 > dim ={0, 0})
Constructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent.
UniformMatrixList(std::vector< Matrix > && mats, std::array< size_t, 2 > dim ={0, 0})
Constructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent.
UniformMatrixList(std::initializer_list< Matrix > mats, std::array< size_t, 2 > dim ={0, 0})
Constructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent.
UniformMatrixList(const UniformMatrixList< D > & that)
Constructs a new UniformMatrixList (copy).
UniformMatrixList(UniformMatrixList< D > && that)
Constructs a new UniformMatrixList (move).
~UniformMatrixList() =default
Destroys the object.
const std::array< size_t, 2 > &dim(size_t n =0) const
gets dimensions of uniformly sized matrices
size_tsize()
size of container
const Matrix &at(size_t n)
gets reference to n^th element
voidSwap(Matrix & that, size_t n)
swaps input matrix with n^th matrix of list
UniformMatrixList< D > &operator=(const UniformMatrixList< D > & that)
assigns the contents (copy)
UniformMatrixList< D > &operator=(UniformMatrixList< D > && that)
assigns the contents (move)
-

- Detailed Description - # -

-
template <MatrixListFreeDim D =kMatFreeDimNone>
-class lds::UniformMatrixList;
-

-
-

- Public Function Details - # -

-

- UniformMatrixList - # -

-
UniformMatrixList() =default
-

-

- UniformMatrixList - # -

-
explicit UniformMatrixList(
-    const std::vector< Matrix > & mats,
-    std::array< size_t, 2 > dim ={0, 0}
-)
-

Parameters:

-
    -
  • mats input matrices
  • -
  • dim dimensions
  • -
-
-

- UniformMatrixList - # -

-
explicit UniformMatrixList(
-    std::vector< Matrix > && mats,
-    std::array< size_t, 2 > dim ={0, 0}
-)
-

Parameters:

-
    -
  • mats input matrices
  • -
  • dim dimensions
  • -
-
-

- UniformMatrixList - # -

-
UniformMatrixList(
-    std::initializer_list< Matrix > mats,
-    std::array< size_t, 2 > dim ={0, 0}
-)
-

Parameters:

-
    -
  • mats input matrices
  • -
  • dim dimensions
  • -
-
-

- UniformMatrixList - # -

-
UniformMatrixList(
-    const UniformMatrixList< D > & that
-)
-

Parameters:

- -
-

- UniformMatrixList - # -

-
UniformMatrixList(
-    UniformMatrixList< D > && that
-)
-

Parameters:

- -
-

- ~UniformMatrixList - # -

-
~UniformMatrixList() =default
-

-

- dim - # -

-
inline const std::array< size_t, 2 > & dim(
-    size_t n =0
-) const
-

Parameters:

-
    -
  • n [optional] index in list of matrices
  • -
-

Return: dimensions

-
-

- size - # -

-
inline size_t size()
-

-

- at - # -

-
inline const Matrix & at(
-    size_t n
-)
-

-

- Swap - # -

-
inline void Swap(
-    Matrix & that,
-    size_t n
-)
-

Parameters:

-
    -
  • that input matrix
  • -
  • n index where the matrix is moved
  • -
-
-

- operator= - # -

-
inline UniformMatrixList< D > & operator=(
-    const UniformMatrixList< D > & that
-)
-

Parameters:

- -

Return: reference to object

-
-

- operator= - # -

-
inline UniformMatrixList< D > & operator=(
-    UniformMatrixList< D > && that
-)
-

Parameters:

- -

Return: reference to object

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1uniformsystemlist/index.html b/docs/docs/api/classes/classlds_1_1uniformsystemlist/index.html deleted file mode 100644 index d9fe4c23..00000000 --- a/docs/docs/api/classes/classlds_1_1uniformsystemlist/index.html +++ /dev/null @@ -1,512 +0,0 @@ - - - - - - - - - - - - - -lds::UniformSystemList | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::UniformSystemList - - -
- - - - - - -
- - - -

- lds::UniformSystemList - # -

-

More…

-

Inherits from std::vector< System >

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformSystemList() =default
Constructs a new UniformSystemList.
UniformSystemList(const std::vector< System > & systems, std::array< size_t, 3 > dim ={0, 0, 0})
Constructs a new UniformSystemList by copying existing vector of System if dimensions consistent.
UniformSystemList(std::vector< System > && systems, std::array< size_t, 3 > dim ={0, 0, 0})
Constructs a new UniformSystemList by moving existing vector of System if dimensions consistent.
UniformSystemList(std::initializer_list< System > systems, std::array< size_t, 3 > dim ={0, 0, 0})
Constructs a new UniformSystemList from initializer_list of System if dimensions consistent.
UniformSystemList(const UniformSystemList & that)
Constructs a new UniformSystemList (copy).
UniformSystemList(UniformSystemList && that)
Constructs a new UniformSystemList (move).
~UniformSystemList() =default
Destroys the object.
const std::array< size_t, 3 > &dim() const
gets dimensions of the uniformly sized systems
size_tsize()
size of container
const System &at(size_t n)
gets reference to n^th element
voidSwap(System & that, size_t n)
swaps input system with n^th system of list
UniformSystemList &operator=(const UniformSystemList & that)
assigns the contents (copy)
UniformSystemList &operator=(UniformSystemList && that)
assigns the contents (move)
-

- Detailed Description - # -

-
template <typename System>
-class lds::UniformSystemList;
-

-
-

- Public Function Details - # -

-

- UniformSystemList - # -

-
UniformSystemList() =default
-

-

- UniformSystemList - # -

-
explicit UniformSystemList(
-    const std::vector< System > & systems,
-    std::array< size_t, 3 > dim ={0, 0, 0}
-)
-

Parameters:

-
    -
  • systems input systems
  • -
  • dim dimensions (n_u, n_x, n_y)
  • -
-
-

- UniformSystemList - # -

-
explicit UniformSystemList(
-    std::vector< System > && systems,
-    std::array< size_t, 3 > dim ={0, 0, 0}
-)
-

Parameters:

-
    -
  • systems input systems
  • -
  • dim dimensions (n_u, n_x, n_y)
  • -
-
-

- UniformSystemList - # -

-
UniformSystemList(
-    std::initializer_list< System > systems,
-    std::array< size_t, 3 > dim ={0, 0, 0}
-)
-

Parameters:

-
    -
  • systems input systems
  • -
  • dim dimensions (n_u, n_x, n_y)
  • -
-
-

- UniformSystemList - # -

-
UniformSystemList(
-    const UniformSystemList & that
-)
-

Parameters:

- -
-

- UniformSystemList - # -

-
UniformSystemList(
-    UniformSystemList && that
-)
-

Parameters:

- -
-

- ~UniformSystemList - # -

-
~UniformSystemList() =default
-

-

- dim - # -

-
inline const std::array< size_t, 3 > & dim() const
-

-

- size - # -

-
inline size_t size()
-

-

- at - # -

-
inline const System & at(
-    size_t n
-)
-

-

- Swap - # -

-
inline void Swap(
-    System & that,
-    size_t n
-)
-

Parameters:

-
    -
  • that input system
  • -
  • n index where the system is moved
  • -
-
-

- operator= - # -

-
inline UniformSystemList & operator=(
-    const UniformSystemList & that
-)
-

Parameters:

- -

Return: reference to object

-
-

- operator= - # -

-
inline UniformSystemList & operator=(
-    UniformSystemList && that
-)
-

Parameters:

- -

Return: reference to object

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/classlds_1_1uniformvectorlist/index.html b/docs/docs/api/classes/classlds_1_1uniformvectorlist/index.html deleted file mode 100644 index 3c5af133..00000000 --- a/docs/docs/api/classes/classlds_1_1uniformvectorlist/index.html +++ /dev/null @@ -1,503 +0,0 @@ - - - - - - - - - - - - - -lds::UniformVectorList | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::UniformVectorList - - -
- - - - - - -
- - - -

- lds::UniformVectorList - # -

-

Inherits from std::vector< Vector >

-

- Public Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
UniformVectorList() =default
Constructs a new UniformVectorList.
UniformVectorList(const std::vector< Vector > & vecs, size_t dim =0)
Constructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent.
UniformVectorList(std::vector< Vector > && vecs, size_t dim =0)
Constructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent.
UniformVectorList(std::initializer_list< Vector > vecs, size_t dim =0)
Constructs a new UniformVectorList from initializer_list of Vector if dimensions consistent.
UniformVectorList(const UniformVectorList & that)
Constructs a new UniformVectorList (copy)
UniformVectorList(UniformVectorList && that)
Constructs a new UniformVectorList (move)
~UniformVectorList() =default
Destroys the object.
size_tdim() const
gets dimensions of the uniformly sized matrices
size_tsize()
size of container
const Vector &at(size_t n)
gets reference to n^th element
voidSwap(Vector & that, size_t n)
swaps input matrix with n^th vector of list
UniformVectorList &operator=(const UniformVectorList & that)
assigns the contents (copy)
UniformVectorList &operator=(UniformVectorList && that)
assigns the contents (move)
-
-
-

- Public Function Details - # -

-

- UniformVectorList - # -

-
UniformVectorList() =default
-

-

- UniformVectorList - # -

-
explicit UniformVectorList(
-    const std::vector< Vector > & vecs,
-    size_t dim =0
-)
-

Parameters:

-
    -
  • vecs input vectors
  • -
  • dims dimension
  • -
-
-

- UniformVectorList - # -

-
explicit UniformVectorList(
-    std::vector< Vector > && vecs,
-    size_t dim =0
-)
-

Parameters:

-
    -
  • vecs input vectors
  • -
  • dim dimension
  • -
-
-

- UniformVectorList - # -

-
UniformVectorList(
-    std::initializer_list< Vector > vecs,
-    size_t dim =0
-)
-

Parameters:

-
    -
  • vecs input vectors
  • -
  • dim dimension
  • -
-
-

- UniformVectorList - # -

-
UniformVectorList(
-    const UniformVectorList & that
-)
-

Parameters:

- -
-

- UniformVectorList - # -

-
UniformVectorList(
-    UniformVectorList && that
-)
-

Parameters:

- -
-

- ~UniformVectorList - # -

-
~UniformVectorList() =default
-

-

- dim - # -

-
inline size_t dim() const
-

-

- size - # -

-
inline size_t size()
-

-

- at - # -

-
inline const Vector & at(
-    size_t n
-)
-

-

- Swap - # -

-
inline void Swap(
-    Vector & that,
-    size_t n
-)
-

Parameters:

-
    -
  • that input vector
  • -
  • n index where the vector is moved
  • -
-
-

- operator= - # -

-
inline UniformVectorList & operator=(
-    const UniformVectorList & that
-)
-

Parameters:

- -

Return: reference to object

-
-

- operator= - # -

-
inline UniformVectorList & operator=(
-    UniformVectorList && that
-)
-

Parameters:

- -

Return: reference to object

-
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/index.html b/docs/docs/api/classes/index.html deleted file mode 100644 index 5e959142..00000000 --- a/docs/docs/api/classes/index.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - - - - - - - -Classes | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - Classes - - -
- - - - - - -
- - - -

- Classes - # -

- -
-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/classes/index.xml b/docs/docs/api/classes/index.xml deleted file mode 100644 index a672af8d..00000000 --- a/docs/docs/api/classes/index.xml +++ /dev/null @@ -1,225 +0,0 @@ - - - - Classes on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/ - Recent content in Classes on LDS C&E - Hugo -- gohugo.io - - lds::Controller - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1controller/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1controller/ - lds::Controller # More&hellip; -Inherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController&lt; System &gt; -Public Functions # Name Controller() =default -Constructs a new Controller. Controller(const System &amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0) -Constructs a new Controller. Controller(System &amp;&amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0) -Constructs a new Controller by moving the system object. - - - - lds::EM - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1em/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1em/ - lds::EM # More&hellip; -Inherited by lds::gaussian::FitEM, lds::poisson::FitEM -Public Functions # Name EM() =default -Constructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train) -Constructs a new EMFit type. EM(const Fit &amp; fit0, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train) -Constructs a new EMFit type. - - - - lds::Fit - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1fit/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1fit/ - LDS Fit Type. - - - - lds::gaussian::Controller - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/ - Gaussian-observation Controller Type. - - - - lds::gaussian::Fit - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/ - GLDS Fit Type. - - - - lds::gaussian::FitEM - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/ - GLDS E-M Fit Type. - - - - lds::gaussian::FitSSID - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/ - Subspace Identification (SSID) for GLDS. - - - - lds::gaussian::SwitchedController - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/ - Gaussian-observation SwitchedController Type. - - - - lds::gaussian::System - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/ - Gaussian LDS Type. - - - - lds::poisson::Controller - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/ - PLDS Controller Type. - - - - lds::poisson::Fit - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/ - PLDS Fit Type. - - - - lds::poisson::FitEM - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/ - PLDS E-M Fit Type. - - - - lds::poisson::FitSSID - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/ - Subspace Identification (SSID) for PLDS. - - - - lds::poisson::SwitchedController - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/ - Poisson-observation SwitchedController Type. - - - - lds::poisson::System - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/ - Poisson System type. - - - - lds::SSID - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/ - lds::SSID # More&hellip; -Inherited by lds::gaussian::FitSSID, lds::poisson::FitSSID -Public Functions # Name SSID() =default -Constructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train, const Vector &amp; d =Vector(1).fill(-kInf)) -Constructs a new SSIDFit type. std::tuple&lt; Fit, Vector &gt; Run(SSIDWt ssid_wt) -Runs fitting by subspace identification (SSID) std::tuple&lt; UniformMatrixList&lt; kMatFreeDim2 &gt;, UniformMatrixList&lt; kMatFreeDim2 &gt; &gt; ReturnData() - - - - lds::SwitchedController - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/ - SwitchedController Type. - - - - lds::System - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1system/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1system/ - Linear Dynamical System Type. - - - - lds::UniformMatrixList - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/ - lds::UniformMatrixList # More&hellip; -Inherits from std::vector&lt; Matrix &gt; -Public Functions # Name UniformMatrixList() =default -Constructs a new UniformMatrixList. UniformMatrixList(const std::vector&lt; Matrix &gt; &amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0}) -Constructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector&lt; Matrix &gt; &amp;&amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0}) - - - - lds::UniformSystemList - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/ - lds::UniformSystemList # More&hellip; -Inherits from std::vector&lt; System &gt; -Public Functions # Name UniformSystemList() =default -Constructs a new UniformSystemList. UniformSystemList(const std::vector&lt; System &gt; &amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0}) -Constructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector&lt; System &gt; &amp;&amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0}) - - - - lds::UniformVectorList - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/ - lds::UniformVectorList # Inherits from std::vector&lt; Vector &gt; -Public Functions # Name UniformVectorList() =default -Constructs a new UniformVectorList. UniformVectorList(const std::vector&lt; Vector &gt; &amp; vecs, size_t dim =0) -Constructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector&lt; Vector &gt; &amp;&amp; vecs, size_t dim =0) -Constructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. - - - - diff --git a/docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html deleted file mode 100644 index 29f4ed7b..00000000 --- a/docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html +++ /dev/null @@ -1,501 +0,0 @@ - - - - - - - - - - - - - -eg_glds_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - eg_glds_ctrl.cpp - - -
- - - - - - -
- - - -

- eg_glds_ctrl.cpp - # -

-

Example GLDS Control

-
//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Gaussian LDS Control ********** \n\n";
-
-  // Make 1st-order SISO system, sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(5.0 / dt);
-
-  // construct ground truth system to be controlled...
-  // initializes to random walk model with top-most n_y state observed
-  lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);
-
-  // Ground-truth parameters for the controlled system
-  // (stand-in for physical system to be controlled)
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.01);
-  Matrix b_true = Matrix(n_x, n_u).fill(2e-4);
-  // control signal to model input unit conversion e.g., V -> mW/mm2:
-  Vector g_true = Vector(n_y).fill(10.0);
-
-  // output noise covariance
-  Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;
-
-  size_t which_m = 0;  // whether low or high disturbance (0, 1)
-  data_t m_low = 5 * dt * (1 - a_true[0]);
-  data_t pr_lo2hi = 1e-3;  // probability of going from low to high disturb.
-  data_t m_high = 20 * dt * (1 - a_true[0]);
-  data_t pr_hi2lo = pr_lo2hi;
-
-  // initially let m be low
-  Vector m0_true = Vector(n_x).fill(m_low);
-
-  // Assign params.
-  controlled_system.set_A(a_true);
-  controlled_system.set_B(b_true);
-  controlled_system.set_m(m0_true);
-  controlled_system.set_g(g_true);
-  controlled_system.set_R(r_true);
-
-  cout << ".....................................\n";
-  cout << "controlled_system:\n";
-  cout << ".....................................\n";
-  controlled_system.Print();
-  cout << ".....................................\n";
-
-  // make a controller
-  lds::gaussian::Controller controller;
-  {
-    // Create **incorrect** model used for control.
-    // (e.g., imperfect model fitting)
-    Matrix b_controller = b_true / 2;
-
-    // let's assume zero process disturbance initially
-    // (will be re-estimating)
-    Vector m_controller = Vector(n_x, arma::fill::zeros);
-
-    // for this demo, just use arbitrary default R
-    Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0;
-
-    lds::gaussian::System controller_system(controlled_system);
-    controller_system.set_B(b_controller);
-    controller_system.set_m(m_controller);
-    controller_system.set_R(r_controller);
-    controller_system.Reset();  // reset to new m
-
-    // going to adaptively re-estimate the disturbance
-    controller_system.do_adapt_m = true;
-
-    // set adaptation rate by changing covariance of assumed process noise
-    // acting on random-walk evolution of m
-    Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6;
-    controller_system.set_Q_m(q_m);
-
-    // create controller
-    // lower and upper bounds on control signal (e.g., in Volts)
-    data_t u_lb = 0.0;  // [=] V
-    data_t u_ub = 5.0;  // [=] V
-    controller = std::move(
-        lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub));
-  }
-
-  // Control variables:
-  // if following enabled, adapts set point with re-estimated process
-  // disturbance n.b., should not need integral action if this is enabled as the
-  // adaptive estimator minimizes DC error
-  bool do_adaptive_set_point = false;
-
-  // Reference/target output, controller gains
-  Vector y_ref0 = Vector(n_y).fill(20.0 * dt);
-  Matrix k_x = Matrix(n_u, n_x).fill(100);     // gains on state error
-  Matrix k_inty = Matrix(n_u, n_y).fill(1e3);  // gains on integrated err
-
-  // setting initial state to target to avoid error at onset:
-  Vector x0 = Vector(n_x).fill(y_ref0[0]);
-
-  // set up controller type bit mask so controller knows how to proceed
-  size_t control_type = 0;
-  if (do_adaptive_set_point) {
-    // adapt set point with estimated disturbance
-    control_type = control_type | lds::kControlTypeAdaptM;
-  } else {
-    // use integral action to minimize DC error
-    control_type = control_type | lds::kControlTypeIntY;
-  }
-
-  // set controller type
-  controller.set_control_type(control_type);
-
-  // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2):
-  Vector g_design = Vector(n_u).fill(9);
-
-  // Set params.
-  // **n.b. using arbitrary defaults for Q, R in this example. Really, these
-  // should be set by users, as they tune characteristics of Kalman filter.
-  // Users can also choose not to recursively calculate the estimator gain and
-  // supply it (setKe) instead of covariances.**
-  controller.set_y_ref(y_ref0);
-  controller.set_Kc(k_x);
-  controller.set_Kc_inty(k_inty);
-  controller.set_g_design(g_design);
-
-  cout << ".....................................\n";
-  cout << "control system:\n";
-  cout << ".....................................\n";
-  controller.Print();
-  cout << ".....................................\n";
-
-  // set up variables for simulation
-  // create Matrix to save outputs in...
-  Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];
-
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // simulated control signal ([=] V)
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // outputs, states and gain/disturbance params
-  // *_hat indicates online estimates
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_x, n_t, arma::fill::zeros);
-
-  // *_true indicates ground truth (system being controlled)
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_x, n_t, arma::fill::zeros);
-
-  // set initial val
-  y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y();
-  y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();
-
-  x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x();
-  x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();
-
-  m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m();
-  m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // simulate a stochastically switched disturbance
-    Vector chance = arma::randu<Vector>(1);
-    if (which_m == 0)  // low disturbance
-    {
-      if (chance[0] < pr_lo2hi) {  // switches low -> high disturbance
-        m0_true = std::vector<data_t>(n_x, m_high);
-        which_m = 1;
-      }
-    } else {                       // high disturbance
-      if (chance[0] < pr_hi2lo) {  // swithces high -> low disturbance
-        m0_true = std::vector<data_t>(n_x, m_low);
-        which_m = 0;
-      }
-    }
-    controlled_system.set_m(m0_true);
-
-    // input
-    Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true);
-
-    // Simulate the true system.
-    z.col(t) = controlled_system.Simulate(u_tm1);
-
-    // This method uses a steady-state solution to control problem to calculate
-    // x_ref, u_ref from reference output y_ref. Therefore, it is only
-    // applicable to regulation problems or cases where reference trajectory
-    // changes slowly compared to system dynamics.
-    u.col(t) = controller.ControlOutputReference(z.col(t));
-
-    // save the signals
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    m_true.col(t) = controlled_system.m();
-
-    y_hat.col(t) = controller.sys().y();
-    x_hat.col(t) = controller.sys().x();
-    m_hat.col(t) = controller.sys().m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  cout << "Saving simulation data to disk.\n";
-
-  // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue,
-  // xTrue, mTrue saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_glds_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_glds_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_glds_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_hat", replace));
-
-  cout << "fin.\n";
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html deleted file mode 100644 index c93dc738..00000000 --- a/docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html +++ /dev/null @@ -1,502 +0,0 @@ - - - - - - - - - - - - - -eg_glds_du_plds_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - eg_glds_du_plds_ctrl.cpp - - -
- - - - - - -
- - - -

- eg_glds_du_plds_ctrl.cpp - # -

-

Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).

-
//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::data_t;
-using lds::Matrix;
-using lds::Vector;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Gaussian LDS du Control of PLDS ********** \n\n";
-
-  // Make 1st-order SISO system, sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(5.0 / dt);
-
-  // construct ground truth system to be controlled...
-  // initializes to random walk model with top-most n_y state observed
-  lds::poisson::System controlled_system(n_u, n_x, n_y, dt);
-
-  // Ground-truth parameters for the controlled system
-  // (stand-in for physical system to be controlled)
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.01);
-  Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2);
-  // control signal to model input unit conversion e.g., V -> mW/mm2:
-  Vector g_true = Vector(n_y).fill(10.0);
-
-  size_t which_m = 0;  // whether low or high disturbance (0, 1)
-  data_t m_low = log(1 * dt) * (1 - a_true[0]);
-  data_t pr_lo2hi =
-      0;  // 1e-3;  // probability of going from low to high disturb.
-  data_t m_high = log(20 * dt) * (1 - a_true[0]);
-  data_t pr_hi2lo = pr_lo2hi;
-
-  // initially let m be low
-  Vector m0_true = Vector(n_x).fill(m_low);
-  Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);
-
-  // Assign params.
-  controlled_system.set_A(a_true);
-  controlled_system.set_B(b_true);
-  controlled_system.set_m(m0_true);
-  controlled_system.set_g(g_true);
-  controlled_system.set_x0(x0_true);
-  controlled_system.Reset();
-
-  cout << ".....................................\n";
-  cout << "controlled_system:\n";
-  cout << ".....................................\n";
-  controlled_system.Print();
-  cout << ".....................................\n";
-
-  // make a controller
-  lds::gaussian::Controller controller;
-  {
-    // Create **incorrect** model used for control.
-    // (e.g., imperfect model fitting)
-    Matrix b_controller = b_true / 50;
-
-    // let's assume zero process disturbance initially
-    // (will be re-estimating)
-    Vector m_controller = Vector(n_x, arma::fill::zeros);
-
-    // process noise covariance
-    Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8;
-
-    // output noise covariance
-    Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2;
-
-    lds::gaussian::System controller_system(n_u, n_x, n_y, dt);
-    controller_system.set_A(a_true);
-    controller_system.set_B(b_controller);
-    controller_system.set_g(g_true);
-    controller_system.set_m(m_controller);
-    controller_system.set_Q(q_controller);
-    controller_system.set_R(r_controller);
-    controller_system.Reset();  // reset to new m
-
-    // going to adaptively re-estimate the disturbance
-    controller_system.do_adapt_m = true;
-
-    // set adaptation rate by changing covariance of assumed process noise
-    // acting on random-walk evolution of m
-    Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8;
-    controller_system.set_Q_m(q_m);
-
-    // create controller
-    // lower and upper bounds on control signal (e.g., in Volts)
-    data_t u_lb = 0.0;  // [=] V
-    data_t u_ub = 5.0;  // [=] V
-    controller = std::move(
-        lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub));
-  }
-
-  // Control variables:
-  // Reference/target output, controller gains
-  Vector y_ref0 = Vector(n_y).fill(20.0 * dt);
-
-  // to design for this example, augmented state with control and made the input
-  // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du =
-  // 1e-2.
-  Matrix k_x = Matrix(n_u, n_x).fill(2.44);     // gains on state error
-  Matrix k_inty = Matrix(n_u, n_y).fill(97.4);  // gains on integrated err
-  Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2);  // gains on input amp
-
-  // set up controller type bit mask so controller knows how to proceed
-  size_t control_type = 0;
-  // use integral action to minimize DC error
-  control_type = control_type | lds::kControlTypeIntY;
-  // update change in control (LP filters control)
-  control_type = control_type | lds::kControlTypeDeltaU;
-
-  // set controller type
-  controller.set_control_type(control_type);
-
-  // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2):
-  Vector g_design = Vector(n_u).fill(10);
-
-  // Set params.
-  // **n.b. using arbitrary defaults for Q, R in this example. Really, these
-  // should be set by users, as they tune characteristics of Kalman filter.
-  // Users can also choose not to recursively calculate the estimator gain and
-  // supply it (setKe) instead of covariances.**
-  controller.set_y_ref(y_ref0);
-  controller.set_Kc(k_x);
-  controller.set_Kc_inty(k_inty);
-  controller.set_Kc_u(k_u);
-  controller.set_g_design(g_design);
-
-  cout << ".....................................\n";
-  cout << "control system:\n";
-  cout << ".....................................\n";
-  controller.Print();
-  cout << ".....................................\n";
-
-  // set up variables for simulation
-  // create Matrix to save outputs in...
-  Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];
-
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // simulated control signal ([=] V)
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // outputs, states and gain/disturbance params
-  // *_hat indicates online estimates
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_x, n_t, arma::fill::zeros);
-
-  // *_true indicates ground truth (system being controlled)
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_x, n_t, arma::fill::zeros);
-
-  // get initial val
-  y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y();
-  y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();
-
-  x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x();
-  x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();
-
-  m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m();
-  m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // simulate a stochastically switched disturbance
-    Vector chance = arma::randu<Vector>(1);
-    if (which_m == 0)  // low disturbance
-    {
-      if (chance[0] < pr_lo2hi) {  // switches low -> high disturbance
-        m0_true = std::vector<data_t>(n_x, m_high);
-        which_m = 1;
-      }
-    } else {                       // high disturbance
-      if (chance[0] < pr_hi2lo) {  // swithces high -> low disturbance
-        m0_true = std::vector<data_t>(n_x, m_low);
-        which_m = 0;
-      }
-    }
-    controlled_system.set_m(m0_true);
-
-    // input
-    Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true);
-
-    // Simulate the true system.
-    z.col(t) = controlled_system.Simulate(u_tm1);
-
-    // This method uses a steady-state solution to control problem to calculate
-    // x_ref, u_ref from reference output y_ref. Therefore, it is only
-    // applicable to regulation problems or cases where reference trajectory
-    // changes slowly compared to system dynamics.
-    u.col(t) = controller.ControlOutputReference(z.col(t));
-
-    // save the signals
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    m_true.col(t) = controlled_system.m();
-
-    y_hat.col(t) = controller.sys().y();
-    x_hat.col(t) = controller.sys().x();
-    m_hat.col(t) = controller.sys().m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  cout << "Saving simulation data to disk.\n";
-
-  // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue,
-  // xTrue, mTrue saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_hat", replace));
-
-  cout << "fin.\n";
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html deleted file mode 100644 index 60f4093b..00000000 --- a/docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html +++ /dev/null @@ -1,471 +0,0 @@ - - - - - - - - - - - - - -eg_plds_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - eg_plds_ctrl.cpp - - -
- - - - - - -
- - - -

- eg_plds_ctrl.cpp - # -

-

Example PLDS Control

-
//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Poisson LDS Control ********** \n\n";
-
-  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(10.0 / dt);
-
-  // Control variables: _reference/target output, controller gains
-  // n.b., Can either use Vector (arma::Col) or std::vector
-  Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt;
-  Matrix k_x =
-      Matrix(n_u, n_x, arma::fill::zeros) + 1;  // gains on state error
-  Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) +
-                   10;  // gains on integrated output err
-
-  // Set control type bit mask, so controller knows what to do
-  size_t control_type = lds::kControlTypeIntY;  // integral action
-
-  // Ground-truth parameters for the controlled system
-  // (stand-in for physical system to be controlled)
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = 0.986;
-  Matrix b_true(n_x, n_u, arma::fill::zeros);
-  b_true[0] = 0.054;
-  Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);
-
-  size_t which_m = 0;
-  data_t m_low = log(1 * dt) * (1 - a_true[0]);
-  data_t pr_lo2hi = 1e-3;
-  data_t m_high = log(20 * dt) * (1 - a_true[0]);
-  data_t pr_hi2lo = pr_lo2hi;
-
-  Vector m0_true = Vector(n_x, arma::fill::ones) * m_low;
-  // construct ground truth system to be controlled...
-  lds::poisson::System controlled_system(n_u, n_x, n_y, dt);
-
-  // Assign params.
-  controlled_system.set_A(a_true);
-  controlled_system.set_B(b_true);
-  controlled_system.set_m(m0_true);
-  controlled_system.set_x0(x0_true);
-  // reset to initial conditions
-  controlled_system.Reset();
-
-  cout << ".....................................\n";
-  cout << "controlled_system:\n";
-  cout << ".....................................\n";
-  controlled_system.Print();
-  cout << ".....................................\n";
-
-  // Create the controller
-  lds::poisson::Controller controller;
-  {
-    // Create model used for control.
-    lds::poisson::System controller_system(controlled_system);
-
-    // for this example, assume model correct, except disturbance
-    Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low;
-    Vector x0_controller = arma::log(y_ref0);
-    controller_system.set_m(m0_controller);
-    controller_system.set_x0(x0_controller);
-    controller_system.Reset(); //reset to new init condition
-
-    // adaptively re-estimate process disturbance (m)
-    controller_system.do_adapt_m = true;
-
-    // set adaptation rate by changing covariance of assumed process noise
-    // acting on random-walk evolution of m
-    Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5;
-    controller_system.set_Q_m(q_m);
-
-    data_t u_lb = 0.0;
-    data_t u_ub = 5.0;
-    controller = std::move(
-        lds::poisson::Controller(std::move(controller_system), u_lb, u_ub));
-  }
-  // set controller type
-  controller.set_control_type(control_type);
-
-  // set controller gains
-  controller.set_Kc(k_x);
-  controller.set_Kc_inty(k_inty);
-
-  // to protect against integral windup when output is consistently above
-  // target:
-  data_t tau_awu(0.1);
-  controller.set_tau_awu(tau_awu);
-
-  cout << ".....................................\n";
-  cout << "controller:\n";
-  cout << ".....................................\n";
-  controller.Print();
-  cout << ".....................................\n";
-
-  // create Matrix to save outputs in...
-  Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros);
-  y_ref.each_col() += y_ref0;
-
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // simulated control signal ([=] V)
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // outputs, states and gain/disturbance params
-  // *_hat indicates online estimates
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_y, n_t, arma::fill::zeros);
-
-  // *_true indicates ground truth (system being controlled)
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_y, n_t, arma::fill::zeros);
-
-  // set initial val
-  y_hat.col(0) = controller.sys().y();
-  y_true.col(0) = controlled_system.y();
-
-  x_hat.col(0) = controller.sys().x();
-  x_true.col(0) = controlled_system.x();
-
-  m_hat.col(0) = controller.sys().m();
-  m_true.col(0) = controlled_system.m();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // simulate a stochastically switched disturbance
-    Vector chance = arma::randu<Vector>(1);
-    if (which_m == 0)  // low disturbance
-    {
-      if (chance[0] < pr_lo2hi) {  // switches low -> high disturbance
-        m0_true = std::vector<data_t>(n_x, m_high);
-        which_m = 1;
-      }
-    } else {                       // high disturbance
-      if (chance[0] < pr_hi2lo) {  // swithces high -> low disturbance
-        m0_true = std::vector<data_t>(n_x, m_low);
-        which_m = 0;
-      }
-    }
-    controlled_system.set_m(m0_true);
-
-    // e.g., use sinusoidal reference
-    data_t f = 0.5;  // freq [=] Hz
-    Vector t_vec = Vector(n_y, arma::fill::ones) * t;
-    y_ref.col(t) +=
-        y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4);
-
-    // Simulate the true system.
-    z.col(t)=controlled_system.Simulate(u.col(t-1));
-
-    // This method uses a steady-state solution to control problem to calculate
-    // x_ref, u_ref from reference output y_ref. Notably, it does this in the
-    // log-linear space (i.e., log(y)).
-    //
-    // Therefore, it is only applicable to regulation problems or cases where
-    // reference trajectory changes slowly compared to system dynamics.
-    controller.set_y_ref(y_ref.col(t));
-    u.col(t)=controller.ControlOutputReference(z.col(t));
-
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    m_true.col(t) = controlled_system.m();
-
-    y_hat.col(t) = controller.sys().y();
-    x_hat.col(t) = controller.sys().x();
-    m_hat.col(t) = controller.sys().m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true,
-  // x_true, m_true saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_plds_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_plds_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_plds_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_hat", replace));
-
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/examples/eg_plds_est_8cpp-example/index.html b/docs/docs/api/examples/eg_plds_est_8cpp-example/index.html deleted file mode 100644 index 16ffe156..00000000 --- a/docs/docs/api/examples/eg_plds_est_8cpp-example/index.html +++ /dev/null @@ -1,421 +0,0 @@ - - - - - - - - - - - - - -eg_plds_est.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - eg_plds_est.cpp - - -
- - - - - - -
- - - -

- eg_plds_est.cpp - # -

-

Example PLDS Estimation

-
//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-
-// for generating random input
-Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0);
-
-int main() {
-  cout << " ********** Example Poisson LDS Estimation ********** \n\n";
-
-  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;                           // no. inputs
-  size_t n_x = 1;                           // no. states
-  size_t n_y = 1;                           // no. outputs
-  auto n_t = static_cast<size_t>(30 / dt);  // no time steps for simulation.
-
-  // construct ground truth system...
-  lds::poisson::System system_true(n_u, n_x, n_y, dt);
-
-  // Model parameters
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.075);
-  Matrix b_true = Matrix(n_x, n_u).fill(1e-2);
-  Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2);  // disturbance
-  Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) -
-                                        a_true);  // initial state
-
-  // Assign params.
-  system_true.set_A(a_true);
-  system_true.set_B(b_true);
-  system_true.set_x0(x0_true);
-  system_true.set_m(m0_true);
-  system_true.Reset();
-
-  // Construct system for estimation
-  // e.g., will create a model with incorrect disturbance
-  lds::poisson::System system_estimator(n_u, n_x, n_y, dt);
-
-  // Can copy parameters from another system object
-  system_estimator = system_true;
-
-  // wrong disturbance
-  Vector m0_est = m0_true * 2;
-  system_estimator.set_m(m0_est);
-
-  // set new initial conditions
-  Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) -
-                                      a_true);  // initial state
-  system_estimator.set_x0(x0_est);
-  system_estimator.Reset();  // reset to initial condition.
-
-  // turn on adaptive disturbance estimation
-  system_estimator.do_adapt_m = true;
-
-  // set adaptation rate by changing covariance of assumed process noise acting
-  // on random-walk evolution of m
-  Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6;
-  system_estimator.set_Q_m(q_m);
-
-  cout << ".....................................\n";
-  cout << "estimator:\n";
-  cout << ".....................................\n";
-  system_estimator.Print();
-  cout << ".....................................\n";
-
-  // Set up simulation :
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // Stimulus (generate random stimulus)
-  Matrix q_u =
-      Matrix(n_u, n_u, arma::fill::eye) * 1e-3;  // cov of random walk
-  Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));
-
-  // create matrix to save outputs in...
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-
-  // states and disturbance params
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_x, n_t, arma::fill::zeros);
-
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_x, n_t, arma::fill::zeros);
-
-  // initial conditions
-  y_hat.col(0) = system_estimator.y();
-  y_true.col(0) = system_true.y();
-  x_hat.col(0) = system_estimator.x();
-  x_true.col(0) = system_true.x();
-  m_hat.col(0) = system_estimator.m();
-  m_true.col(0) = system_true.m();
-
-  cout << "Starting " << n_t * dt << " sec simlation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // Simlate the true system.
-    z.col(t) = system_true.Simulate(u.col(t - 1));
-
-    // Filter (predict -> update)
-    system_estimator.Filter(u.col(t - 1), z.col(t));
-
-    // save signals
-    y_hat.col(t) = system_estimator.y();
-    y_true.col(t) = system_true.y();
-
-    x_true.col(t) = system_true.x();
-    m_true.col(t) = system_true.m();
-
-    x_hat.col(t) = system_estimator.x();
-    m_hat.col(t) = system_estimator.m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simlation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true,
-  // x_true, m_true saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_plds_est.h5", "dt"));
-  u.save(arma::hdf5_name("eg_plds_est.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_plds_est.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_plds_est.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_plds_est.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_plds_est.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_plds_est.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_plds_est.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_plds_est.h5", "y_hat", replace));
-
-  return 0;
-}
-
-// for generating random input
-Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0) {
-  size_t n = Q.n_rows;
-
-  if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) {
-    throw std::logic_error("Q must be `n` x `n`.");
-  }
-
-  Matrix x(n, n_t, arma::fill::zeros);
-  x.col(0) = x0;
-  for (size_t t = 1; t < n_t; t++) {
-    x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q);
-  }
-
-  return x;
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html deleted file mode 100644 index f55a0f2e..00000000 --- a/docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html +++ /dev/null @@ -1,466 +0,0 @@ - - - - - - - - - - - - - -eg_plds_switched_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - eg_plds_switched_ctrl.cpp - - -
- - - - - - -
- - - -

- eg_plds_switched_ctrl.cpp - # -

-

Example Switched PLDS Control

-
//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::data_t;
-using lds::Matrix;
-using lds::Vector;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Switched Poisson LDS Control ********** \n\n";
-
-  // whether to do switched control
-  bool do_switch_ctrl = true;
-
-  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(30.0 / dt);
-
-  // for simulating switching
-  size_t which_mode = 1;
-  data_t pr_21 = 1e-3;   // prob mode 1 -> 2
-  data_t pr_12 = pr_21;  // prob mode 2 -> 1
-
-  // simulated system being controlled
-  lds::poisson::System controlled_system(n_u, n_x, n_y, dt);
-
-  // **Assume the system is not well characterized by one LDS, but is well
-  // characterized by two LDS models with different input matrices.**
-  data_t scale_sys_b = 2;
-
-  Matrix a(n_x, n_x, arma::fill::eye);
-  a[0] = 0.985;
-  Matrix b1 = Matrix(n_x, n_u).fill(0.05);
-  Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));
-
-  controlled_system.set_A(a);
-  controlled_system.set_B(b1);
-  controlled_system.set_d(d);
-  controlled_system.Reset();  // reset to initial conditions
-
-  // reference
-  Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);
-
-  // Let underlying system 1 be more sensitive than system 2
-  Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);
-
-  // create switched controller
-  lds::poisson::SwitchedController switched_controller;
-  lds::UniformMatrixList<> k_x;  // feedback controller gains
-  {
-    // create switched controller sub-systems
-    // system 1
-    lds::poisson::System sys1(controlled_system);
-
-    // set process noise covariance
-    Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3;
-    sys1.set_Q(q_controller);
-
-    // adaptively estimate process disturbance (m)
-    // n.b. using arbitrary default value for process noise if enabled.
-    sys1.do_adapt_m = true;
-
-    // setting initial mode to target to avoid large error at onset:
-    Vector x0_controller = arma::log(y_ref0) - d;
-    sys1.set_x0(x0_controller);
-    sys1.Reset();  // reset to initial conditions
-
-    cout << ".....................................\n";
-    cout << "sys1:\n";
-    cout << ".....................................\n";
-    sys1.Print();
-    cout << ".....................................\n";
-
-    // system 2
-    lds::poisson::System sys2 = sys1;
-
-    // set parameters
-    sys2.set_B(b2);
-
-    cout << ".....................................\n";
-    cout << "sys2:\n";
-    cout << ".....................................\n";
-    sys2.Print();
-    cout << ".....................................\n";
-
-    lds::UniformSystemList<lds::poisson::System> systems({sys1, sys2});
-
-    // controller gains for underlying system s:
-    Matrix k_x1(n_u, n_x, arma::fill::ones);
-    Matrix k_x2 = scale_sys_b * k_x1;  // system2 is x-times less sensitive.
-    k_x = lds::UniformMatrixList<>({k_x1, k_x2});
-
-    data_t u_lb = 0.0;
-    data_t u_ub = 5.0;
-    switched_controller = std::move(
-        lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub));
-  }
-  // Control variables
-  size_t control_type = 0;  // no integral action, etc
-  switched_controller.set_control_type(control_type);
-  switched_controller.set_Kc(std::move(k_x));
-
-  switched_controller.set_y_ref(y_ref0);
-
-  std::vector<lds::poisson::System> systems_vec(3, lds::poisson::System());
-  lds::UniformSystemList<lds::poisson::System> systems(std::move(systems_vec));
-
-  cout << ".....................................\n";
-  cout << "switched_controller:\n";
-  cout << ".....................................\n";
-  switched_controller.Print();
-  cout << ".....................................\n";
-
-  // Fake measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // Will later contain control.
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // create Matrix to save outputs in...
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);
-
-  // modes and gain/disturbance params
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix mode(1, n_t, arma::fill::ones);
-
-  // set initial val
-  y_hat.col(0) = switched_controller.sys().y();
-  y_true.col(0) = controlled_system.y();
-  x_hat.col(0) = switched_controller.sys().x();
-  x_true.col(0) = controlled_system.x();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // Let the controlled system stochastically change gain
-    // Assume another algorithm decodes this mode change and signals the
-    // switched_controller
-    Vector chance(1, arma::fill::randu);
-    if (which_mode == 1)  // mode1
-    {
-      if (chance[0] < pr_21) {
-        which_mode = 2;
-        controlled_system.set_B(b2);
-        if (do_switch_ctrl) {
-          switched_controller.Switch(1);
-        }
-      }
-    } else {  // mode2
-      if (chance[0] < pr_12) {
-        which_mode = 1;
-        controlled_system.set_B(b1);
-        if (do_switch_ctrl) {
-          switched_controller.Switch(0);
-        }
-      }
-    }
-
-    // Simulate the true system.
-    z.col(t) = controlled_system.Simulate(u.col(t - 1));
-
-    // perform control
-    u.col(t) = switched_controller.ControlOutputReference(z.col(t));
-
-    mode.col(t) = which_mode;
-    y_ref.col(t) = y_ref0;
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    y_hat.col(t) = switched_controller.sys().y();
-    x_hat.col(t) = switched_controller.sys().x();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true,
-  // x_true, m_true saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_true", replace));
-  y_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_hat", replace));
-  mode.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "mode", replace));
-
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/examples/index.html b/docs/docs/api/examples/index.html deleted file mode 100644 index fe84e8d4..00000000 --- a/docs/docs/api/examples/index.html +++ /dev/null @@ -1,263 +0,0 @@ - - - - - - - - - - - -Examples | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - Examples - - -
- - - - - - -
- - - - - - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/examples/index.xml b/docs/docs/api/examples/index.xml deleted file mode 100644 index 972a6789..00000000 --- a/docs/docs/api/examples/index.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - Examples on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/ - Recent content in Examples on LDS C&E - Hugo -- gohugo.io - - eg_glds_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/ - eg_glds_ctrl.cpp # Example GLDS Control -//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &#34;AS IS&#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - - - - eg_glds_du_plds_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/ - eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). -//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www. - - - - eg_plds_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/ - eg_plds_ctrl.cpp # Example PLDS Control -//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &#34;AS IS&#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - - - - eg_plds_est.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/ - eg_plds_est.cpp # Example PLDS Estimation -//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &#34;AS IS&#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - - - - eg_plds_switched_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/ - eg_plds_switched_ctrl.cpp # Example Switched PLDS Control -//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &#34;AS IS&#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - - - - diff --git a/docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html b/docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html deleted file mode 100644 index 460ab2b6..00000000 --- a/docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html +++ /dev/null @@ -1,341 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h - # -

-

- Files - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
ldsCtrlEst_h/lds.h
lds namespace
ldsCtrlEst_h/lds_ctrl.h
Controller.
ldsCtrlEst_h/lds_fit.h
LDS base fit type.
ldsCtrlEst_h/lds_fit_em.h
subspace identification
ldsCtrlEst_h/lds_fit_ssid.h
subspace identification
ldsCtrlEst_h/lds_gaussian.h
glds namespace
ldsCtrlEst_h/lds_gaussian_ctrl.h
GLDS Controller.
ldsCtrlEst_h/lds_gaussian_fit.h
GLDS fit type.
ldsCtrlEst_h/lds_gaussian_fit_em.h
GLDS E-M fit type.
ldsCtrlEst_h/lds_gaussian_fit_ssid.h
GLDS SSID fit type.
ldsCtrlEst_h/lds_gaussian_sctrl.h
GLDS switched controller type.
ldsCtrlEst_h/lds_gaussian_sys.h
GLDS base type.
ldsCtrlEst_h/lds_poisson.h
plds namespace
ldsCtrlEst_h/lds_poisson_ctrl.h
PLDS controller type.
ldsCtrlEst_h/lds_poisson_fit.h
PLDS base fit type.
ldsCtrlEst_h/lds_poisson_fit_em.h
PLDS E-M fit type.
ldsCtrlEst_h/lds_poisson_fit_ssid.h
PLDS SSID fit type.
ldsCtrlEst_h/lds_poisson_sctrl.h
PLDS switched controller type.
ldsCtrlEst_h/lds_poisson_sys.h
PLDS base type.
ldsCtrlEst_h/lds_sctrl.h
SwitchedController type.
ldsCtrlEst_h/lds_sys.h
LDS base type.
ldsCtrlEst_h/lds_uniform_mats.h
List of uniformly sized matrices.
ldsCtrlEst_h/lds_uniform_systems.h
List of uniformly sized Systems.
ldsCtrlEst_h/lds_uniform_vecs.h
List of uniformly sized vectors.
ldsCtrlEst_h/mex_c_util.h
arma <-> mex interoperability utilities (Matlab C API)
ldsCtrlEst_h/mex_cpp_util.h
arma <-> mex interoperability utilities (Matlab C++ API)
-
-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html b/docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html deleted file mode 100644 index 66c5e29f..00000000 --- a/docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html +++ /dev/null @@ -1,278 +0,0 @@ - - - - - - - - - - - - - -src | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - src - - -
- - - - - - -
- - - - - - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html b/docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html deleted file mode 100644 index 57978f9e..00000000 --- a/docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html +++ /dev/null @@ -1,278 +0,0 @@ - - - - - - - - - - - - - -examples | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - examples - - -
- - - - - - -
- - - - - - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html b/docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html deleted file mode 100644 index d7546f23..00000000 --- a/docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html +++ /dev/null @@ -1,266 +0,0 @@ - - - - - - - - - - - - - -include | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - include - - -
- - - - - - -
- - - -

- include - # -

-

- Directories - # -

- - - - - - - - - - - -
Name
ldsCtrlEst_h
-
-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/eg__glds__ctrl_8cpp/index.html b/docs/docs/api/files/eg__glds__ctrl_8cpp/index.html deleted file mode 100644 index 12f97b0e..00000000 --- a/docs/docs/api/files/eg__glds__ctrl_8cpp/index.html +++ /dev/null @@ -1,552 +0,0 @@ - - - - - - - - - - - - - -examples/eg_glds_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - examples/eg_glds_ctrl.cpp - - -
- - - - - - -
- - - -

- examples/eg_glds_ctrl.cpp - # -

-

- Functions - # -

- - - - - - - - - - - - - -
Name
automain()
-

- Function Details - # -

-

- main - # -

-
auto main()
-

Going to simulate a switching disturbance (m) acting on system

-

- Source code - # -

-
//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Gaussian LDS Control ********** \n\n";
-
-  // Make 1st-order SISO system, sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(5.0 / dt);
-
-  // construct ground truth system to be controlled...
-  // initializes to random walk model with top-most n_y state observed
-  lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);
-
-  // Ground-truth parameters for the controlled system
-  // (stand-in for physical system to be controlled)
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.01);
-  Matrix b_true = Matrix(n_x, n_u).fill(2e-4);
-  // control signal to model input unit conversion e.g., V -> mW/mm2:
-  Vector g_true = Vector(n_y).fill(10.0);
-
-  // output noise covariance
-  Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;
-
-  size_t which_m = 0;  // whether low or high disturbance (0, 1)
-  data_t m_low = 5 * dt * (1 - a_true[0]);
-  data_t pr_lo2hi = 1e-3;  // probability of going from low to high disturb.
-  data_t m_high = 20 * dt * (1 - a_true[0]);
-  data_t pr_hi2lo = pr_lo2hi;
-
-  // initially let m be low
-  Vector m0_true = Vector(n_x).fill(m_low);
-
-  // Assign params.
-  controlled_system.set_A(a_true);
-  controlled_system.set_B(b_true);
-  controlled_system.set_m(m0_true);
-  controlled_system.set_g(g_true);
-  controlled_system.set_R(r_true);
-
-  cout << ".....................................\n";
-  cout << "controlled_system:\n";
-  cout << ".....................................\n";
-  controlled_system.Print();
-  cout << ".....................................\n";
-
-  // make a controller
-  lds::gaussian::Controller controller;
-  {
-    // Create **incorrect** model used for control.
-    // (e.g., imperfect model fitting)
-    Matrix b_controller = b_true / 2;
-
-    // let's assume zero process disturbance initially
-    // (will be re-estimating)
-    Vector m_controller = Vector(n_x, arma::fill::zeros);
-
-    // for this demo, just use arbitrary default R
-    Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0;
-
-    lds::gaussian::System controller_system(controlled_system);
-    controller_system.set_B(b_controller);
-    controller_system.set_m(m_controller);
-    controller_system.set_R(r_controller);
-    controller_system.Reset();  // reset to new m
-
-    // going to adaptively re-estimate the disturbance
-    controller_system.do_adapt_m = true;
-
-    // set adaptation rate by changing covariance of assumed process noise
-    // acting on random-walk evolution of m
-    Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6;
-    controller_system.set_Q_m(q_m);
-
-    // create controller
-    // lower and upper bounds on control signal (e.g., in Volts)
-    data_t u_lb = 0.0;  // [=] V
-    data_t u_ub = 5.0;  // [=] V
-    controller = std::move(
-        lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub));
-  }
-
-  // Control variables:
-  // if following enabled, adapts set point with re-estimated process
-  // disturbance n.b., should not need integral action if this is enabled as the
-  // adaptive estimator minimizes DC error
-  bool do_adaptive_set_point = false;
-
-  // Reference/target output, controller gains
-  Vector y_ref0 = Vector(n_y).fill(20.0 * dt);
-  Matrix k_x = Matrix(n_u, n_x).fill(100);     // gains on state error
-  Matrix k_inty = Matrix(n_u, n_y).fill(1e3);  // gains on integrated err
-
-  // setting initial state to target to avoid error at onset:
-  Vector x0 = Vector(n_x).fill(y_ref0[0]);
-
-  // set up controller type bit mask so controller knows how to proceed
-  size_t control_type = 0;
-  if (do_adaptive_set_point) {
-    // adapt set point with estimated disturbance
-    control_type = control_type | lds::kControlTypeAdaptM;
-  } else {
-    // use integral action to minimize DC error
-    control_type = control_type | lds::kControlTypeIntY;
-  }
-
-  // set controller type
-  controller.set_control_type(control_type);
-
-  // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2):
-  Vector g_design = Vector(n_u).fill(9);
-
-  // Set params.
-  // **n.b. using arbitrary defaults for Q, R in this example. Really, these
-  // should be set by users, as they tune characteristics of Kalman filter.
-  // Users can also choose not to recursively calculate the estimator gain and
-  // supply it (setKe) instead of covariances.**
-  controller.set_y_ref(y_ref0);
-  controller.set_Kc(k_x);
-  controller.set_Kc_inty(k_inty);
-  controller.set_g_design(g_design);
-
-  cout << ".....................................\n";
-  cout << "control system:\n";
-  cout << ".....................................\n";
-  controller.Print();
-  cout << ".....................................\n";
-
-  // set up variables for simulation
-  // create Matrix to save outputs in...
-  Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];
-
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // simulated control signal ([=] V)
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // outputs, states and gain/disturbance params
-  // *_hat indicates online estimates
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_x, n_t, arma::fill::zeros);
-
-  // *_true indicates ground truth (system being controlled)
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_x, n_t, arma::fill::zeros);
-
-  // set initial val
-  y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y();
-  y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();
-
-  x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x();
-  x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();
-
-  m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m();
-  m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // simulate a stochastically switched disturbance
-    Vector chance = arma::randu<Vector>(1);
-    if (which_m == 0)  // low disturbance
-    {
-      if (chance[0] < pr_lo2hi) {  // switches low -> high disturbance
-        m0_true = std::vector<data_t>(n_x, m_high);
-        which_m = 1;
-      }
-    } else {                       // high disturbance
-      if (chance[0] < pr_hi2lo) {  // swithces high -> low disturbance
-        m0_true = std::vector<data_t>(n_x, m_low);
-        which_m = 0;
-      }
-    }
-    controlled_system.set_m(m0_true);
-
-    // input
-    Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true);
-
-    // Simulate the true system.
-    z.col(t) = controlled_system.Simulate(u_tm1);
-
-    // This method uses a steady-state solution to control problem to calculate
-    // x_ref, u_ref from reference output y_ref. Therefore, it is only
-    // applicable to regulation problems or cases where reference trajectory
-    // changes slowly compared to system dynamics.
-    u.col(t) = controller.ControlOutputReference(z.col(t));
-
-    // save the signals
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    m_true.col(t) = controlled_system.m();
-
-    y_hat.col(t) = controller.sys().y();
-    x_hat.col(t) = controller.sys().x();
-    m_hat.col(t) = controller.sys().m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  cout << "Saving simulation data to disk.\n";
-
-  // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue,
-  // xTrue, mTrue saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_glds_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_glds_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_glds_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_hat", replace));
-
-  cout << "fin.\n";
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html b/docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html deleted file mode 100644 index 86d81638..00000000 --- a/docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html +++ /dev/null @@ -1,553 +0,0 @@ - - - - - - - - - - - - - -examples/eg_glds_du_plds_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - examples/eg_glds_du_plds_ctrl.cpp - - -
- - - - - - -
- - - -

- examples/eg_glds_du_plds_ctrl.cpp - # -

-

- Functions - # -

- - - - - - - - - - - - - -
Name
automain()
-

- Function Details - # -

-

- main - # -

-
auto main()
-

Going to simulate a switching disturbance (m) acting on system

-

- Source code - # -

-
//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::data_t;
-using lds::Matrix;
-using lds::Vector;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Gaussian LDS du Control of PLDS ********** \n\n";
-
-  // Make 1st-order SISO system, sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(5.0 / dt);
-
-  // construct ground truth system to be controlled...
-  // initializes to random walk model with top-most n_y state observed
-  lds::poisson::System controlled_system(n_u, n_x, n_y, dt);
-
-  // Ground-truth parameters for the controlled system
-  // (stand-in for physical system to be controlled)
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.01);
-  Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2);
-  // control signal to model input unit conversion e.g., V -> mW/mm2:
-  Vector g_true = Vector(n_y).fill(10.0);
-
-  size_t which_m = 0;  // whether low or high disturbance (0, 1)
-  data_t m_low = log(1 * dt) * (1 - a_true[0]);
-  data_t pr_lo2hi =
-      0;  // 1e-3;  // probability of going from low to high disturb.
-  data_t m_high = log(20 * dt) * (1 - a_true[0]);
-  data_t pr_hi2lo = pr_lo2hi;
-
-  // initially let m be low
-  Vector m0_true = Vector(n_x).fill(m_low);
-  Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);
-
-  // Assign params.
-  controlled_system.set_A(a_true);
-  controlled_system.set_B(b_true);
-  controlled_system.set_m(m0_true);
-  controlled_system.set_g(g_true);
-  controlled_system.set_x0(x0_true);
-  controlled_system.Reset();
-
-  cout << ".....................................\n";
-  cout << "controlled_system:\n";
-  cout << ".....................................\n";
-  controlled_system.Print();
-  cout << ".....................................\n";
-
-  // make a controller
-  lds::gaussian::Controller controller;
-  {
-    // Create **incorrect** model used for control.
-    // (e.g., imperfect model fitting)
-    Matrix b_controller = b_true / 50;
-
-    // let's assume zero process disturbance initially
-    // (will be re-estimating)
-    Vector m_controller = Vector(n_x, arma::fill::zeros);
-
-    // process noise covariance
-    Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8;
-
-    // output noise covariance
-    Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2;
-
-    lds::gaussian::System controller_system(n_u, n_x, n_y, dt);
-    controller_system.set_A(a_true);
-    controller_system.set_B(b_controller);
-    controller_system.set_g(g_true);
-    controller_system.set_m(m_controller);
-    controller_system.set_Q(q_controller);
-    controller_system.set_R(r_controller);
-    controller_system.Reset();  // reset to new m
-
-    // going to adaptively re-estimate the disturbance
-    controller_system.do_adapt_m = true;
-
-    // set adaptation rate by changing covariance of assumed process noise
-    // acting on random-walk evolution of m
-    Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8;
-    controller_system.set_Q_m(q_m);
-
-    // create controller
-    // lower and upper bounds on control signal (e.g., in Volts)
-    data_t u_lb = 0.0;  // [=] V
-    data_t u_ub = 5.0;  // [=] V
-    controller = std::move(
-        lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub));
-  }
-
-  // Control variables:
-  // Reference/target output, controller gains
-  Vector y_ref0 = Vector(n_y).fill(20.0 * dt);
-
-  // to design for this example, augmented state with control and made the input
-  // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du =
-  // 1e-2.
-  Matrix k_x = Matrix(n_u, n_x).fill(2.44);     // gains on state error
-  Matrix k_inty = Matrix(n_u, n_y).fill(97.4);  // gains on integrated err
-  Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2);  // gains on input amp
-
-  // set up controller type bit mask so controller knows how to proceed
-  size_t control_type = 0;
-  // use integral action to minimize DC error
-  control_type = control_type | lds::kControlTypeIntY;
-  // update change in control (LP filters control)
-  control_type = control_type | lds::kControlTypeDeltaU;
-
-  // set controller type
-  controller.set_control_type(control_type);
-
-  // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2):
-  Vector g_design = Vector(n_u).fill(10);
-
-  // Set params.
-  // **n.b. using arbitrary defaults for Q, R in this example. Really, these
-  // should be set by users, as they tune characteristics of Kalman filter.
-  // Users can also choose not to recursively calculate the estimator gain and
-  // supply it (setKe) instead of covariances.**
-  controller.set_y_ref(y_ref0);
-  controller.set_Kc(k_x);
-  controller.set_Kc_inty(k_inty);
-  controller.set_Kc_u(k_u);
-  controller.set_g_design(g_design);
-
-  cout << ".....................................\n";
-  cout << "control system:\n";
-  cout << ".....................................\n";
-  controller.Print();
-  cout << ".....................................\n";
-
-  // set up variables for simulation
-  // create Matrix to save outputs in...
-  Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];
-
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // simulated control signal ([=] V)
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // outputs, states and gain/disturbance params
-  // *_hat indicates online estimates
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_x, n_t, arma::fill::zeros);
-
-  // *_true indicates ground truth (system being controlled)
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_x, n_t, arma::fill::zeros);
-
-  // get initial val
-  y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y();
-  y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();
-
-  x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x();
-  x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();
-
-  m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m();
-  m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // simulate a stochastically switched disturbance
-    Vector chance = arma::randu<Vector>(1);
-    if (which_m == 0)  // low disturbance
-    {
-      if (chance[0] < pr_lo2hi) {  // switches low -> high disturbance
-        m0_true = std::vector<data_t>(n_x, m_high);
-        which_m = 1;
-      }
-    } else {                       // high disturbance
-      if (chance[0] < pr_hi2lo) {  // swithces high -> low disturbance
-        m0_true = std::vector<data_t>(n_x, m_low);
-        which_m = 0;
-      }
-    }
-    controlled_system.set_m(m0_true);
-
-    // input
-    Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true);
-
-    // Simulate the true system.
-    z.col(t) = controlled_system.Simulate(u_tm1);
-
-    // This method uses a steady-state solution to control problem to calculate
-    // x_ref, u_ref from reference output y_ref. Therefore, it is only
-    // applicable to regulation problems or cases where reference trajectory
-    // changes slowly compared to system dynamics.
-    u.col(t) = controller.ControlOutputReference(z.col(t));
-
-    // save the signals
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    m_true.col(t) = controlled_system.m();
-
-    y_hat.col(t) = controller.sys().y();
-    x_hat.col(t) = controller.sys().x();
-    m_hat.col(t) = controller.sys().m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  cout << "Saving simulation data to disk.\n";
-
-  // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue,
-  // xTrue, mTrue saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_hat", replace));
-
-  cout << "fin.\n";
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/eg__plds__ctrl_8cpp/index.html b/docs/docs/api/files/eg__plds__ctrl_8cpp/index.html deleted file mode 100644 index 384c0f5a..00000000 --- a/docs/docs/api/files/eg__plds__ctrl_8cpp/index.html +++ /dev/null @@ -1,522 +0,0 @@ - - - - - - - - - - - - - -examples/eg_plds_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - examples/eg_plds_ctrl.cpp - - -
- - - - - - -
- - - -

- examples/eg_plds_ctrl.cpp - # -

-

- Functions - # -

- - - - - - - - - - - - - -
Name
automain()
-

- Function Details - # -

-

- main - # -

-
auto main()
-

Going to simulate a switching disturbance (m) acting on system

-

- Source code - # -

-
//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Poisson LDS Control ********** \n\n";
-
-  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(10.0 / dt);
-
-  // Control variables: _reference/target output, controller gains
-  // n.b., Can either use Vector (arma::Col) or std::vector
-  Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt;
-  Matrix k_x =
-      Matrix(n_u, n_x, arma::fill::zeros) + 1;  // gains on state error
-  Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) +
-                   10;  // gains on integrated output err
-
-  // Set control type bit mask, so controller knows what to do
-  size_t control_type = lds::kControlTypeIntY;  // integral action
-
-  // Ground-truth parameters for the controlled system
-  // (stand-in for physical system to be controlled)
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = 0.986;
-  Matrix b_true(n_x, n_u, arma::fill::zeros);
-  b_true[0] = 0.054;
-  Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);
-
-  size_t which_m = 0;
-  data_t m_low = log(1 * dt) * (1 - a_true[0]);
-  data_t pr_lo2hi = 1e-3;
-  data_t m_high = log(20 * dt) * (1 - a_true[0]);
-  data_t pr_hi2lo = pr_lo2hi;
-
-  Vector m0_true = Vector(n_x, arma::fill::ones) * m_low;
-  // construct ground truth system to be controlled...
-  lds::poisson::System controlled_system(n_u, n_x, n_y, dt);
-
-  // Assign params.
-  controlled_system.set_A(a_true);
-  controlled_system.set_B(b_true);
-  controlled_system.set_m(m0_true);
-  controlled_system.set_x0(x0_true);
-  // reset to initial conditions
-  controlled_system.Reset();
-
-  cout << ".....................................\n";
-  cout << "controlled_system:\n";
-  cout << ".....................................\n";
-  controlled_system.Print();
-  cout << ".....................................\n";
-
-  // Create the controller
-  lds::poisson::Controller controller;
-  {
-    // Create model used for control.
-    lds::poisson::System controller_system(controlled_system);
-
-    // for this example, assume model correct, except disturbance
-    Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low;
-    Vector x0_controller = arma::log(y_ref0);
-    controller_system.set_m(m0_controller);
-    controller_system.set_x0(x0_controller);
-    controller_system.Reset(); //reset to new init condition
-
-    // adaptively re-estimate process disturbance (m)
-    controller_system.do_adapt_m = true;
-
-    // set adaptation rate by changing covariance of assumed process noise
-    // acting on random-walk evolution of m
-    Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5;
-    controller_system.set_Q_m(q_m);
-
-    data_t u_lb = 0.0;
-    data_t u_ub = 5.0;
-    controller = std::move(
-        lds::poisson::Controller(std::move(controller_system), u_lb, u_ub));
-  }
-  // set controller type
-  controller.set_control_type(control_type);
-
-  // set controller gains
-  controller.set_Kc(k_x);
-  controller.set_Kc_inty(k_inty);
-
-  // to protect against integral windup when output is consistently above
-  // target:
-  data_t tau_awu(0.1);
-  controller.set_tau_awu(tau_awu);
-
-  cout << ".....................................\n";
-  cout << "controller:\n";
-  cout << ".....................................\n";
-  controller.Print();
-  cout << ".....................................\n";
-
-  // create Matrix to save outputs in...
-  Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros);
-  y_ref.each_col() += y_ref0;
-
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // simulated control signal ([=] V)
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // outputs, states and gain/disturbance params
-  // *_hat indicates online estimates
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_y, n_t, arma::fill::zeros);
-
-  // *_true indicates ground truth (system being controlled)
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_y, n_t, arma::fill::zeros);
-
-  // set initial val
-  y_hat.col(0) = controller.sys().y();
-  y_true.col(0) = controlled_system.y();
-
-  x_hat.col(0) = controller.sys().x();
-  x_true.col(0) = controlled_system.x();
-
-  m_hat.col(0) = controller.sys().m();
-  m_true.col(0) = controlled_system.m();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // simulate a stochastically switched disturbance
-    Vector chance = arma::randu<Vector>(1);
-    if (which_m == 0)  // low disturbance
-    {
-      if (chance[0] < pr_lo2hi) {  // switches low -> high disturbance
-        m0_true = std::vector<data_t>(n_x, m_high);
-        which_m = 1;
-      }
-    } else {                       // high disturbance
-      if (chance[0] < pr_hi2lo) {  // swithces high -> low disturbance
-        m0_true = std::vector<data_t>(n_x, m_low);
-        which_m = 0;
-      }
-    }
-    controlled_system.set_m(m0_true);
-
-    // e.g., use sinusoidal reference
-    data_t f = 0.5;  // freq [=] Hz
-    Vector t_vec = Vector(n_y, arma::fill::ones) * t;
-    y_ref.col(t) +=
-        y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4);
-
-    // Simulate the true system.
-    z.col(t)=controlled_system.Simulate(u.col(t-1));
-
-    // This method uses a steady-state solution to control problem to calculate
-    // x_ref, u_ref from reference output y_ref. Notably, it does this in the
-    // log-linear space (i.e., log(y)).
-    //
-    // Therefore, it is only applicable to regulation problems or cases where
-    // reference trajectory changes slowly compared to system dynamics.
-    controller.set_y_ref(y_ref.col(t));
-    u.col(t)=controller.ControlOutputReference(z.col(t));
-
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    m_true.col(t) = controlled_system.m();
-
-    y_hat.col(t) = controller.sys().y();
-    x_hat.col(t) = controller.sys().x();
-    m_hat.col(t) = controller.sys().m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true,
-  // x_true, m_true saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_plds_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_plds_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_plds_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_hat", replace));
-
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/eg__plds__est_8cpp/index.html b/docs/docs/api/files/eg__plds__est_8cpp/index.html deleted file mode 100644 index cb4b0a49..00000000 --- a/docs/docs/api/files/eg__plds__est_8cpp/index.html +++ /dev/null @@ -1,484 +0,0 @@ - - - - - - - - - - - - - -examples/eg_plds_est.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - examples/eg_plds_est.cpp - - -
- - - - - - -
- - - -

- examples/eg_plds_est.cpp - # -

-

- Functions - # -

- - - - - - - - - - - - - - - - - -
Name
Matrixrandom_walk(size_t n_t, const Matrix & Q, const Vector & x0)
intmain()
-

- Function Details - # -

-

- random_walk - # -

-
Matrix random_walk(
-    size_t n_t,
-    const Matrix & Q,
-    const Vector & x0
-)
-

- main - # -

-
int main()
-

- Source code - # -

-
//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-
-// for generating random input
-Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0);
-
-int main() {
-  cout << " ********** Example Poisson LDS Estimation ********** \n\n";
-
-  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;                           // no. inputs
-  size_t n_x = 1;                           // no. states
-  size_t n_y = 1;                           // no. outputs
-  auto n_t = static_cast<size_t>(30 / dt);  // no time steps for simulation.
-
-  // construct ground truth system...
-  lds::poisson::System system_true(n_u, n_x, n_y, dt);
-
-  // Model parameters
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.075);
-  Matrix b_true = Matrix(n_x, n_u).fill(1e-2);
-  Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2);  // disturbance
-  Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) -
-                                        a_true);  // initial state
-
-  // Assign params.
-  system_true.set_A(a_true);
-  system_true.set_B(b_true);
-  system_true.set_x0(x0_true);
-  system_true.set_m(m0_true);
-  system_true.Reset();
-
-  // Construct system for estimation
-  // e.g., will create a model with incorrect disturbance
-  lds::poisson::System system_estimator(n_u, n_x, n_y, dt);
-
-  // Can copy parameters from another system object
-  system_estimator = system_true;
-
-  // wrong disturbance
-  Vector m0_est = m0_true * 2;
-  system_estimator.set_m(m0_est);
-
-  // set new initial conditions
-  Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) -
-                                      a_true);  // initial state
-  system_estimator.set_x0(x0_est);
-  system_estimator.Reset();  // reset to initial condition.
-
-  // turn on adaptive disturbance estimation
-  system_estimator.do_adapt_m = true;
-
-  // set adaptation rate by changing covariance of assumed process noise acting
-  // on random-walk evolution of m
-  Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6;
-  system_estimator.set_Q_m(q_m);
-
-  cout << ".....................................\n";
-  cout << "estimator:\n";
-  cout << ".....................................\n";
-  system_estimator.Print();
-  cout << ".....................................\n";
-
-  // Set up simulation :
-  // Simulated measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // Stimulus (generate random stimulus)
-  Matrix q_u =
-      Matrix(n_u, n_u, arma::fill::eye) * 1e-3;  // cov of random walk
-  Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));
-
-  // create matrix to save outputs in...
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-
-  // states and disturbance params
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix m_hat(n_x, n_t, arma::fill::zeros);
-
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix m_true(n_x, n_t, arma::fill::zeros);
-
-  // initial conditions
-  y_hat.col(0) = system_estimator.y();
-  y_true.col(0) = system_true.y();
-  x_hat.col(0) = system_estimator.x();
-  x_true.col(0) = system_true.x();
-  m_hat.col(0) = system_estimator.m();
-  m_true.col(0) = system_true.m();
-
-  cout << "Starting " << n_t * dt << " sec simlation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // Simlate the true system.
-    z.col(t) = system_true.Simulate(u.col(t - 1));
-
-    // Filter (predict -> update)
-    system_estimator.Filter(u.col(t - 1), z.col(t));
-
-    // save signals
-    y_hat.col(t) = system_estimator.y();
-    y_true.col(t) = system_true.y();
-
-    x_true.col(t) = system_true.x();
-    m_true.col(t) = system_true.m();
-
-    x_hat.col(t) = system_estimator.x();
-    m_hat.col(t) = system_estimator.m();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simlation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true,
-  // x_true, m_true saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_plds_est.h5", "dt"));
-  u.save(arma::hdf5_name("eg_plds_est.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_plds_est.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_plds_est.h5", "x_true", replace));
-  m_true.save(arma::hdf5_name("eg_plds_est.h5", "m_true", replace));
-  y_true.save(arma::hdf5_name("eg_plds_est.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_plds_est.h5", "x_hat", replace));
-  m_hat.save(arma::hdf5_name("eg_plds_est.h5", "m_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_plds_est.h5", "y_hat", replace));
-
-  return 0;
-}
-
-// for generating random input
-Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0) {
-  size_t n = Q.n_rows;
-
-  if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) {
-    throw std::logic_error("Q must be `n` x `n`.");
-  }
-
-  Matrix x(n, n_t, arma::fill::zeros);
-  x.col(0) = x0;
-  for (size_t t = 1; t < n_t; t++) {
-    x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q);
-  }
-
-  return x;
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html b/docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html deleted file mode 100644 index 50d0a33c..00000000 --- a/docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html +++ /dev/null @@ -1,514 +0,0 @@ - - - - - - - - - - - - - -examples/eg_plds_switched_ctrl.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - examples/eg_plds_switched_ctrl.cpp - - -
- - - - - - -
- - - -

- examples/eg_plds_switched_ctrl.cpp - # -

-

- Functions - # -

- - - - - - - - - - - - - -
Name
automain()
-

- Function Details - # -

-

- main - # -

-
auto main()
-

- Source code - # -

-
//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst>
-
-using lds::data_t;
-using lds::Matrix;
-using lds::Vector;
-using std::cout;
-
-auto main() -> int {
-  cout << " ********** Example Switched Poisson LDS Control ********** \n\n";
-
-  // whether to do switched control
-  bool do_switch_ctrl = true;
-
-  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(30.0 / dt);
-
-  // for simulating switching
-  size_t which_mode = 1;
-  data_t pr_21 = 1e-3;   // prob mode 1 -> 2
-  data_t pr_12 = pr_21;  // prob mode 2 -> 1
-
-  // simulated system being controlled
-  lds::poisson::System controlled_system(n_u, n_x, n_y, dt);
-
-  // **Assume the system is not well characterized by one LDS, but is well
-  // characterized by two LDS models with different input matrices.**
-  data_t scale_sys_b = 2;
-
-  Matrix a(n_x, n_x, arma::fill::eye);
-  a[0] = 0.985;
-  Matrix b1 = Matrix(n_x, n_u).fill(0.05);
-  Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));
-
-  controlled_system.set_A(a);
-  controlled_system.set_B(b1);
-  controlled_system.set_d(d);
-  controlled_system.Reset();  // reset to initial conditions
-
-  // reference
-  Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);
-
-  // Let underlying system 1 be more sensitive than system 2
-  Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);
-
-  // create switched controller
-  lds::poisson::SwitchedController switched_controller;
-  lds::UniformMatrixList<> k_x;  // feedback controller gains
-  {
-    // create switched controller sub-systems
-    // system 1
-    lds::poisson::System sys1(controlled_system);
-
-    // set process noise covariance
-    Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3;
-    sys1.set_Q(q_controller);
-
-    // adaptively estimate process disturbance (m)
-    // n.b. using arbitrary default value for process noise if enabled.
-    sys1.do_adapt_m = true;
-
-    // setting initial mode to target to avoid large error at onset:
-    Vector x0_controller = arma::log(y_ref0) - d;
-    sys1.set_x0(x0_controller);
-    sys1.Reset();  // reset to initial conditions
-
-    cout << ".....................................\n";
-    cout << "sys1:\n";
-    cout << ".....................................\n";
-    sys1.Print();
-    cout << ".....................................\n";
-
-    // system 2
-    lds::poisson::System sys2 = sys1;
-
-    // set parameters
-    sys2.set_B(b2);
-
-    cout << ".....................................\n";
-    cout << "sys2:\n";
-    cout << ".....................................\n";
-    sys2.Print();
-    cout << ".....................................\n";
-
-    lds::UniformSystemList<lds::poisson::System> systems({sys1, sys2});
-
-    // controller gains for underlying system s:
-    Matrix k_x1(n_u, n_x, arma::fill::ones);
-    Matrix k_x2 = scale_sys_b * k_x1;  // system2 is x-times less sensitive.
-    k_x = lds::UniformMatrixList<>({k_x1, k_x2});
-
-    data_t u_lb = 0.0;
-    data_t u_ub = 5.0;
-    switched_controller = std::move(
-        lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub));
-  }
-  // Control variables
-  size_t control_type = 0;  // no integral action, etc
-  switched_controller.set_control_type(control_type);
-  switched_controller.set_Kc(std::move(k_x));
-
-  switched_controller.set_y_ref(y_ref0);
-
-  std::vector<lds::poisson::System> systems_vec(3, lds::poisson::System());
-  lds::UniformSystemList<lds::poisson::System> systems(std::move(systems_vec));
-
-  cout << ".....................................\n";
-  cout << "switched_controller:\n";
-  cout << ".....................................\n";
-  switched_controller.Print();
-  cout << ".....................................\n";
-
-  // Fake measurements
-  Matrix z(n_y, n_t, arma::fill::zeros);
-
-  // Will later contain control.
-  Matrix u(n_u, n_t, arma::fill::zeros);
-
-  // create Matrix to save outputs in...
-  Matrix y_hat(n_y, n_t, arma::fill::zeros);
-  Matrix y_true(n_y, n_t, arma::fill::zeros);
-  Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);
-
-  // modes and gain/disturbance params
-  Matrix x_hat(n_x, n_t, arma::fill::zeros);
-  Matrix x_true(n_x, n_t, arma::fill::zeros);
-  Matrix mode(1, n_t, arma::fill::ones);
-
-  // set initial val
-  y_hat.col(0) = switched_controller.sys().y();
-  y_true.col(0) = controlled_system.y();
-  x_hat.col(0) = switched_controller.sys().x();
-  x_true.col(0) = controlled_system.x();
-
-  cout << "Starting " << n_t * dt << " sec simulation ... \n";
-  auto start = std::chrono::high_resolution_clock::now();
-  for (size_t t = 1; t < n_t; t++) {
-    // Let the controlled system stochastically change gain
-    // Assume another algorithm decodes this mode change and signals the
-    // switched_controller
-    Vector chance(1, arma::fill::randu);
-    if (which_mode == 1)  // mode1
-    {
-      if (chance[0] < pr_21) {
-        which_mode = 2;
-        controlled_system.set_B(b2);
-        if (do_switch_ctrl) {
-          switched_controller.Switch(1);
-        }
-      }
-    } else {  // mode2
-      if (chance[0] < pr_12) {
-        which_mode = 1;
-        controlled_system.set_B(b1);
-        if (do_switch_ctrl) {
-          switched_controller.Switch(0);
-        }
-      }
-    }
-
-    // Simulate the true system.
-    z.col(t) = controlled_system.Simulate(u.col(t - 1));
-
-    // perform control
-    u.col(t) = switched_controller.ControlOutputReference(z.col(t));
-
-    mode.col(t) = which_mode;
-    y_ref.col(t) = y_ref0;
-    y_true.col(t) = controlled_system.y();
-    x_true.col(t) = controlled_system.x();
-    y_hat.col(t) = switched_controller.sys().y();
-    x_hat.col(t) = switched_controller.sys().x();
-  }
-
-  auto finish = std::chrono::high_resolution_clock::now();
-  std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start;
-  cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n";
-  cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n";
-
-  // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true,
-  // x_true, m_true saving with hdf5 via armadillo
-  arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;
-
-  auto dt_vec = Vector(1).fill(dt);
-  dt_vec.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "dt"));
-  y_ref.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_ref", replace));
-  u.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "u", replace));
-  z.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "z", replace));
-  x_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_true", replace));
-  y_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_true", replace));
-  x_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_hat", replace));
-  y_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_hat", replace));
-  mode.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "mode", replace));
-
-  return 0;
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/index.html b/docs/docs/api/files/index.html deleted file mode 100644 index cf4728e1..00000000 --- a/docs/docs/api/files/index.html +++ /dev/null @@ -1,365 +0,0 @@ - - - - - - - - - - - -Files | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - Files - - -
- - - - - - -
- - - -

- Files - # -

- -
-

Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/index.xml b/docs/docs/api/files/index.xml deleted file mode 100644 index eaa0bdc5..00000000 --- a/docs/docs/api/files/index.xml +++ /dev/null @@ -1,372 +0,0 @@ - - - - Files on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/ - Recent content in Files on LDS C&E - Hugo -- gohugo.io - - examples - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/ - examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time - - - - examples/eg_glds_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/ - examples/eg_glds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system -Source code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2. - - - - examples/eg_glds_du_plds_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/ - examples/eg_glds_du_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system -Source code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2. - - - - examples/eg_plds_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/ - examples/eg_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system -Source code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2. - - - - examples/eg_plds_est.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/ - examples/eg_plds_est.cpp # Functions # Name Matrix random_walk(size_t n_t, const Matrix &amp; Q, const Vector &amp; x0) int main() Function Details # random_walk # Matrix random_walk( size_t n_t, const Matrix &amp; Q, const Vector &amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2. - - - - examples/eg_plds_switched_ctrl.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/ - examples/eg_plds_switched_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); // you may not use this file except in compliance with the License. - - - - include - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/ - include # Directories # Name ldsCtrlEst_h Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time - - - - ldsCtrlEst_h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/ - ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl. - - - - ldsCtrlEst_h/lds.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/ - lds namespace - - - - ldsCtrlEst_h/lds_ctrl.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/ - Controller. - - - - ldsCtrlEst_h/lds_fit.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/ - LDS base fit type. - - - - ldsCtrlEst_h/lds_fit_em.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/ - subspace identification - - - - ldsCtrlEst_h/lds_fit_ssid.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/ - subspace identification - - - - ldsCtrlEst_h/lds_gaussian.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/ - glds namespace - - - - ldsCtrlEst_h/lds_gaussian_ctrl.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/ - GLDS Controller. - - - - ldsCtrlEst_h/lds_gaussian_fit.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/ - GLDS fit type. - - - - ldsCtrlEst_h/lds_gaussian_fit_em.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/ - GLDS E-M fit type. - - - - ldsCtrlEst_h/lds_gaussian_fit_ssid.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/ - GLDS SSID fit type. - - - - ldsCtrlEst_h/lds_gaussian_sctrl.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/ - GLDS switched controller type. - - - - ldsCtrlEst_h/lds_gaussian_sys.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/ - GLDS base type. - - - - ldsCtrlEst_h/lds_poisson.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/ - plds namespace - - - - ldsCtrlEst_h/lds_poisson_ctrl.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/ - PLDS controller type. - - - - ldsCtrlEst_h/lds_poisson_fit.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/ - PLDS base fit type. - - - - ldsCtrlEst_h/lds_poisson_fit_em.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/ - PLDS E-M fit type. - - - - ldsCtrlEst_h/lds_poisson_fit_ssid.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/ - PLDS SSID fit type. - - - - ldsCtrlEst_h/lds_poisson_sctrl.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/ - PLDS switched controller type. - - - - ldsCtrlEst_h/lds_poisson_sys.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/ - PLDS base type. - - - - ldsCtrlEst_h/lds_sctrl.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/ - SwitchedController type. - - - - ldsCtrlEst_h/lds_sys.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/ - LDS base type. - - - - ldsCtrlEst_h/lds_uniform_mats.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/ - List of uniformly sized matrices. - - - - ldsCtrlEst_h/lds_uniform_systems.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/ - List of uniformly sized Systems. - - - - ldsCtrlEst_h/lds_uniform_vecs.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/ - List of uniformly sized vectors. - - - - ldsCtrlEst_h/mex_c_util.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/ - arma &lt;-&gt; mex interoperability utilities (Matlab C API) - - - - ldsCtrlEst_h/mex_cpp_util.h - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/ - arma &lt;-&gt; mex interoperability utilities (Matlab C++ API) - - - - src - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/ - src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time - - - - src/lds.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/ - misc lds namespace functions - - - - src/lds_gaussian_sys.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/ - GLDS base type. - - - - src/lds_poisson_sys.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/ - PLDS base type. - - - - src/lds_sys.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/ - LDS base type. - - - - src/lds_uniform_vecs.cpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/ - Uniformly sized vectors. - - - - diff --git a/docs/docs/api/files/lds_8cpp/index.html b/docs/docs/api/files/lds_8cpp/index.html deleted file mode 100644 index f7bad079..00000000 --- a/docs/docs/api/files/lds_8cpp/index.html +++ /dev/null @@ -1,405 +0,0 @@ - - - - - - - - - - - - - -src/lds.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - src/lds.cpp - - -
- - - - - - -
- - - -

- src/lds.cpp - # -

-

misc lds namespace functions More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Detailed Description - # -

-

This file implements miscellaneous lds namespace functions not bound to a class.

-

- Source code - # -

-
//===-- lds.cpp - LDS -----------------------------------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst_h/lds.h>
-
-// insert any necessary function definitions here.
-namespace lds {
-
-void ForceSymPD(Matrix& X) {
-  if (X.is_sympd() || !X.is_square()) {
-    return;
-  }
-
-  // make symmetric
-  X = (X + X.t()) / 2;
-
-  // for eigenval decomp
-  bool did_succeed(true);
-  Vector d;
-  Matrix u;
-
-  // see first method (which may not be ideal):
-  // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/
-  size_t k(1);
-  bool is_sympd = X.is_sympd();
-  Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye);
-
-  while (!is_sympd) {
-    if (k > 100) {
-      did_succeed = arma::eig_sym(d, u, X, "std");
-      data_t min_eig = arma::min(d);
-      std::cerr << "After multiple iterations, min eigen val = " << min_eig
-                << ".\n";
-      throw std::runtime_error(
-          "Failed to make matrix symmetric positive definite.");
-      return;
-    }
-
-    // Limit(d, arma::eps(0), kInf);  // force to be positive...
-    // Matrix d_diag = arma::diagmat(d);
-    // X = u * d_diag * u.t();
-
-    did_succeed = arma::eig_sym(d, u, X, "std");
-    if (!did_succeed) {
-      throw std::runtime_error("ForceSymPD failed.");
-    }
-
-    data_t min_eig = arma::min(d);
-    X += id * abs(min_eig) + arma::datum::eps;
-
-    // make sure symm:
-    X = (X + X.t()) / 2;
-    // double check eigenvals positive after symmetrizing:
-    arma::eig_sym(d, u, X, "std");
-    min_eig = arma::min(d);
-    is_sympd = min_eig > 0;
-    k++;
-  }
-}
-
-void ForceSymMinEig(Matrix& X, data_t eig_min) {
-  if (!X.is_square()) {
-    return;
-  }
-
-  // make symmetric
-  X = (X + X.t()) / 2;
-
-  bool did_succeed(true);
-  Vector d;
-  Matrix u;
-  did_succeed = arma::eig_sym(d, u, X, "std");
-  if (!did_succeed) {
-    throw std::runtime_error("ForceSymMinEig failed.");
-  }
-  Limit(d, eig_min + arma::eps(eig_min), kInf);  // enforce lower bound
-  Matrix d_diag = arma::diagmat(d);
-  X = u * d_diag * u.t();
-
-  // double check symmetric
-  X = (X + X.t()) / 2;
-}
-
-void lq(Matrix& L, Matrix& Qt, const Matrix& X) {
-  bool did_succeed(true);
-  did_succeed = arma::qr_econ(Qt, L, X.t());
-  if (!did_succeed) {
-    throw std::runtime_error("LQ decomposition failed.");
-  }
-  arma::inplace_trans(L);
-  arma::inplace_trans(Qt);
-}
-
-Matrix calcCov(const Matrix& A, const Matrix& B) {
-  // subtract out mean
-  auto m_a = arma::mean(A, 1);
-  Matrix a0 = A;
-  a0.each_col() -= m_a;
-
-  auto m_b = arma::mean(B, 1);
-  Matrix b0 = B;
-  b0.each_col() -= m_b;
-
-  Matrix cov = a0 * b0.t() / a0.n_cols;
-  return cov;
-}
-
-}  // namespace lds
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds_8h/index.html b/docs/docs/api/files/lds_8h/index.html deleted file mode 100644 index fda95abd..00000000 --- a/docs/docs/api/files/lds_8h/index.html +++ /dev/null @@ -1,427 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds.h - # -

-

lds namespace More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Detailed Description - # -

-

This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_H
-#define LDSCTRLEST_LDS_H
-
-// #ifndef LDSCTRLEST
-// #include <ldsCtrlEst>
-// #endif
-
-#include <armadillo>
-
-namespace lds {
-using data_t = double;  // may change to float (but breaks mex functions)
-using Vector = arma::Col<data_t>;
-using Matrix = arma::Mat<data_t>;
-using Cube = arma::Cube<data_t>;
-using View = arma::subview<data_t>;
-
-namespace fill = arma::fill;
-
-
-static const std::size_t kControlTypeDeltaU = 0x1;
-
-static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1;
-
-static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2;
-
-static const data_t kInf = std::numeric_limits<data_t>::infinity();
-static const data_t kPi = arma::datum::pi;
-
-static const data_t kDefaultP0 = 1e-6;  
-static const data_t kDefaultQ0 = 1e-6;  
-static const data_t kDefaultR0 = 1e-2;  
-
-enum SSIDWt {
-  kSSIDNone,   
-  kSSIDMOESP,  
-  kSSIDCVA     
-};
-
-enum MatrixListFreeDim {
-  kMatFreeDimNone,  
-  kMatFreeDim1,     
-  kMatFreeDim2      
-};
-
-// TODO(mfbolus): for SwitchedController, may want systems to have differing
-// numbers of states. Use this enum as template parameter?
-// enum SystemListFreeDim {
-//   kSysFreeDimNone,
-//   kSysFreeDimX  ///< allow state dim (x) of systems in list to be hetero
-// };
-
-// place hard limits on contents of vecors/mats
-void Limit(std::vector<data_t>& x, data_t lb, data_t ub);
-void Limit(Vector& x, data_t lb, data_t ub);
-void Limit(Matrix& x, data_t lb, data_t ub);
-
-// in-place assign that errs if there are dimension mismatches:
-void Reassign(Vector& some, const Vector& other,
-              const std::string& parenthetical = "Reassign");
-void Reassign(Matrix& some, const Matrix& other,
-              const std::string& parenthetical = "Reassign");
-
-// TODO(mfbolus): this is a fudge, but for some reason, cov mats often going
-// numerically asymm.
-
-void ForceSymPD(Matrix& X);
-
-void ForceSymMinEig(Matrix& X, data_t eig_min = 0);
-
-void lq(Matrix& L, Matrix& Qt, const Matrix& X);
-
-Matrix calcCov(const Matrix& A, const Matrix& B);
-
-inline void Limit(std::vector<data_t>& x, data_t lb, data_t ub) {
-  for (data_t& el : x) {
-    el = el < lb ? lb : el;
-    el = el > ub ? ub : el;
-  }
-}
-inline void Limit(Vector& x, data_t lb, data_t ub) {
-  for (data_t& el : x) {
-    el = el < lb ? lb : el;
-    el = el > ub ? ub : el;
-  }
-}
-inline void Limit(Matrix& x, data_t lb, data_t ub) {
-  for (data_t& el : x) {
-    el = el < lb ? lb : el;
-    el = el > ub ? ub : el;
-  }
-}
-
-inline void Reassign(Vector& some, const Vector& other,
-                     const std::string& parenthetical) {
-  // check dimensions
-  if (other.n_elem != some.n_elem) {
-    std::ostringstream ss;
-    ss << "cannot reassign vector of size " << some.n_elem
-       << " with vector of size " << other.n_elem << "(" << parenthetical
-       << ")";
-    throw std::runtime_error(ss.str());
-  }
-
-  for (size_t k = 0; k < some.n_elem; k++) {
-    some[k] = other[k];
-  }
-}
-
-inline void Reassign(Matrix& some, const Matrix& other,
-                     const std::string& parenthetical) {
-  // check dimensions
-  if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) {
-    std::ostringstream ss;
-    ss << "cannot reassign matrix of size " << some.n_rows << "x" << some.n_cols
-       << " with matrix of size " << other.n_rows << "x" << other.n_cols << "("
-       << parenthetical << ")";
-    throw std::runtime_error(ss.str());
-  }
-
-  for (size_t k = 0; k < some.n_elem; k++) {
-    some[k] = other[k];
-  }
-}
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__ctrl_8h/index.html b/docs/docs/api/files/lds__ctrl_8h/index.html deleted file mode 100644 index 1dbe5d2b..00000000 --- a/docs/docs/api/files/lds__ctrl_8h/index.html +++ /dev/null @@ -1,808 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_ctrl.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_ctrl.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_ctrl.h - # -

-

Controller. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::Controller
-

- Detailed Description - # -

-

This file declares the type for control of a linear dynamical system (lds::Controller).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_CTRL_H
-#define LDSCTRLEST_LDS_CTRL_H
-
-// namespace
-#include "lds.h"
-// system type
-#include "lds_sys.h"
-
-namespace lds {
-
-template <typename System>
-class Controller {
-  static_assert(std::is_base_of<lds::System, System>::value,
-                "System must be derived from lds::System type.");
-
- public:
-  Controller() = default;
-
-  Controller(const System& sys, data_t u_lb, data_t u_ub,
-             size_t control_type = 0);
-
-  Controller(System&& sys, data_t u_lb, data_t u_ub, size_t control_type = 0);
-
-  const Vector& Control(const Vector& z, bool do_control = true,
-                        bool do_lock_control = false,
-                        data_t sigma_soft_start = 0, data_t sigma_u_noise = 0,
-                        bool do_reset_at_control_onset = true);
-
-  const Vector& ControlOutputReference(const Vector& z, bool do_control = true,
-                                       bool do_estimation = true,
-                                       bool do_lock_control = false,
-                                       data_t sigma_soft_start = 0,
-                                       data_t sigma_u_noise = 0,
-                                       bool do_reset_at_control_onset = true);
-
-  // get methods:
-  const System& sys() const { return sys_; };
-  const Matrix& Kc() const { return Kc_; };
-  const Matrix& Kc_inty() const { return Kc_inty_; };
-  const Matrix& Kc_u() const { return Kc_u_; };
-  const Vector& g_design() const { return g_design_; };
-  const Vector& u_ref() const { return u_ref_; };
-  const Vector& x_ref() const { return x_ref_; };
-  const Vector& y_ref() const { return y_ref_; };
-  size_t control_type() const { return control_type_; };
-  data_t tau_awu() const { return tau_awu_; };
-  data_t u_lb() const { return u_lb_; };
-  data_t u_ub() const { return u_ub_; };
-
-  // set methods
-  void set_sys(const System& sys) {
-    bool does_match = sys_.n_u() == sys.n_u();
-    does_match = does_match && (sys_.n_x() == sys.n_x());
-    does_match = does_match && (sys_.n_y() == sys.n_y());
-    if (does_match) {
-      sys_ = sys;
-    } else {
-      throw std::runtime_error(
-          "new system argument to `set_sys` does not match dimensionality of "
-          "existing system");
-    }
-  };
-  void set_g_design(const Vector& g_design) { Reassign(g_design_, g_design); };
-  void set_u_ref(const Vector& u_ref) { Reassign(u_ref_, u_ref); };
-  void set_x_ref(const Vector& x_ref) {
-    Reassign(x_ref_, x_ref);
-    cx_ref_ = sys_.C() * x_ref_;
-  };
-
-  // y_ref needs to be handled differently depending on output fn.
-  // (need to populate cx_ref_ too, which depends on output fn)
-  virtual void set_y_ref(const Vector& y_ref) { Reassign(y_ref_, y_ref); };
-
-  void set_Kc(const Matrix& Kc) { Reassign(Kc_, Kc); };
-  void set_Kc_inty(const Matrix& Kc_inty) { Reassign(Kc_inty_, Kc_inty); };
-  void set_Kc_u(const Matrix& Kc_u) { Reassign(Kc_u_, Kc_u); };
-  void set_tau_awu(data_t tau) {
-    tau_awu_ = tau;
-    k_awu_ = sys_.dt() / tau_awu_;
-  };
-
-  void set_control_type(size_t control_type);
-
-  // There is no reason u_lb/ub should not be public, but making set methods
-  // anyway.
-  void set_u_lb(data_t u_lb) { u_lb_ = u_lb; };
-
-  void set_u_ub(data_t u_ub) { u_ub_ = u_ub; };
-
-  void Reset() {
-    sys_.Reset();
-    u_ref_.zeros();
-    u_ref_prev_.zeros();
-    int_e_.zeros();
-    int_e_awu_adjust_.zeros();
-    u_sat_.zeros();
-    u_saturated_ = false;
-    t_since_control_onset_ = 0.0;
-  };
-
-  void Print() {
-    sys_.Print();
-    std::cout << "g_design : " << g_design_ << "\n";
-    std::cout << "u_lb : " << u_lb_ << "\n";
-    std::cout << "u_ub : " << u_ub_ << "\n";
-  };
-
- protected:
-  System sys_;  
-
-  Vector u_;         
-  Vector u_return_;  
-
-  Vector g_design_;  
-
-  //  reference signals
-  Vector u_ref_;  
-  // create no set method for this:
-  Vector u_ref_prev_;  
-  Vector x_ref_;       
-  Vector y_ref_;       
-  Vector cx_ref_;
-
-  // Controller gains
-  Matrix Kc_;  
-  Matrix
-      Kc_u_;  
-  Matrix Kc_inty_;  
-
-  // control after g inversion
-  // do not need set methods for these.
-  Vector du_ref_;
-  Vector dv_ref_;
-  Vector v_ref_;
-  Vector dv_;
-  Vector v_;  
-
-  // integral error
-  // do not need set method for this
-  Vector int_e_;             
-  Vector int_e_awu_adjust_;  
-  Vector u_sat_;  
-
-  bool do_control_prev_ = false;
-  bool do_lock_control_prev_ = false;
-
-  // whether the g of system has become inverted from what you think it is
-  // (gain_ref)
-  bool u_saturated_ =
-      false;  
-
-  // should be safe to have references here bc nothing needs to be done
-  // (like reset vars) when it changes...
-  data_t u_lb_{};  
-  data_t u_ub_{};  
-
-  data_t tau_awu_{};  
-  data_t k_awu_ = 0;
-
-  data_t t_since_control_onset_ = 0;  
-  size_t control_type_{};             
-
- private:
-  void CalcControl(bool do_control = true, bool do_estimation = true,
-                   bool do_lock_control = false, data_t sigma_soft_start = 0,
-                   data_t sigma_u_noise = 0,
-                   bool do_reset_at_control_onset = true);
-
-  void CalcSteadyStateSetPoint();
-
-  void AntiWindup();
-
-  void InitVars(size_t control_type);
-};
-
-// Implement the above:
-
-template <typename System>
-inline Controller<System>::Controller(const System& sys, data_t u_lb,
-                                      data_t u_ub, size_t control_type)
-    : sys_(sys),
-      u_lb_(u_lb),
-      u_ub_(u_ub),
-      tau_awu_(lds::kInf) {
-  InitVars(control_type);
-}
-
-template <typename System>
-inline Controller<System>::Controller(System&& sys, data_t u_lb, data_t u_ub,
-                                      size_t control_type)
-    : sys_(std::move(sys)),
-      u_lb_(u_lb),
-      u_ub_(u_ub),
-      tau_awu_(lds::kInf) {
-  InitVars(control_type);
-}
-
-template <typename System>
-inline void Controller<System>::set_control_type(size_t control_type) {
-  if (control_type_ == control_type) {
-    return;
-  }
-
-  // creating a blank slate...
-  control_type_ = 0;
-  Kc_inty_.zeros(0, 0);
-  Kc_u_.zeros(0, 0);
-  int_e_.zeros(0, 0);
-  int_e_awu_adjust_.zeros(0, 0);
-
-  // controller was designed to minimize integral error
-  if (control_type & kControlTypeIntY) {
-    Kc_inty_.zeros(sys_.n_u(), sys_.n_y());
-    int_e_.zeros(sys_.n_y());
-    int_e_awu_adjust_.zeros(sys_.n_u());
-    control_type_ = control_type_ | kControlTypeIntY;
-  }
-
-  // controller was designed to minimize deltaU
-  // (i.e. state augmented with u)
-  if (control_type & kControlTypeDeltaU) {
-    Kc_u_.zeros(sys_.n_u(), sys_.n_u());
-    control_type_ = control_type_ | kControlTypeDeltaU;
-  }
-
-  // whether to adapt set point calculate with (re-estimated) process
-  // disturbance (m)
-  if (control_type & kControlTypeAdaptM) {
-    if (sys_.do_adapt_m)  // only if adapting m...
-    {
-      control_type_ = control_type_ | kControlTypeAdaptM;
-    }
-  }
-}  // set_control_type
-
-template <typename System>
-inline const Vector& Controller<System>::Control(
-    const Vector& z, bool do_control, bool do_lock_control,
-    data_t sigma_soft_start, data_t sigma_u_noise,
-    bool do_reset_at_control_onset) {
-  // update state estimates, given latest measurement
-  sys_.Filter(u_, z);
-
-  bool do_estimation = true;  // always have estimator on in this case
-
-  // calculate control signal
-  CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start,
-              sigma_u_noise, do_reset_at_control_onset);
-
-  return u_return_;
-}
-
-template <typename System>
-inline const Vector& Controller<System>::ControlOutputReference(
-    const Vector& z, bool do_control, bool do_estimation, bool do_lock_control,
-    data_t sigma_soft_start, data_t sigma_u_noise,
-    bool do_reset_at_control_onset) {
-  // update state estimates, given latest measurement
-  if (do_estimation) {
-    sys_.Filter(u_, z);
-  } else {
-    sys_.f(u_);
-  }
-
-  // calculate the set point
-  // solves for u_ref and x_ref when output is at y_ref at steady state.
-  if (do_control) {
-    CalcSteadyStateSetPoint();
-  }
-
-  // calculate control signal
-  CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start,
-              sigma_u_noise, do_reset_at_control_onset);
-
-  return u_return_;
-}
-
-template <typename System>
-inline void Controller<System>::CalcControl(bool do_control, bool do_estimation,
-                                            bool do_lock_control,
-                                            data_t sigma_soft_start,
-                                            data_t sigma_u_noise,
-                                            bool do_reset_at_control_onset) {
-  if (do_control && do_estimation) {
-    if (!do_control_prev_) {
-      if (do_reset_at_control_onset) {
-        Reset();
-      }
-      t_since_control_onset_ = 0.0;
-    } else {
-      t_since_control_onset_ += sys_.dt();
-    }
-
-    // enforce softstart on control vars.
-    if (sigma_soft_start > 0) {
-      // half-Gaussian soft-start scaling factor
-      data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) /
-                                     (2 * pow(sigma_soft_start, 2)));
-      u_ref_ *= soft_start_sf;
-      // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too
-      // x_ref_ *= soft_start_sf;
-      // cx_ref_ *= soft_start_sf;
-      // y_ref_ *= soft_start_sf;
-    }
-
-    if (!do_lock_control) {
-      // first do u -> v change of vars. (v = g.*u)
-      // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver
-      // control voltage u[=]V)
-      v_ref_ = g_design_ % u_ref_;
-
-      // Given FB, calc. the change in control
-      if (control_type_ & kControlTypeDeltaU) {
-        // if control designed to minimize not u but deltaU (i.e. state aug with
-        // u):
-
-        // TODO(mfbolus): Commented out for now. See note below.
-        // du_ref_ = u_ref_ - u_ref_prev_;
-        // dv_ref_ = g_design_ % du_ref_;
-
-        // TODO(mfbolus): Assuming users want *smooth* control signals if using
-        // kControlTypeDeltaU, it should be the case that dv_ref_ is --> 0. May
-        // want to revisit, but I am going to force it to be zero unless a
-        // situation arises that argues for keeping the above.
-        dv_ref_.zeros();
-
-        dv_ = dv_ref_;                     // nominally-optimal.
-        dv_ -= Kc_ * (sys_.x() - x_ref_);  // instantaneous state error
-        dv_ -= Kc_u_ * (v_ - v_ref_);      // penalty on amp u (rel to ref)
-
-        if (control_type_ & kControlTypeIntY) {
-          // TODO(mfbolus): one approach to protection against integral windup
-          // would be to not integrate error when control signal saturated:
-
-          // if(!uSaturated)
-          int_e_ += (sys_.cx() - cx_ref_) * sys_.dt();  // integrated error
-          dv_ -= Kc_inty_ * int_e_;  // control for integrated error
-        }
-
-        // update the control
-        v_ += dv_;
-      } else {
-        v_ = v_ref_;                      // nominally-optimal.
-        v_ -= Kc_ * (sys_.x() - x_ref_);  // instantaneous state error
-
-        if (control_type_ & kControlTypeIntY) {
-          // TODO(mfbolus): one approach to protection against integral windup
-          // would be to not integrate error when control signal saturated:
-
-          // if (!uSaturated)
-          int_e_ += (sys_.cx() - cx_ref_) * sys_.dt();  // integrated error
-          v_ -= Kc_inty_ * int_e_;  // control for integrated error
-        }
-      }
-
-      // convert back to control voltage u[=]V
-      u_ = v_ / sys_.g();
-    }       // else do nothing until lock is low
-  } else {  // if not control
-    // feed through u_ref in open loop
-    u_ = u_ref_ % g_design_ / sys_.g();
-    v_ = sys_.g() % u_;
-    u_ref_.zeros();
-    int_e_.zeros();
-    int_e_awu_adjust_.zeros();
-    u_sat_.zeros();
-  }  // ends do_control
-
-  // enforce box constraints (and antiwindup)
-  AntiWindup();
-
-  // add noise to input?
-  // The value for u that is *returned* to user after addition of any noise,
-  // while keeping controller/estimator blind to this addition.
-  u_return_ = u_;
-  if ((sigma_u_noise > 0.0) && (do_control && !do_lock_control)) {
-    u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn);
-    Limit(u_return_, u_lb_, u_ub_);
-  };
-
-  // For next time step:
-  u_ref_prev_ = u_ref_;
-  do_control_prev_ = do_control;
-  do_lock_control_prev_ = do_lock_control;
-}  // CalcControl
-
-template <typename System>
-inline void Controller<System>::CalcSteadyStateSetPoint() {
-  // Linearly-constrained least squares (ls).
-  //
-  // _reference:
-  //  Boyd & Vandenberghe (2018) Introduction to Applied Linear Algebra
-  //
-  Matrix a_ls =
-      join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros));
-  Vector b_ls = cx_ref_;
-  Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye),
-                           sys_.B() * arma::diagmat(sys_.g()));
-  Vector d_ls = -sys_.m0();
-  if (control_type_ & kControlTypeAdaptM) {
-    d_ls = -sys_.m();  // adapt setpoint calc with disturbance?
-  }
-
-  Matrix a_ls_t = a_ls.t();  // TODO(mfbolus): not sure why but causes seg
-                             // fault if I do not do this.
-  Matrix phi_ls =
-      join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()),
-                join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros)));
-  // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse:
-  Matrix inv_phi = pinv(phi_ls);
-  Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls);
-  x_ref_ = xulam.subvec(0, sys_.n_x() - 1);
-  u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1);
-  cx_ref_ = sys_.C() * x_ref_;
-}  // CalcSteadyStateSetPoint
-
-template <typename System>
-void Controller<System>::AntiWindup() {
-  u_saturated_ = false;
-  u_sat_ = u_;
-
-  // limit u and flag whether saturated
-  for (size_t k = 0; k < u_.n_elem; k++) {
-    if (u_[k] < u_lb_) {
-      u_sat_[k] = u_lb_;
-      u_saturated_ = true;
-    }
-
-    if (u_[k] > u_ub_) {
-      u_sat_[k] = u_ub_;
-      u_saturated_ = true;
-    }
-  }
-
-  if ((control_type_ & kControlTypeIntY) && (tau_awu_ < lds::kInf)) {
-    // one-step back-calculation (calculate intE for u=u_sat)
-    // (Astroem, Rundqwist 1989 warn against using this...)
-    // int_e_awu_adjust_ =
-    //     solve(Kc_inty_, (u_ - u_sat_));  // pinv(Kc_inty) * (u-uSat);
-
-    // gradual: see Astroem, Rundqwist 1989
-    // this is a fudge for doing MIMO gradual
-    // n.b., went ahead and multiplied 1/T by dt so don't have to do that here.
-    int_e_awu_adjust_ =
-        k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_);
-    // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_);
-
-    int_e_ += int_e_awu_adjust_;
-  }
-
-  // set u to saturated version
-  u_ = u_sat_;
-}
-
-template <typename System>
-void Controller<System>::InitVars(size_t control_type) {
-  // initialize to default values
-  u_ref_ = Vector(sys_.n_u(), fill::zeros);
-  u_ref_prev_ = Vector(sys_.n_u(), fill::zeros);
-  x_ref_ = Vector(sys_.n_x(), fill::zeros);
-  y_ref_ = Vector(sys_.n_y(), fill::zeros);
-  cx_ref_ = Vector(sys_.n_y(), fill::zeros);
-
-  u_ = Vector(sys_.n_u(), fill::zeros);
-  u_return_ = Vector(sys_.n_u(), fill::zeros);
-  u_sat_ = Vector(sys_.n_u(), fill::zeros);
-
-  // Might not need all these, so zero elements until later.
-  Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros);
-  Kc_u_ = Matrix(0, 0, fill::zeros);
-  Kc_inty_ = Matrix(0, 0, fill::zeros);
-
-  g_design_ = sys_.g();  // by default, same as model
-  dv_ = Vector(sys_.n_u(), fill::zeros);
-  v_ = Vector(sys_.n_u(), fill::zeros);
-  du_ref_ = Vector(sys_.n_u(), fill::zeros);
-  dv_ref_ = Vector(sys_.n_u(), fill::zeros);
-  v_ref_ = Vector(sys_.n_u(), fill::zeros);
-
-  int_e_ = Vector(0, fill::zeros);
-  int_e_awu_adjust_ = Vector(0, fill::zeros);
-
-  set_control_type(control_type);
-}
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__fit_8h/index.html b/docs/docs/api/files/lds__fit_8h/index.html deleted file mode 100644 index 75f338a0..00000000 --- a/docs/docs/api/files/lds__fit_8h/index.html +++ /dev/null @@ -1,408 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_fit.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_fit.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_fit.h - # -

-

LDS base fit type. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::Fit
LDS Fit Type.
-

- Detailed Description - # -

-

This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDS_FIT_HPP
-#define LDS_FIT_HPP
-
-// namespace
-#include "lds.h"
-#include "lds_uniform_mats.h"
-
-namespace lds {
-class Fit {
- public:
-  Fit() = default;
-  Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt);
-
-  virtual ~Fit() = default;
-
-  // get methods
-  size_t n_u() const { return n_u_; };
-  size_t n_x() const { return n_x_; };
-  size_t n_y() const { return n_y_; };
-  data_t dt() const { return dt_; };
-  const Matrix& A() const { return A_; };
-  const Matrix& B() const { return B_; };
-  const Vector& g() const { return g_; };
-  const Vector& m() const { return m_; };
-  const Matrix& Q() const { return Q_; };
-  const Vector& x0() const { return x0_; };
-  const Matrix& P0() const { return P0_; };
-  const Matrix& C() const { return C_; };
-  const Vector& d() const { return d_; };
-  // gets measurement noise
-  virtual const Matrix& R() const = 0;
-
-  // set methods (e.g., seeding initial fit values)
-  void set_A(const Matrix& A) { Reassign(A_, A); };
-  void set_B(const Matrix& B) { Reassign(B_, B); };
-  void set_g(const Vector& g) { Reassign(g_, g); };
-  void set_m(const Vector& m) { Reassign(m_, m); };
-  void set_Q(const Matrix& Q) {
-    Reassign(Q_, Q);
-    ForceSymPD(Q_);
-  };
-  virtual void set_R(const Matrix& R) = 0;
-  void set_x0(const Vector& x0) { Reassign(x0_, x0); };
-  void set_P0(const Matrix& P0) {
-    Reassign(P0_, P0);
-    ForceSymPD(P0_);
-  };
-  void set_C(const Matrix& C) { Reassign(C_, C); };
-  void set_d(const Vector& d) { Reassign(d_, d); };
-
-  View f(Matrix& x, const Matrix& u, size_t t) {
-    x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_;
-    return x.col(t);
-  };
-
-  View f(Matrix& x_pre, const Matrix& x_post, const Matrix& u, size_t t) {
-    x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_;
-    return x_pre.col(t);
-  };
-
-  virtual View h(Matrix& y, const Matrix& x, size_t t) = 0;
-
- protected:
-  data_t dt_{};  
-
-  // Dynamics
-  Matrix A_;  
-  Matrix B_;  
-  Vector g_;  
-  Vector m_;  
-  Matrix Q_;  
-
-  // Output
-  Matrix C_;  
-  Vector d_;  
-  Matrix R_; 
-
-  // initial conditions
-  Vector x0_;  
-  Matrix P0_;  
-
-  size_t n_u_{};  
-  size_t n_x_{};  
-  size_t n_y_{};  
-};
-
-}  // namespace lds
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__fit__em_8h/index.html b/docs/docs/api/files/lds__fit__em_8h/index.html deleted file mode 100644 index d654fb42..00000000 --- a/docs/docs/api/files/lds__fit__em_8h/index.html +++ /dev/null @@ -1,891 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_fit_em.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_fit_em.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_fit_em.h - # -

-

subspace identification More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::EM
-

- Detailed Description - # -

-

This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_EMAX_H
-#define LDSCTRLEST_LDS_EMAX_H
-
-#include "lds_fit.h"
-
-namespace lds {
-
-template <typename Fit>
-class EM {
-  static_assert(std::is_base_of<lds::Fit, Fit>::value,
-                "Fit must be derived from lds::Fit type.");
-
- public:
-  EM() = default;
-
-  EM(size_t n_x, data_t dt, UniformMatrixList<kMatFreeDim2>&& u_train,
-     UniformMatrixList<kMatFreeDim2>&& z_train);
-
-  EM(const Fit& fit0, UniformMatrixList<kMatFreeDim2>&& u_train,
-     UniformMatrixList<kMatFreeDim2>&& z_train);
-
-  virtual ~EM() = default;
-
-  const Fit& Run(bool calc_dynamics = true, bool calc_Q = true,
-                 bool calc_init = true, bool calc_output = true,
-                 bool calc_measurement = true, size_t max_iter = 100,
-                 data_t tol = 1e-2);
-
-  std::tuple<UniformMatrixList<kMatFreeDim2>, UniformMatrixList<kMatFreeDim2>>
-  ReturnData() {
-    auto tuple = std::make_tuple(std::move(u_), std::move(z_));
-    // auto tuple = std::make_tuple(u_, z_);
-    u_ = UniformMatrixList<kMatFreeDim2>();
-    z_ = UniformMatrixList<kMatFreeDim2>();
-    return tuple;
-  }
-
-  const std::vector<Matrix>& x() const { return x_; };
-  const std::vector<Matrix>& y() const { return y_; };
-
-  const Matrix& sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; };
-  const Matrix& sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; };
-  const Matrix& sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; };
-  size_t n_t_tot() { return n_t_tot_; }
-
-  const Vector& theta() const { return theta_; };
-
- protected:
-  void Expectation(bool force_common_initial = false);
-
-  void Maximization(bool calc_dynamics = true, bool calc_Q = true,
-                    bool calc_init = false, bool calc_output = false,
-                    bool calc_measurement = false);
-
-  void MaximizeDynamics();
-  void MaximizeQ();
-  void MaximizeInitial();
-  virtual void MaximizeOutput() = 0;
-  virtual void MaximizeMeasurement() = 0;
-
-  void Smooth(bool force_common_initial);
-
-  virtual void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) = 0;
-
-  void Reset();
-
-  void InitVars();
-
-  Vector UpdateTheta();
-
-  // input/output training data
-  UniformMatrixList<kMatFreeDim2> u_;  
-  UniformMatrixList<kMatFreeDim2> z_;  
-  std::vector<Matrix> x_;              
-  std::vector<Cube> P_;                
-  std::vector<Cube> P_t_tm1_;          
-  std::vector<Matrix> y_;              
-  Matrix diag_y_;
-
-  // expectations calculated in E-step
-  Matrix sum_E_x_t_x_t_;        
-  Matrix sum_E_xu_tm1_xu_tm1_;  
-  Matrix sum_E_xu_t_xu_tm1_;    
-
-  Fit fit_;
-  Vector theta_;
-
-  data_t dt_{};              
-  size_t n_u_{};             
-  size_t n_x_{};             
-  size_t n_y_{};             
-  size_t n_trials_{};        
-  std::vector<size_t> n_t_;  
-  size_t n_t_tot_{};         
-};
-
-template <typename Fit>
-EM<Fit>::EM(size_t n_x, data_t dt, UniformMatrixList<kMatFreeDim2>&& u_train,
-            UniformMatrixList<kMatFreeDim2>&& z_train) {
-  n_u_ = u_train.at(0).n_rows;
-  n_y_ = z_train.at(0).n_rows;
-  fit_ = Fit(n_u_, n_x, n_y_, dt);
-  u_ = std::move(u_train);
-  z_ = std::move(z_train);
-  InitVars();
-}
-
-template <typename Fit>
-EM<Fit>::EM(const Fit& fit0, UniformMatrixList<kMatFreeDim2>&& u_train,
-            UniformMatrixList<kMatFreeDim2>&& z_train) {
-  // make sure fit dims match I/O data
-  if (fit0.n_u() != u_train.at(0).n_rows) {
-    throw std::runtime_error(
-        "Initial fit and input training data have inconsistent dimensions");
-  }
-  if (fit0.n_y() != z_train.at(0).n_rows) {
-    throw std::runtime_error(
-        "Initial fit and output training data have inconsistent dimensions");
-  }
-
-  fit_ = fit0;
-  u_ = std::move(u_train);
-  z_ = std::move(z_train);
-
-  InitVars();
-}
-
-template <typename Fit>
-void EM<Fit>::InitVars() {
-  // check input/output data dimensions are consistent
-  if (z_.size() != u_.size()) {
-    throw std::runtime_error(
-        "I/O training data have different number of trials.");
-  }
-  n_trials_ = u_.size();
-
-  n_t_tot_ = 0;
-  n_t_ = std::vector<size_t>(n_trials_);
-  for (size_t trial = 0; trial < n_trials_; trial++) {
-    if (z_.at(trial).n_cols != u_.at(trial).n_cols) {
-      throw std::runtime_error(
-          "I/O training data have different number of time steps.");
-    }
-    n_t_[trial] = u_.at(trial).n_cols;
-    n_t_tot_ += n_t_[trial];
-  }
-
-  n_u_ = fit_.n_u();
-  n_x_ = fit_.n_x();
-  n_y_ = fit_.n_y();
-  dt_ = fit_.dt();
-
-  x_ = std::vector<Matrix>(n_trials_);
-  P_ = std::vector<Cube>(n_trials_);
-  P_t_tm1_ = std::vector<Cube>(n_trials_);
-  y_ = std::vector<Matrix>(n_trials_);
-  for (size_t trial = 0; trial < n_trials_; trial++) {
-    x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros);
-    P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros);
-    P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros);
-    y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros);
-  }
-
-  diag_y_ = Matrix(n_y_, n_y_, fill::zeros);
-
-  // covariances in expectation step
-  sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros);
-  sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros);
-  sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros);
-}
-
-template <typename Fit>
-const Fit& EM<Fit>::Run(bool calc_dynamics, bool calc_Q, bool calc_init,
-                        bool calc_output, bool calc_measurement,
-                        size_t max_iter, data_t tol) {
-  Reset();  // to initial conditions
-
-  size_t n_params =
-      3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_;
-  Vector theta(n_params);
-  Vector theta_new(n_params);
-  data_t max_dtheta = 1;
-
-  // if solving for initial conditions, allow them be varied.
-  // otherwise, freeze at provided values.
-  bool force_common_initial = !calc_init;
-
-  // go until parameter convergence
-  for (size_t l = 0; l < max_iter; l++) {
-    theta_ = UpdateTheta();
-
-    std::cout << "Iteration " << l + 1 << "/" << max_iter << " ...\n";
-
-    Expectation(force_common_initial);
-    Maximization(calc_dynamics, calc_Q, calc_init, calc_output,
-                 calc_measurement);
-
-    // check convergence
-    theta_new = UpdateTheta();
-
-    Vector dtheta = abs(theta_new - theta_) / abs(theta_);
-    // some parameters could be zero...
-    arma::uvec ubi_finite = find_finite(dtheta);
-
-    max_dtheta = max(dtheta.elem(ubi_finite));
-    std::cout << "max dtheta: " << max_dtheta << "\n";
-    if (max_dtheta < tol) {
-      std::cout << "Converged.\n";
-      break;
-    }
-
-    std::cout << "\n";
-  }
-
-  return fit_;
-}
-
-template <typename Fit>
-void EM<Fit>::Smooth(bool force_common_initial) {
-  Matrix k_e(n_x_, n_y_);  // estimator gain
-  Cube k_backfilt;         // back-filtering gains
-
-  // TODO(mfbolus): this loop could be made parallel
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    Matrix x_pre(n_x_, n_t_[trial], fill::zeros);
-    Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros);
-    Matrix x_post(n_x_, n_t_[trial], fill::zeros);
-    Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros);
-
-    if (force_common_initial)  // forces all trials to have same initial
-                               // conditions.
-    {
-      x_[trial].col(0) = fit_.x0();
-      P_[trial].slice(0) = fit_.P0();
-    }
-    y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d();
-
-    // This *should not* be necessary but make sure P is symmetric.
-    ForceSymPD(P_[trial].slice(0));
-
-    x_pre.col(0) = x_[trial].col(0);
-    p_pre.slice(0) = P_[trial].slice(0);
-
-    x_post.col(0) = x_[trial].col(0);
-    p_post.slice(0) = P_[trial].slice(0);
-
-    // filter
-    for (size_t t = 1; t < n_t_[trial]; t++) {
-      // predict
-      fit_.f(x_pre, x_post, u_.at(trial), t);
-      fit_.h(y_[trial], x_pre, t);
-      diag_y_.diag() = y_[trial].col(t);  // TODO(mfbolus): change if parallel
-
-      // update --> posterior estimation
-      RecurseKe(k_e, p_pre, p_post, t);
-      x_post.col(t) =
-          x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t));
-      y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d();
-    }
-
-    // backfilter -> Smoothed estimate
-    // Reference:
-    // Shumway et Stoffer (1982)
-    ForceSymPD(p_post.slice(n_t_[trial] - 1));
-    k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros);
-    x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1);
-    P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1);
-    for (size_t t = (n_t_[trial] - 1); t > 0; t--) {
-      // TODO(mfmbolus): should not be necessary to force symm positive def
-      ForceSymPD(p_pre.slice(t));
-      ForceSymPD(p_post.slice(t - 1));
-      ForceSymPD(P_[trial].slice(t));
-      k_backfilt.slice(t - 1) =
-          p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t));
-      x_[trial].col(t - 1) =
-          x_post.col(t - 1) +
-          k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t));
-      P_[trial].slice(t - 1) =
-          p_post.slice(t - 1) + k_backfilt.slice(t - 1) *
-                                    (P_[trial].slice(t) - p_pre.slice(t)) *
-                                    k_backfilt.slice(t - 1).t();
-    }
-
-    // do the same for P_t_tm1
-    Matrix id(n_x_, n_x_, fill::eye);
-    P_t_tm1_[trial].slice(n_t_[trial] - 1) =
-        (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2);
-    for (size_t t = (n_t_[trial] - 1); t > 1; t--) {
-      P_t_tm1_[trial].slice(t - 1) =
-          p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() +
-          k_backfilt.slice(t - 1) *
-              (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) *
-              k_backfilt.slice(t - 2).t();
-    }
-
-    // finally, get smoothed estimate of output
-    for (size_t t = 0; t < n_t_[trial]; t++) {
-      fit_.h(y_[trial], x_[trial], t);
-    }  // samps loop
-  }    // trial loop
-}  // Smooth
-
-// template <typename Fit>
-// void EM<Fit>::RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) {
-//   // predict covar
-//   P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q();
-
-//   // update Ke
-//   Ke = P_pre.slice(t) * fit_.C().t() *
-//        inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R());
-
-//   // update cov
-//   // Reference: Ghahramani et Hinton (1996)
-//   P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t);
-
-//   // // n.b. for poisson :
-//   // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() +
-//   fit_.Q();
-//   // // update cov
-//   // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ *
-//   // fit_.C());
-//   // // update Ke
-//   // Ke = P_post.slice(t) * fit_.C();
-// }
-
-template <typename Fit>
-void EM<Fit>::Expectation(bool force_common_initial) {
-  // calculate the mean/cov of state needed for maximizing E[pr(z|theta)]
-  Smooth(force_common_initial);
-
-  // now get the various forms of sum(E[xx']) needed
-  // n.b. Going to start at t=1 rather than 0 bc most max terms need that.
-  // so really "n_t_tot_" is (n_t_tot_-1)
-  n_t_tot_ = 0;
-  sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros);
-  sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros);
-  sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros);
-
-  Vector xu_tm1(n_x_ + n_u_, fill::zeros);
-  Vector xu_t(n_x_ + n_u_, fill::zeros);
-
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    for (size_t t = 1; t < n_t_[trial]; t++) {
-      // ------------ sum_E_x_t_x_t ------------
-      sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t();
-      sum_E_x_t_x_t_ += P_[trial].slice(t);
-
-      // ------------ sum_E_xu_tm1_xu_tm1 ------------
-      xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1));
-      sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t();
-      sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) +=
-          P_[trial].slice(t - 1);
-
-      // ------------ sum_E_xu_t_xu_tm1 ------------
-      xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t));
-      sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t();
-      sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) +=
-          P_t_tm1_[trial].slice(t);
-
-      n_t_tot_ += 1;
-    }  // time
-  }    // trial
-}  // Expectation
-
-template <typename Fit>
-void EM<Fit>::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init,
-                           bool calc_output, bool calc_measurement) {
-  if (calc_output) {
-    MaximizeOutput();
-  }
-
-  if (calc_measurement) {
-    MaximizeMeasurement();
-  }
-
-  if (calc_dynamics) {
-    MaximizeDynamics();
-  }
-
-  if (calc_Q) {
-    MaximizeQ();
-  }
-
-  if (calc_init) {
-    MaximizeInitial();
-  }
-}  // Maximization
-
-template <typename Fit>
-void EM<Fit>::MaximizeDynamics() {
-  // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996)
-  Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) *
-              inv_sympd(sum_E_xu_tm1_xu_tm1_);
-  fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1));
-  fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1));
-  std::cout << "A_new[0]: " << fit_.A()[0] << "\n";
-  std::cout << "B_new[0]: " << fit_.B()[0] << "\n";
-}
-
-template <typename Fit>
-void EM<Fit>::MaximizeQ() {
-  // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996)
-  // View sum_e_x_t_xu_tm1 =
-  //     sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1);
-  // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 *
-  //                                inv_sympd(sum_E_xu_tm1_xu_tm1_) *
-  //                                sum_e_x_t_xu_tm1.t();
-  // q /= n_t_tot_;
-
-  // this way is same as above iff dynamics were just updated:
-  // View sum_e_x_t_xu_tm1 =
-  //     sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1);
-  // Matrix ab = arma::join_horiz(fit_.A(), fit_.B());
-  // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t();
-  // q /= n_t_tot_;
-
-  // From scratch method:
-  // Q is covariance of the error between state and dynamics-predicted state
-  // (aka process noise)
-  // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})']
-  // t-1 terms:
-  View sum_e_x_tm1_x_tm1 =
-      sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1);
-  View sum_e_u_tm1_u_tm1 =
-      sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1);
-  View sum_e_x_tm1_u_tm1 =
-      sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1);
-
-  // t, t-1 terms:
-  View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1);
-  View sum_e_x_t_u_tm1 =
-      sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1);
-
-  Matrix q = sum_E_x_t_x_t_;
-  q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t();
-  q -= sum_e_x_t_x_tm1 * fit_.A().t();
-  q -= fit_.A() * sum_e_x_t_x_tm1.t();
-  // input-related terms:
-  q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t();
-  q -= sum_e_x_t_u_tm1 * fit_.B().t();
-  q -= fit_.B() * sum_e_x_t_u_tm1.t();
-  q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t();
-  q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t();
-  q /= n_t_tot_;
-
-  fit_.set_Q(q);
-  std::cout << "Q_new[0]: " << fit_.Q()[0] << "\n";
-  // std::cout << "Q_new: \n" << fit_.Q() << "\n";
-}
-
-template <typename Fit>
-void EM<Fit>::MaximizeInitial() {
-  Vector x0 = fit_.x0();
-  x0.zeros();
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    x0 += x_[trial].col(0);
-  }
-  x0 /= z_.size();
-  std::cout << "x0_new[0]: " << x0[0] << "\n";
-
-  // always recalc P0 even if the initial state is fixed (at zero, for
-  // example)
-  Matrix e_var(n_x_, n_x_, fill::zeros);
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t();
-  }
-  e_var /= z_.size();
-
-  // go ahead and subtract x0*x0' so don't have to below.
-  e_var -= x0 * x0.t();
-
-  // To get P0, going to get initial P_ per trial and average.
-  // (which might be wrong, but need a single number)
-  Matrix p0 = fit_.P0();
-  p0.zeros();
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    p0 +=
-        (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var;
-  }
-  p0 /= z_.size();
-
-  fit_.set_P0(p0);
-  std::cout << "P0_new[0]: " << fit_.P0()[0] << "\n";
-}
-
-template <typename Fit>
-void EM<Fit>::MaximizeOutput() {
-  // solve for C+d:
-  Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros);
-  Vector x1(n_x_ + 1, fill::zeros);
-  x1[n_x_] = 1.0;  // augment with one to solve for bias
-  Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros);
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    for (size_t t = 1; t < n_t_[trial]; t++) {
-      x1.subvec(0, n_x_ - 1) = x_[trial].col(t);
-      sum_zx += z_.at(trial).col(t) * x1.t();
-      sum_e_x1_x1 += x1 * x1.t();
-      sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t);
-    }
-  }
-  Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1);
-  fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1));
-  fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_)));
-  std::cout << "C_new[0]: " << fit_.C()[0] << "\n";
-  std::cout << "d_new[0]: " << fit_.d()[0] << "\n";
-}
-
-template <typename Fit>
-void EM<Fit>::MaximizeMeasurement() {
-  // Solve for measurement noise covar
-  size_t n_t_tot = 0;
-  // Ghahgramani, Hinton 1996:
-  Matrix sum_zz(n_y_, n_y_, fill::zeros);
-  Matrix sum_yz(n_y_, n_y_, fill::zeros);
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    for (size_t t = 1; t < n_t_[trial]; t++) {
-      sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t();
-      // Use Cnew:
-      sum_yz +=
-          (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t();
-      n_t_tot += 1;
-    }
-  }
-  fit_.set_R((sum_zz - sum_yz) / n_t_tot);
-  std::cout << "R_new[0]: " << fit_.R()[0] << "\n";
-}
-
-template <typename Fit>
-void EM<Fit>::Reset() {
-  // reset to initial conditions
-  for (size_t trial = 0; trial < n_trials_; trial++) {
-    x_[trial].col(0) = fit_.x0();
-    P_[trial].slice(0) = fit_.P0();
-    y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d();
-  }
-}
-
-template <typename Fit>
-Vector EM<Fit>::UpdateTheta() {
-  // TODO(mfbolus): This should include n_y_ more params for d.
-  size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_;
-  if (fit_.R().n_elem > 0) {
-    n_params += n_y_ * n_y_;
-  }
-  Vector theta(n_params);
-
-  size_t idx_start = 0;
-  theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A());
-  idx_start += n_x_ * n_x_;
-  theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B());
-  idx_start += n_x_ * n_u_;
-  theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q());
-  idx_start += n_x_ * n_x_;
-  theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0());
-  idx_start += n_x_;
-  theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0());
-  idx_start += n_x_ * n_x_;
-  theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C());
-  idx_start += n_y_ * n_x_;
-  theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d());
-  idx_start += n_y_;
-  if (fit_.R().n_elem > 0) {
-    theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R());
-  }
-
-  return theta;
-}
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__fit__ssid_8h/index.html b/docs/docs/api/files/lds__fit__ssid_8h/index.html deleted file mode 100644 index c61c8c8d..00000000 --- a/docs/docs/api/files/lds__fit__ssid_8h/index.html +++ /dev/null @@ -1,837 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_fit_ssid.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_fit_ssid.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_fit_ssid.h - # -

-

subspace identification More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::SSID
-

- Detailed Description - # -

-

This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)<Fit>).

-

References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_FIT_SSID_H
-#define LDSCTRLEST_LDS_FIT_SSID_H
-
-#include "lds_fit.h"
-
-namespace lds {
-
-template <typename Fit>
-class SSID {
-  static_assert(std::is_base_of<lds::Fit, Fit>::value,
-                "Fit must be derived from lds::Fit type.");
-
- public:
-  SSID() = default;
-
-  SSID(size_t n_x, size_t n_h, data_t dt,
-       UniformMatrixList<kMatFreeDim2>&& u_train,
-       UniformMatrixList<kMatFreeDim2>&& z_train,
-       const Vector& d = Vector(1).fill(-kInf));
-
-  std::tuple<Fit, Vector> Run(SSIDWt ssid_wt);
-
-  std::tuple<UniformMatrixList<kMatFreeDim2>, UniformMatrixList<kMatFreeDim2>>
-  ReturnData() {
-    auto tuple = std::make_tuple(std::move(u_), std::move(z_));
-    u_ = UniformMatrixList<kMatFreeDim2>();
-    z_ = UniformMatrixList<kMatFreeDim2>();
-    return tuple;
-  }
-
- protected:
-  void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001);
-
-  void CreateHankelDataMat();
-
-  virtual void DecomposeData() = 0;
-
-  void CalcSVD(SSIDWt wt);
-
-  void Solve(data_t wt_dc);
-
-  void RecomputeExtObs();
-
-  // input/output training data
-  UniformMatrixList<kMatFreeDim2> u_;  
-  UniformMatrixList<kMatFreeDim2> z_;  
-  Matrix D_;                           
-
-  Fit fit_;      
-  Matrix g_dc_;  
-
-  data_t dt_{};   
-  size_t n_u_{};  
-  size_t n_x_{};  
-  size_t n_y_{};  
-  size_t n_h_{};
-  size_t n_trials_{};        
-  std::vector<size_t> n_t_;  
-  size_t n_t_tot_{};         
-
-  Matrix L_;          
-  Vector s_;          
-  Matrix ext_obs_t_;  
-};
-
-template <typename Fit>
-SSID<Fit>::SSID(size_t n_x, size_t n_h, data_t dt,
-                UniformMatrixList<kMatFreeDim2>&& u_train,
-                UniformMatrixList<kMatFreeDim2>&& z_train, const Vector& d) {
-  // check input/output data dimensions are consistent
-  if (z_train.size() != u_train.size()) {
-    throw std::runtime_error(
-        "I/O training data have different number of trials.");
-  }
-  n_trials_ = u_train.size();
-
-  n_t_tot_ = 0;
-  n_t_ = std::vector<size_t>(n_trials_);
-  for (size_t trial = 0; trial < n_trials_; trial++) {
-    if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) {
-      throw std::runtime_error(
-          "I/O training data have different number of time steps.");
-    }
-    n_t_[trial] = u_train.at(trial).n_cols;
-    n_t_tot_ += n_t_[trial];
-  }
-
-  dt_ = dt;
-  n_x_ = n_x;
-  n_u_ = u_train.at(0).n_rows;
-  n_y_ = z_train.at(0).n_rows;
-  n_h_ = n_h;
-
-  // dimensionality check for eventual block-hankel data matrix
-  size_t len = n_t_tot_ - 2 * n_h_ + 1;
-  if (len < (2 * n_h_ * (n_u_ + n_y_))) {
-    std::ostringstream ss;
-    ss << "Dataset problem! More rows than columns in block-hankel data "
-          "matrix: 2*(n_u+n_y)*n_h > data-length! Need higher data-length or "
-          "lower n_h.";
-    throw std::runtime_error(ss.str());
-  }
-
-  fit_ = Fit(n_u_, n_x_, n_y_, dt_);
-
-  u_ = std::move(u_train);
-  z_ = std::move(z_train);
-
-  if (!d.is_finite() || (d.n_rows != n_y_)) {
-    // TODO(mfbolus): implement least-square solution for impulse response with
-    // a second input of ones. Data-driven way of accounting for offset *not*
-    // driven by an input.
-    //
-    // For now, calculate output bias (d) as the
-    // output wherever the stimulus has not been on for some amount of time.
-    // convolve u with rectangle and take all samples. This is a reasonable
-    // approach, since often when autonomous systems are fit (i.e., systems with
-    // no input), they will subtract off the mean of the output. This
-    // essentially amounts to setting output bias to the mean of the output when
-    // there is no stimulation.
-    data_t t_silence = 0.1;
-    data_t thresh_silence = 0.001;
-    CalcD(t_silence, thresh_silence);
-  } else {
-    fit_.set_d(d);
-  }
-}
-
-template <typename Fit>
-std::tuple<Fit, Vector> SSID<Fit>::Run(SSIDWt ssid_wt) {
-  // the weight on minimizing dc I/O gain only works for gaussian,
-  // and hopefully not necessary with appropriate dataset.
-  data_t wt_dc = 0;
-  // std::cout << "creating hankel mat\n";
-  CreateHankelDataMat();
-  // std::cout << "decomposing data\n";
-  DecomposeData();
-  // std::cout << "calculating svd\n";
-  CalcSVD(ssid_wt);
-  // std::cout << "solving for params\n";
-  Solve(wt_dc);
-  // std::cout << "fin\n";
-  return std::make_tuple(fit_, s_);
-}
-
-template <typename Fit>
-void SSID<Fit>::CalcD(data_t t_silence, data_t thresh_silence) {
-  Vector d(z_.at(0).n_rows, fill::zeros);
-  Vector win(static_cast<size_t>(t_silence / dt_), fill::ones);
-  Vector sum_z_silence(n_y_, fill::zeros);
-  size_t n_silence(0);
-  for (size_t trial = 0; trial < u_.size(); trial++) {
-    // find silent samples
-    // start by convolving with
-    Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0));
-    Vector u_conv = conv(sum_u, win, "same");
-
-    // get only the samples that are silent...
-    arma::uvec ubi_silence = find(u_conv <= thresh_silence);
-    if (ubi_silence.n_elem > 0) {
-      sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1);
-      n_silence += ubi_silence.n_elem;
-    }
-  }
-  if (n_silence > 0) {
-    d = sum_z_silence / n_silence;
-  }
-  fit_.set_d(d);
-}
-
-template <typename Fit>
-void SSID<Fit>::CreateHankelDataMat() {
-  // temporary copy of data
-  Matrix z(n_y_, n_t_tot_, fill::zeros);
-  Matrix u(n_u_, n_t_tot_, fill::zeros);
-  size_t so_far(0);
-
-  for (size_t trial = 0; trial < z_.size(); trial++) {
-    z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial);
-    u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial);
-    so_far += n_t_.at(trial);
-  }
-
-  // remove output bias
-  z.each_col() -= fit_.d();
-
-  // calculate I/O gain @ DC while data in convenient form
-  g_dc_ = z * pinv(u);
-  // std::cout << "G0_data = " << g_dc_ << "\n";
-
-  // create hankel data matrix
-  size_t len = z.n_cols - 2 * n_h_ + 1;  // data length in hankel mat
-
-  // block-hankel data matrix
-  D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros);
-  // past input
-  auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1);
-  // future input
-  auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1);
-  // past output
-  auto y_p =
-      D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1);
-  // future output
-  auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0,
-                       2 * n_h_ * (n_u_ + n_y_) - 1, len - 1);
-
-  size_t idx = 0;
-  for (size_t k = 0; k < len; k++) {
-    idx = 0;
-    for (size_t kk = k; kk < (n_h_ + k); kk++) {
-      u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk);
-      idx += n_u_;
-    }
-
-    idx = 0;
-    for (size_t kk = (n_h_ + k); kk < (2 * n_h_ + k); kk++) {
-      u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk);
-      idx += n_u_;
-    }
-
-    idx = 0;
-    for (size_t kk = k; kk < (n_h_ + k); kk++) {
-      y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk);
-      idx += n_y_;
-    }
-
-    idx = 0;
-    for (size_t kk = (n_h_ + k); kk < (2 * n_h_ + k); kk++) {
-      y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk);
-      idx += n_y_;
-    }
-  }
-
-  D_ /= sqrt(static_cast<data_t>(len));
-}
-
-// template <typename Fit>
-// void SSID<Fit>::DecomposeData() {
-//   // do LQ decomp instead of calculating covariance expensive way
-//   // Note that "R" in van Overschee is lower-triangular (L), not "R" in QR
-//   // decomp. Very confusing.
-//   Matrix q_t;
-//   lq(L_, q_t, D_);
-//   // van Overschee zeros out the other elements.
-//   L_ = trimatl(L_);
-// }
-
-template <typename Fit>
-void SSID<Fit>::CalcSVD(SSIDWt wt) {
-  // submats that will be needed:
-  auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1,
-                           n_h_ * (2 * n_u_ + n_y_) - 1);
-  auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1);
-  auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1);
-  auto R_23_13 =
-      L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1);
-  auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1,
-                           n_h_ * (2 * n_u_ + n_y_) - 1);
-  auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1,
-                           n_h_ * (2 * n_u_) - 1);
-  auto R_44 =
-      L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1,
-                n_h_ * (2 * n_u_ + n_y_) - 1);
-  auto R_56_14 =
-      L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1,
-                n_h_ * (2 * n_u_ + n_y_) - 1);
-
-  Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14);
-  auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1);
-  auto Luf =
-      Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1);
-  auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1,
-                                n_h_ * (2 * n_u_ + n_y_) - 1);
-
-  // aka: R_f
-  Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0,
-                             2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1);
-  // from van Overschee subid.m:
-  // Rf = R((2*m+l)*i+1:2*(m+l)*i,:);   % Future outputs
-
-  Matrix U;
-  Matrix V;
-  switch (wt) {
-    case kSSIDNone: {
-      // No weighting. (what van Overschee calls "N4SID")
-      Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14;
-      arma::svd(U, s_, V, O_k_sans_Qt, "std");
-    } break;
-    case kSSIDMOESP: {
-      // MOESP weighting
-      // This is what they use in the "robust" algorithm van Overschee, de Moor
-      // 1996
-      Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) -
-                  R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13;
-      Matrix O_k_ortho_Uf_sans_Qt =
-          join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44);
-      svd(U, s_, V, O_k_ortho_Uf_sans_Qt, "std");
-    } break;
-    case kSSIDCVA: {
-      // CVA weighting
-      // See van Overschee's matlab code (subid.m):
-      // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems
-      Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) -
-                  R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13;
-      Matrix O_k_ortho_Uf_sans_Qt =
-          join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44);
-
-      Matrix inv_w1;
-      Matrix qt1;
-      lq(inv_w1, qt1, R_56_16);  // lq decomp of R_f (future output data)
-      inv_w1 = trimatl(inv_w1);
-      inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1);
-      Matrix w_o_w = arma::solve(
-          inv_w1, O_k_ortho_Uf_sans_Qt);  // alternatively
-                                          // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt
-      svd(U, s_, V, w_o_w, "std");
-
-      U = inv_w1 * U;
-      break;
-    }
-  }
-
-  // Truncate to model order (heart of ssid method)
-  auto s_hat = s_.subvec(0, n_x_ - 1);
-  Matrix diag_sqrt_s = diagmat(sqrt(s_hat));
-  auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1);
-
-  // get extended observability and controllability mats
-  ext_obs_t_ = u_hat * diag_sqrt_s;  // extended observability matrix
-}
-
-template <typename Fit>
-void SSID<Fit>::Solve(data_t wt_dc) {
-  // required submats
-  auto R_56_14 =
-      L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1,
-                n_h_ * (2 * n_u_ + n_y_) - 1);
-  auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1,
-                           n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1);
-  auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0,
-                           2 * n_h_ * (n_u_ + n_y_) - 1,
-                           n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1);
-  auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0,
-                           n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1,
-                           n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1);
-  auto R_56_15 =
-      L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1,
-                n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1);
-
-  // Solve for params using appropriate algorithm:
-  // robust deterministic/stochastic algorithm in van Overschee 1996
-  // algorithm that the authors say "works" in practice.
-  auto ext_obs_tm1 = ext_obs_t_.submat(
-      0, 0, ext_obs_t_.n_rows - 1 - n_y_,
-      ext_obs_t_.n_cols - 1);  // extended observability matrix
-
-  // This is what textbook (1996) says:
-  //
-  // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15);
-  //
-  // HOWEVER, do not know why but have to fill the last place with zeros like
-  // authors' matlab implementation (see `subid.m`)
-  // Otherwise, get ridiculous covariances (although A,C estimates are close to
-  // same...)
-  Matrix Tr = join_vert(
-      join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)),
-      R_23_15);
-  Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15);
-  Matrix S = Tl * pinv(Tr);
-
-  // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure
-  // stability.
-  fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1));
-  Matrix ext_obs_t_p1 = join_vert(
-      ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1),
-      Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros));
-  fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1);
-
-  // At this point, van Overschee & de Moor suggest re-calculating ext_obs_t_,
-  // ext_obs_tm1 from (A, C) because it was just an approximation. This is
-  RecomputeExtObs();
-  ext_obs_tm1 = ext_obs_t_.submat(
-      0, 0, ext_obs_t_.n_rows - 1 - n_y_,
-      ext_obs_t_.n_cols - 1);  // extended observability matrix
-  Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15);
-  Tr = join_vert(
-      join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)),
-      R_23_15);
-  S = Tl * pinv(Tr);
-
-  Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_);
-  Matrix Mcurly = pinv(ext_obs_tm1);
-  Matrix Pcurly = Tl - Lcurly * R_56_15;
-  Vector Pvec = vectorise(Pcurly);
-  Matrix Qcurly = R_23_15;
-
-  // Identify [D; B], assuming D=0 and ensuring DC gain is correct
-  Matrix sum_QcurlyT_kron_Ncurly(
-      (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_),
-      fill::zeros);
-
-  Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols,
-                         fill::eye);
-  eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1,
-                         eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1;
-
-  // van Overschee (1996) p. 126
-  Matrix N1_Tl = -Lcurly;
-  N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) +=
-      join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly);
-  N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) +=
-      Matrix(n_y_, n_y_, fill::eye);
-
-  Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros);
-  Matrix N_k;
-  for (size_t k = 0; k < n_h_; k++) {
-    auto Qcurly_k =
-        Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1);
-
-    Nk_Tl.zeros();
-    Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) =
-        N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1);
-    N_k = Nk_Tl * eye_ext_obs_tm1;
-
-    sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k);
-  }
-
-  Matrix err_vec;
-  if (wt_dc > 0) {
-    // Constraints enforced by weighted least squares
-    //
-    // Reference:
-    //
-    // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly
-    // Excited Industrial Systems. Conference in Decision and Control.
-
-    // constraint 1: assume D=0 --> remove the components for Dvec (this is
-    // actually a hard constraint in that it ignores D)
-    Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly;
-    sum_QcurlyT_kron_Ncurly =
-        Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_);
-
-    size_t kkk = 0;
-    for (size_t k = 1; k < (n_u_ + 1); k++) {
-      size_t start_idx = k * (n_y_ + n_x_) - n_x_;
-      for (size_t kk = 0; kk < n_x_; kk++) {
-        sum_QcurlyT_kron_Ncurly.col(kkk) =
-            sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk);
-        kkk++;
-      }
-    }
-
-    // constraint 2: Make sure DC I/O gain is correct
-    Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A());
-    Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_));
-    Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0);
-    Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 =
-        join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0);
-
-    // WEIGHTED LS
-    // Important in practice because I care a lot about at least getting the DC
-    // gain correct. Put x weight on minimizing error at DC, relative to others
-    Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows,
-             sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye);
-    // Make weight on minimizing DC error immense so at least that
-    // should be nailed.
-    size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows;
-    size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows;
-    size_t stop_row = w.n_rows - 1;
-    size_t stop_col = w.n_cols - 1;
-    // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale
-    // weight with data length?
-    w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc;
-    Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w *
-                       sum_QcurlyT_kron_Ncurly_b_to_g0) *
-                   sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec;
-
-    fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_));
-
-    // Calculate residuals and their cov.
-    // Because I've added constraints, I need to re-calculate the right term
-    // with b_vec instead of how van Overschee do in final algorithm.
-    err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec;
-  } else {
-    // default way: *no* constraint on G0 or D=0
-    Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec;
-    // TODO(mfbolus) n.b., this gets thrown away...
-    // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_);
-    fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_));
-    err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec;
-  }
-  // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols);
-
-  // TODO(mfbolus): Something is wrong with the error calculation above.
-  // Use the way van overschee does it in `subid.m`
-  // WARNING: this ignores any above constraints, so Q, R will be approximate...
-  Matrix err = Tl - S * Tr;
-  Matrix cov_err = err * err.t();
-  fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1));
-  fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1));
-}
-
-template <typename Fit>
-void SSID<Fit>::RecomputeExtObs() {
-  ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C();
-  for (size_t k = 2; k < (n_h_ + 1); k++) {
-    ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) =
-        ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1,
-                          ext_obs_t_.n_cols - 1) *
-        fit_.A();
-  }
-}
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian_8h/index.html b/docs/docs/api/files/lds__gaussian_8h/index.html deleted file mode 100644 index b8fdc127..00000000 --- a/docs/docs/api/files/lds__gaussian_8h/index.html +++ /dev/null @@ -1,316 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_gaussian.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_gaussian.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_gaussian.h - # -

-

glds namespace More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
-

- Detailed Description - # -

-

This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_GAUSSIAN_H
-#define LDSCTRLEST_LDS_GAUSSIAN_H
-
-// namespace
-#include "lds.h"
-
-namespace lds {
-namespace gaussian {
-// insert any Gaussian-specific things here...
-}  // namespace gaussian
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian__ctrl_8h/index.html b/docs/docs/api/files/lds__gaussian__ctrl_8h/index.html deleted file mode 100644 index 76387c22..00000000 --- a/docs/docs/api/files/lds__gaussian__ctrl_8h/index.html +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_gaussian_ctrl.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_gaussian_ctrl.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_gaussian_ctrl.h - # -

-

GLDS Controller. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::gaussian::Controller
Gaussian-observation Controller Type.
-

- Detailed Description - # -

-

This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H
-#define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H
-
-// namespace
-#include "lds_gaussian.h"
-// system
-#include "lds_gaussian_sys.h"
-// controller
-#include "lds_ctrl.h"
-
-namespace lds {
-namespace gaussian {
-class Controller : public lds::Controller<System> {
- public:
-  void set_y_ref(const Vector& y_ref) override {
-    Reassign(y_ref_,y_ref);
-    cx_ref_ = y_ref - sys_.d();
-  };
-
-  // make sure base class template methods available
-  using lds::Controller<System>::Controller;
-  using lds::Controller<System>::Control;
-  using lds::Controller<System>::ControlOutputReference;
-
-  using lds::Controller<System>::sys;
-  using lds::Controller<System>::Kc;
-  using lds::Controller<System>::Kc_inty;
-  using lds::Controller<System>::Kc_u;
-  using lds::Controller<System>::g_design;
-  using lds::Controller<System>::u_ref;
-  using lds::Controller<System>::x_ref;
-  using lds::Controller<System>::y_ref;
-  using lds::Controller<System>::control_type;
-
-  using lds::Controller<System>::set_sys;
-  using lds::Controller<System>::set_g_design;
-  using lds::Controller<System>::set_u_ref;
-  using lds::Controller<System>::set_x_ref;
-  using lds::Controller<System>::set_y_ref;
-  using lds::Controller<System>::set_Kc;
-  using lds::Controller<System>::set_Kc_inty;
-  using lds::Controller<System>::set_Kc_u;
-  using lds::Controller<System>::set_tau_awu;
-  using lds::Controller<System>::set_control_type;
-
-  using lds::Controller<System>::Reset;
-  using lds::Controller<System>::Print;
-};
-}  // namespace gaussian
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian__fit_8h/index.html b/docs/docs/api/files/lds__gaussian__fit_8h/index.html deleted file mode 100644 index a82159cd..00000000 --- a/docs/docs/api/files/lds__gaussian__fit_8h/index.html +++ /dev/null @@ -1,353 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_gaussian_fit.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_gaussian_fit.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_gaussian_fit.h - # -

-

GLDS fit type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::gaussian::Fit
GLDS Fit Type.
-

- Detailed Description - # -

-

This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H
-#define LDSCTRLEST_LDS_GAUSSIAN_FIT_H
-
-// namespace
-#include "lds_gaussian.h"
-// fit type
-#include "lds_fit.h"
-
-namespace lds {
-namespace gaussian {
-class Fit : public lds::Fit {
- public:
-  Fit() = default;
-
-  Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt);
-  const Matrix& R() const override { return R_; };
-  void set_R(const Matrix& R) override {
-    Reassign(R_, R);
-    ForceSymPD(R_);
-  };
-
-  View h(Matrix& y, const Matrix& x, size_t t) override {
-    y.col(t) = C_ * x.col(t) + d_;
-    return y.col(t);
-  };
-};
-
-};  // namespace gaussian
-}  // namespace lds
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian__fit__em_8h/index.html b/docs/docs/api/files/lds__gaussian__fit__em_8h/index.html deleted file mode 100644 index 42703787..00000000 --- a/docs/docs/api/files/lds__gaussian__fit__em_8h/index.html +++ /dev/null @@ -1,349 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_gaussian_fit_em.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_gaussian_fit_em.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_gaussian_fit_em.h - # -

-

GLDS E-M fit type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::gaussian::FitEM
GLDS E-M Fit Type.
-

- Detailed Description - # -

-

This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).

-

References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).

-

[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H
-#define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H
-
-#include "lds_fit_em.h"
-#include "lds_gaussian_fit.h"
-
-namespace lds {
-namespace gaussian {
-class FitEM : public EM<Fit> {
- public:
-  using EM<Fit>::EM;
-
- private:
-  void MaximizeOutput() override;
-
-  void MaximizeMeasurement() override;
-
-  void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) override;
-};
-
-}  // namespace gaussian
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html b/docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html deleted file mode 100644 index 414efe66..00000000 --- a/docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html +++ /dev/null @@ -1,349 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_gaussian_fit_ssid.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_gaussian_fit_ssid.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - # -

-

GLDS SSID fit type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::gaussian::FitSSID
Subspace Identification (SSID) for GLDS.
-

- Detailed Description - # -

-

This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).

-

References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H
-#define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H
-
-#include "lds_fit_ssid.h"
-#include "lds_gaussian_fit.h"
-
-namespace lds {
-namespace gaussian {
-class FitSSID : public SSID<Fit> {
- public:
-  using SSID<Fit>::SSID;
-  using SSID<Fit>::Run;
-
- private:
-  using SSID<Fit>::CreateHankelDataMat;
-  using SSID<Fit>::CalcSVD;
-  using SSID<Fit>::Solve;
-
-  void DecomposeData() override;
-
-  void SolveVanOverschee();
-};
-}  // namespace gaussian
-}  // namespace lds
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian__sctrl_8h/index.html b/docs/docs/api/files/lds__gaussian__sctrl_8h/index.html deleted file mode 100644 index fd0f2a53..00000000 --- a/docs/docs/api/files/lds__gaussian__sctrl_8h/index.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_gaussian_sctrl.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_gaussian_sctrl.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_gaussian_sctrl.h - # -

-

GLDS switched controller type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::gaussian::SwitchedController
Gaussian-observation SwitchedController Type.
-

- Detailed Description - # -

-

This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H
-#define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H
-
-// controller type
-#include "lds_gaussian_ctrl.h"
-// switched controller
-#include "lds_sctrl.h"
-
-namespace lds {
-namespace gaussian {
-class SwitchedController : public lds::SwitchedController<System> {
- public:
-  void set_y_ref(const Vector& y_ref) override {
-    Reassign(y_ref_, y_ref);
-    cx_ref_ = y_ref - sys_.d();
-  }
-
-  // make sure base class template methods available
-  using lds::SwitchedController<System>::SwitchedController;
-  using lds::SwitchedController<System>::Switch;
-  using lds::SwitchedController<System>::Control;
-  using lds::SwitchedController<System>::ControlOutputReference;
-
-  using lds::SwitchedController<System>::sys;
-  using lds::SwitchedController<System>::Kc;
-  using lds::SwitchedController<System>::Kc_inty;
-  using lds::SwitchedController<System>::Kc_u;
-  using lds::SwitchedController<System>::g_design;
-  using lds::SwitchedController<System>::u_ref;
-  using lds::SwitchedController<System>::x_ref;
-  using lds::SwitchedController<System>::y_ref;
-  using lds::SwitchedController<System>::control_type;
-
-  using lds::SwitchedController<System>::set_g_design;
-  using lds::SwitchedController<System>::set_u_ref;
-  using lds::SwitchedController<System>::set_x_ref;
-  using lds::SwitchedController<System>::set_y_ref;
-  using lds::SwitchedController<System>::set_Kc;
-  using lds::SwitchedController<System>::set_Kc_inty;
-  using lds::SwitchedController<System>::set_Kc_u;
-  using lds::SwitchedController<System>::set_tau_awu;
-  using lds::SwitchedController<System>::set_control_type;
-
-  using lds::SwitchedController<System>::Reset;
-  using lds::SwitchedController<System>::Print;
-};  // SwitchedController
-}  // namespace gaussian
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian__sys_8cpp/index.html b/docs/docs/api/files/lds__gaussian__sys_8cpp/index.html deleted file mode 100644 index ab497625..00000000 --- a/docs/docs/api/files/lds__gaussian__sys_8cpp/index.html +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - - - - - - - - -src/lds_gaussian_sys.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - src/lds_gaussian_sys.cpp - - -
- - - - - - -
- - - -

- src/lds_gaussian_sys.cpp - # -

-

GLDS base type. More…

-

- Detailed Description - # -

-

This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).

-

- Source code - # -

-
//===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst_h/lds_gaussian_sys.h>
-
-lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0,
-                   data_t q0, data_t r0)
-    : lds::System(n_u, n_x, n_y, dt, p0, q0) {
-
-  R_.zeros(n_y, n_y);
-  R_.diag().fill(r0);
-
-  do_recurse_Ke_=true;
-};
-
-// recursively estimate Ke
-void lds::gaussian::System::RecurseKe() {
-  if (!do_recurse_Ke_) {
-    return;
-  }
-
-  // predict covariance
-  P_ = A_ * P_ * A_.t() + Q_;
-
-  // calc Kalman gain
-  Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_);
-
-  // update covariance
-  // Reference: Ghahramani et Hinton (1996)
-  P_ = P_ - Ke_ * C_ * P_;
-
-  if (do_adapt_m) {
-    P_m_ += Q_m_;  // A_m = I (i.e., random walk)
-    Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_);
-    P_m_ = P_m_ - Ke_m_ * C_ * P_m_;
-  }
-}
-
-// Simulate
-const lds::Vector& lds::gaussian::System::Simulate(const Vector& u_tm1){
-  f(u_tm1, true);//simulate dynamics with noise added
-  h();//output
-  z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure
-  return z_;
-}
-
-void lds::gaussian::System::Print() {
-  lds::System::Print();
-  std::cout << "R: \n" << R_ << "\n";
-}
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__gaussian__sys_8h/index.html b/docs/docs/api/files/lds__gaussian__sys_8h/index.html deleted file mode 100644 index 0a4f2d02..00000000 --- a/docs/docs/api/files/lds__gaussian__sys_8h/index.html +++ /dev/null @@ -1,385 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_gaussian_sys.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_gaussian_sys.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_gaussian_sys.h - # -

-

GLDS base type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::gaussian::System
Gaussian LDS Type.
-

- Detailed Description - # -

-

This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H
-#define LDSCTRLEST_LDS_GAUSSIAN_SYS_H
-
-// namespace
-#include "lds_gaussian.h"
-// system
-#include "lds_sys.h"
-
-namespace lds {
-namespace gaussian {
-class System : public lds::System {
- public:
-  System() = default;
-
-  System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt,
-         data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0,
-         data_t r0 = kDefaultR0);
-
-  const Vector& Simulate(const Vector& u_tm1) override;
-
-  // get methods
-  const Matrix& R() const { return R_; };
-
-  // set methods
-  void set_Q(const Matrix& Q) {
-    lds::System::set_Q(Q);
-    do_recurse_Ke_ = true;
-  }
-  void set_R(const Matrix& R) {
-    Reassign(R_,R);
-    do_recurse_Ke_ = true;
-  };
-
-  void set_Ke(const Matrix& Ke) {
-    Reassign(Ke_,Ke);
-    // if users have set Ke, they must not want to calculate it online.
-    do_recurse_Ke_ = false;
-  };
-  void set_Ke_m(const Matrix& Ke_m) {
-    Reassign(Ke_m_,Ke_m);
-    // if users have set Ke, they must not want to calculate it online.
-    do_recurse_Ke_ = false;
-  };
-
-  void Print();
-
- protected:
-  void h() override {
-    cx_ = C_ * x_;
-    y_ = cx_ + d_;
-  };
-
-  void RecurseKe() override;
-
-  // Gaussian-output-specific
-  Matrix R_;           
-  bool do_recurse_Ke_{};  
-};                      // System
-}  // namespace gaussian
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson_8h/index.html b/docs/docs/api/files/lds__poisson_8h/index.html deleted file mode 100644 index 3ca47f14..00000000 --- a/docs/docs/api/files/lds__poisson_8h/index.html +++ /dev/null @@ -1,320 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_poisson.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_poisson.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_poisson.h - # -

-

plds namespace More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Detailed Description - # -

-

This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_POISSON_H
-#define LDSCTRLEST_LDS_POISSON_H
-
-#include "lds.h"
-
-namespace lds {
-namespace poisson {
-// TODO(mfbolus): Not sure if defining these as static here makes the most
-// sense. Is there a downside to letting multiple poisson System objects share a
-// common random number generator?
-static std::random_device rd;  
-static std::mt19937 rng = std::mt19937(
-    rd());  
-}  // namespace poisson
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson__ctrl_8h/index.html b/docs/docs/api/files/lds__poisson__ctrl_8h/index.html deleted file mode 100644 index eef9e81e..00000000 --- a/docs/docs/api/files/lds__poisson__ctrl_8h/index.html +++ /dev/null @@ -1,380 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_poisson_ctrl.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_poisson_ctrl.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_poisson_ctrl.h - # -

-

PLDS controller type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::poisson::Controller
PLDS Controller Type.
-

- Detailed Description - # -

-

This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)), including state estimation.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_POISSON_CTRL_H
-#define LDSCTRLEST_LDS_POISSON_CTRL_H
-
-// namespace
-#include "lds_poisson.h"
-// system type
-#include "lds_poisson_sys.h"
-// control type
-#include "lds_ctrl.h"
-
-namespace lds {
-namespace poisson {
-class Controller : public lds::Controller<System> {
- public:
-  void set_y_ref(const Vector& y_ref) override {
-    Reassign(y_ref_, y_ref);
-    lds::Limit(y_ref_, kYRefLb, lds::kInf);
-    cx_ref_ = log(y_ref_) - sys_.d();
-  };
-
-  // make sure base class template methods available
-  using lds::Controller<System>::Controller;
-  using lds::Controller<System>::Control;
-  using lds::Controller<System>::ControlOutputReference;
-
-  using lds::Controller<System>::sys;
-  using lds::Controller<System>::Kc;
-  using lds::Controller<System>::Kc_inty;
-  using lds::Controller<System>::Kc_u;
-  using lds::Controller<System>::g_design;
-  using lds::Controller<System>::u_ref;
-  using lds::Controller<System>::x_ref;
-  using lds::Controller<System>::y_ref;
-  using lds::Controller<System>::control_type;
-
-  using lds::Controller<System>::set_sys;
-  using lds::Controller<System>::set_g_design;
-  using lds::Controller<System>::set_u_ref;
-  using lds::Controller<System>::set_x_ref;
-  using lds::Controller<System>::set_y_ref;
-  using lds::Controller<System>::set_Kc;
-  using lds::Controller<System>::set_Kc_inty;
-  using lds::Controller<System>::set_Kc_u;
-  using lds::Controller<System>::set_tau_awu;
-  using lds::Controller<System>::set_control_type;
-
-  using lds::Controller<System>::Reset;
-  using lds::Controller<System>::Print;
-
- private:
-  constexpr static const data_t kYRefLb =
-      1e-4;  
-};
-}  // namespace poisson
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson__fit_8h/index.html b/docs/docs/api/files/lds__poisson__fit_8h/index.html deleted file mode 100644 index 44c83d5f..00000000 --- a/docs/docs/api/files/lds__poisson__fit_8h/index.html +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_poisson_fit.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_poisson_fit.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_poisson_fit.h - # -

-

PLDS base fit type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::poisson::Fit
PLDS Fit Type.
-

- Detailed Description - # -

-

This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_POISSON_FIT_H
-#define LDSCTRLEST_LDS_POISSON_FIT_H
-
-// namespace
-#include "lds_poisson.h"
-// fit
-#include "lds_fit.h"
-
-namespace lds {
-namespace poisson {
-class Fit : public lds::Fit {
- public:
-  Fit() = default;
-
-  Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)
-      : lds::Fit(n_u, n_x, n_y, dt){};
-
-  View h(Matrix& y, const Matrix& x, size_t t) override {
-    y.col(t) = exp(C_ * x.col(t) + d_);
-    return y.col(t);
-  };
-
-  void set_R(const Matrix& R) override {
-    std::cerr
-        << "WARNING: Cannot set R (R[0] = " << R.at(0)
-        << "). No Gaussian measurement noise in Poisson observation model.\n";
-  };
-
-  const Matrix& R() const override {
-    return R_;
-  };
-
-};
-
-};  // namespace poisson
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson__fit__em_8h/index.html b/docs/docs/api/files/lds__poisson__fit__em_8h/index.html deleted file mode 100644 index 533a394f..00000000 --- a/docs/docs/api/files/lds__poisson__fit__em_8h/index.html +++ /dev/null @@ -1,354 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_poisson_fit_em.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_poisson_fit_em.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_poisson_fit_em.h - # -

-

PLDS E-M fit type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::poisson::FitEM
PLDS E-M Fit Type.
-

- Detailed Description - # -

-

This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).

-

References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).

-

[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.

-

[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H
-#define LDSCTRLEST_LDS_POISSON_FIT_EM_H
-
-#include "lds_fit_em.h"
-#include "lds_poisson_fit.h"
-
-namespace lds {
-namespace poisson {
-class FitEM : public EM<Fit> {
- public:
-  using EM<Fit>::EM;
-
- private:
-  void MaximizeOutput() override;
-
-  void MaximizeMeasurement() override{};
-
-  void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) override;
-
-  data_t NewtonSolveC();
-
-  void AnalyticalSolveD();
-};
-
-}  // namespace poisson
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html b/docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html deleted file mode 100644 index 83d6dec1..00000000 --- a/docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html +++ /dev/null @@ -1,348 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_poisson_fit_ssid.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_poisson_fit_ssid.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_poisson_fit_ssid.h - # -

-

PLDS SSID fit type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::poisson::FitSSID
Subspace Identification (SSID) for PLDS.
-

- Detailed Description - # -

-

This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)).

-

References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H
-#define LDSCTRLEST_LDS_POISSON_FIT_SSID_H
-
-#include "lds_fit_ssid.h"
-#include "lds_poisson_fit.h"
-
-namespace lds {
-namespace poisson {
-class FitSSID : public SSID<Fit> {
- public:
-  using SSID<Fit>::SSID;
-
- private:
-  void DecomposeData() override;
-
-  void CalcCov();
-
-  void PoissonToGaussianMoments();
-  Matrix cov_;
-};
-
-}  // namespace poisson
-}  // namespace lds
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson__sctrl_8h/index.html b/docs/docs/api/files/lds__poisson__sctrl_8h/index.html deleted file mode 100644 index 0e0a16f8..00000000 --- a/docs/docs/api/files/lds__poisson__sctrl_8h/index.html +++ /dev/null @@ -1,376 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_poisson_sctrl.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_poisson_sctrl.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_poisson_sctrl.h - # -

-

PLDS switched controller type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::poisson::SwitchedController
Poisson-observation SwitchedController Type.
-

- Detailed Description - # -

-

This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H
-#define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H
-
-#include "lds_poisson_ctrl.h"
-#include "lds_sctrl.h"
-
-namespace lds {
-namespace poisson {
-class SwitchedController : public lds::SwitchedController<System> {
- public:
-  void set_y_ref(const Vector& y_ref) override {
-    Reassign(y_ref_,y_ref);
-    lds::Limit(y_ref_, kYRefLB, lds::kInf);
-    cx_ref_ = log(y_ref_) - sys_.d();
-  };
-
-  // make sure base class template methods available
-  using lds::SwitchedController<System>::SwitchedController;
-  using lds::SwitchedController<System>::Switch;
-  using lds::SwitchedController<System>::Control;
-  using lds::SwitchedController<System>::ControlOutputReference;
-
-  using lds::SwitchedController<System>::sys;
-  using lds::SwitchedController<System>::Kc;
-  using lds::SwitchedController<System>::Kc_inty;
-  using lds::SwitchedController<System>::Kc_u;
-  using lds::SwitchedController<System>::g_design;
-  using lds::SwitchedController<System>::u_ref;
-  using lds::SwitchedController<System>::x_ref;
-  using lds::SwitchedController<System>::y_ref;
-  using lds::SwitchedController<System>::control_type;
-
-  using lds::SwitchedController<System>::set_g_design;
-  using lds::SwitchedController<System>::set_u_ref;
-  using lds::SwitchedController<System>::set_x_ref;
-  using lds::SwitchedController<System>::set_y_ref;
-  using lds::SwitchedController<System>::set_Kc;
-  using lds::SwitchedController<System>::set_Kc_inty;
-  using lds::SwitchedController<System>::set_Kc_u;
-  using lds::SwitchedController<System>::set_tau_awu;
-  using lds::SwitchedController<System>::set_control_type;
-
-  using lds::SwitchedController<System>::Reset;
-  using lds::SwitchedController<System>::Print;
-
- private:
-  constexpr static data_t kYRefLB =
-      1e-4;  
-};
-}  // namespace poisson
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson__sys_8cpp/index.html b/docs/docs/api/files/lds__poisson__sys_8cpp/index.html deleted file mode 100644 index 614eef57..00000000 --- a/docs/docs/api/files/lds__poisson__sys_8cpp/index.html +++ /dev/null @@ -1,325 +0,0 @@ - - - - - - - - - - - - - -src/lds_poisson_sys.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - src/lds_poisson_sys.cpp - - -
- - - - - - -
- - - -

- src/lds_poisson_sys.cpp - # -

-

PLDS base type. More…

-

- Detailed Description - # -

-

This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).

-

- Source code - # -

-
//===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst_h/lds_poisson_sys.h>
-
-lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt,
-                             data_t p0, data_t q0)
-    : lds::System(n_u, n_x, n_y, dt, p0, q0) {
-  diag_y_ = diagmat(y_);
-  pd_ = std::poisson_distribution<size_t>(0);
-};
-
-// Correct: Given measurement (z) and current input (u), update estimate of the
-// state, covar, output.
-//
-// see Eden et al. 2004
-void lds::poisson::System::RecurseKe() {
-  // predict covariance
-  P_ = A_ * P_ * A_.t() + Q_;
-
-  // update cov
-  P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_);
-  Ke_ = P_ * C_.t();
-  if (do_adapt_m) {
-    P_m_ += Q_m_;                                     // predict (A_m = I)
-    P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_);  // update
-    Ke_m_ = P_m_ * C_.t();
-  }
-}
-
-// Simulate Measurement: z ~ Poisson(y)
-const lds::Vector& lds::poisson::System::Simulate(const Vector& u_tm1) {
-  f(u_tm1, true);  // simulate dynamics with noise added
-  h();             // output
-
-  z_.zeros();
-  for (std::size_t k = 0; k < n_y_; k++) {
-    // construct a Poisson distribution object with mean y[k]
-    pd_ = std::poisson_distribution<size_t>(y_[k]);
-    // pull random sample from this distribution
-    z_[k] = pd_(rng);
-  }
-
-  return z_;
-}
-// ******************* SYS_T *******************
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__poisson__sys_8h/index.html b/docs/docs/api/files/lds__poisson__sys_8h/index.html deleted file mode 100644 index e1616f02..00000000 --- a/docs/docs/api/files/lds__poisson__sys_8h/index.html +++ /dev/null @@ -1,365 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_poisson_sys.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_poisson_sys.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_poisson_sys.h - # -

-

PLDS base type. More…

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::poisson::System
Poisson System type.
-

- Detailed Description - # -

-

This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_POISSON_SYS_H
-#define LDSCTRLEST_LDS_POISSON_SYS_H
-
-// namespace
-#include "lds_poisson.h"
-// system
-#include "lds_sys.h"
-
-// needed for Poisson random number generation
-#include <random>
-
-namespace lds {
-namespace poisson {
-
-class System : public lds::System {
- public:
-  System() = default;
-
-  System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt,
-         data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0);
-
-  const Vector& Simulate(const Vector& u_tm1) override;
-
- protected:
-  void h() override {
-    cx_ = C_ * x_;
-    y_ = exp(cx_ + d_);
-    diag_y_.diag() = y_;
-  };
-
-  void RecurseKe() override;
-
- private:
-  // Poisson-output-specific
-  Matrix diag_y_;                 
-  std::poisson_distribution<size_t>
-      pd_;  
-};          // System
-}  // namespace poisson
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__sctrl_8h/index.html b/docs/docs/api/files/lds__sctrl_8h/index.html deleted file mode 100644 index d2d11892..00000000 --- a/docs/docs/api/files/lds__sctrl_8h/index.html +++ /dev/null @@ -1,532 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_sctrl.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_sctrl.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_sctrl.h - # -

-

SwitchedController type. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::SwitchedController
SwitchedController Type.
-

- Detailed Description - # -

-

This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_SCTRL_H
-#define LDSCTRLEST_LDS_SCTRL_H
-
-#include "lds_ctrl.h"
-#include "lds_uniform_mats.h"
-#include "lds_uniform_vecs.h"
-
-namespace lds {
-template <typename System>
-class SwitchedController : public Controller<System> {
- public:
-  SwitchedController() = default;
-
-  SwitchedController(const std::vector<System>& systems, data_t u_lb,
-                     data_t u_ub, size_t control_type = 0);
-
-  SwitchedController(std::vector<System>&& systems, data_t u_lb, data_t u_ub,
-                     size_t control_type = 0);
-
-  void Switch(size_t idx, bool do_force_switch = false);
-
-  void set_Kc(const UniformMatrixList<>& Kc) {
-    Kc_list_ = Kc;
-    Kc_ = Kc_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-  void set_Kc(UniformMatrixList<>&& Kc) {
-    Kc_list_ = std::move(Kc);
-    Kc_ = Kc_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-
-  void set_Kc_inty(const UniformMatrixList<>& Kc_inty) {
-    Kc_inty_list_ = Kc_inty;
-    Kc_inty_ = Kc_inty_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-  void set_Kc_inty(UniformMatrixList<>&& Kc_inty) {
-    Kc_inty_list_ = std::move(Kc_inty);
-    Kc_inty_ = Kc_inty_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-
-  void set_Kc_u(const UniformMatrixList<>& Kc_u) {
-    Kc_u_list_ = Kc_u;
-    Kc_u_ = Kc_u_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-  void set_Kc_u(UniformMatrixList<>&& Kc_u) {
-    Kc_u_list_ = std::move(Kc_u);
-    Kc_u_ = Kc_u_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-
-  void set_g_design(const UniformVectorList& g) {
-    g_design_list_ = g;
-    g_design_ = g_design_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-  void set_g_design(UniformVectorList&& g) {
-    g_design_list_ = std::move(g);
-    g_design_ = g_design_list_.at(0);  // set to first
-    if (idx_ != 0) {
-      Switch(idx_, true);
-    }
-  };
-
-  // make sure base class template methods available
-  using lds::Controller<System>::Controller;
-  using lds::Controller<System>::Control;
-  using lds::Controller<System>::ControlOutputReference;
-
-  using lds::Controller<System>::sys;
-  using lds::Controller<System>::Kc;
-  using lds::Controller<System>::Kc_inty;
-  using lds::Controller<System>::Kc_u;
-  using lds::Controller<System>::g_design;
-  using lds::Controller<System>::u_ref;
-  using lds::Controller<System>::x_ref;
-  using lds::Controller<System>::y_ref;
-  using lds::Controller<System>::control_type;
-
-  using lds::Controller<System>::set_u_ref;
-  using lds::Controller<System>::set_x_ref;
-  using lds::Controller<System>::set_y_ref;
-  using lds::Controller<System>::set_tau_awu;
-  using lds::Controller<System>::set_control_type;
-
-  using lds::Controller<System>::Reset;
-  using lds::Controller<System>::Print;
-
- protected:
-  std::vector<System>
-      systems_;     
-  size_t n_sys_{};  
-  size_t idx_{};    
-
-  // controller gains could be different for each
-  UniformMatrixList<> Kc_list_;
-  UniformMatrixList<> Kc_inty_list_;
-  UniformMatrixList<> Kc_u_list_;
-
-  // design-phase input gain could also be different
-  UniformVectorList g_design_list_;
-
-  // TODO(mfbolus): not sure why I need to do this.
-  using Controller<System>::Kc_;
-  using Controller<System>::Kc_inty_;
-  using Controller<System>::Kc_u_;
-  using Controller<System>::g_design_;
-  using Controller<System>::sys_;
-  // using Controller<System>::u_ref_;
-  // using Controller<System>::x_ref_;
-  // using Controller<System>::y_ref_;
-  //
-  using Controller<System>::control_type_;
-
- private:
-  void InitVars();
-
-  using lds::Controller<System>::set_sys;
-  // using Controller<System>::set_Kc;
-  // using Controller<System>::set_Kc_inty;
-  // using Controller<System>::set_Kc_u;
-  // using Controller<System>::set_g_design;
-};
-
-template <typename System>
-inline SwitchedController<System>::SwitchedController(
-    const std::vector<System>& systems, data_t u_lb, data_t u_ub,
-    size_t control_type)
-    : Controller<System>(systems.at(0), u_lb, u_ub, control_type),
-      systems_(systems) {
-  InitVars();
-}
-
-template <typename System>
-inline SwitchedController<System>::SwitchedController(
-    std::vector<System>&& systems, data_t u_lb, data_t u_ub,
-    size_t control_type)
-    : Controller<System>(System(systems.at(0).n_u(), systems.at(0).n_x(),
-                                systems.at(0).n_y(), systems.at(0).dt()),
-                         u_lb, u_ub, control_type),
-      systems_(std::move(systems)) {
-  InitVars();
-}
-
-template <typename System>
-inline void SwitchedController<System>::InitVars() {
-  n_sys_ = systems_.size();
-  sys_ = systems_.at(0);
-
-  Kc_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_));
-  Kc_inty_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_inty_));
-  Kc_u_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_inty_));
-  g_design_list_ = UniformVectorList(std::vector<Vector>(n_sys_, g_design_));
-}
-
-template <typename System>
-inline void SwitchedController<System>::Switch(size_t idx,
-                                               bool do_force_switch) {
-  if ((idx == idx_) && !do_force_switch) {
-    return;  // already there.
-  }
-
-  // put old up and get new one out
-  systems_.at(idx_) = std::move(sys_);
-  sys_ = std::move(systems_.at(idx));
-
-  // set the state of this system to that of the previous system
-  // TODO(mfbolus): This will only work as intended if state matrix is the same.
-  // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp.
-  sys_.set_m(systems_.at(idx_).m(), true);
-  sys_.set_x(systems_.at(idx_).x());
-
-  // swap controller gains
-  Kc_list_.Swap(Kc_, idx_);
-  Kc_list_.Swap(Kc_, idx);
-
-  if (control_type_ & kControlTypeIntY) {
-    Kc_inty_list_.Swap(Kc_inty_, idx_);
-    Kc_inty_list_.Swap(Kc_inty_, idx);
-  }
-
-  if (control_type_ & kControlTypeDeltaU) {
-    Kc_u_list_.Swap(Kc_u_, idx_);
-    Kc_u_list_.Swap(Kc_u_, idx);
-  }
-
-  g_design_list_.Swap(g_design_, idx_);
-  g_design_list_.Swap(g_design_, idx);
-
-  idx_ = idx;
-}  // Switch
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__sys_8cpp/index.html b/docs/docs/api/files/lds__sys_8cpp/index.html deleted file mode 100644 index 46c7a742..00000000 --- a/docs/docs/api/files/lds__sys_8cpp/index.html +++ /dev/null @@ -1,371 +0,0 @@ - - - - - - - - - - - - - -src/lds_sys.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - src/lds_sys.cpp - - -
- - - - - - -
- - - -

- src/lds_sys.cpp - # -

-

LDS base type. More…

-

- Detailed Description - # -

-

This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.

-

- Source code - # -

-
//===-- lds_sys.cpp - LDS -------------------------------------------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst_h/lds_sys.h>
-
-lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0,
-                    data_t q0)
-    : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) {
-  InitVars(p0, q0);
-}
-
-void lds::System::InitVars(data_t p0, data_t q0) {
-  // initial conditions.
-  x0_ = Vector(n_x_, fill::zeros);  // includes bias (nY) and g (nU)
-  P0_ = p0 * Matrix(n_x_, n_x_, fill::eye);
-
-  m0_ = x0_;
-  P0_m_ = P0_;
-
-  // signals
-  x_ = x0_;
-  P_ = P0_;
-  m_ = m0_;
-  P_m_ = P0_m_;
-  y_ = Vector(n_y_, fill::zeros);
-  cx_ = Vector(n_y_, fill::zeros);
-  z_ = Vector(n_y_, fill::zeros);
-
-  // By default, random walk where each state is independent
-  // In this way, provides independent estimates of rate per channel of output.
-  A_ = Matrix(n_x_, n_x_, fill::eye);
-  B_ = Matrix(n_x_, n_u_, fill::zeros);
-  g_ = Vector(n_u_, fill::ones);
-  Q_ = q0 * Matrix(n_x_, n_x_, fill::eye);
-  Q_m_ = Q_;
-
-  C_ = Matrix(n_y_, n_x_, fill::eye);  // each state will map to an output by
-  d_ = Vector(n_y_, fill::zeros);
-
-  Ke_ = Matrix(n_x_, n_y_, fill::zeros);    // estimator gain.
-  Ke_m_ = Matrix(n_x_, n_y_, fill::zeros);  // estimator gain for m adaptation.
-
-  do_adapt_m = false;
-}
-
-// Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state
-// and update estimate of the state, covar, output using Kalman filter
-void lds::System::Filter(const Vector& u_tm1, const Vector& z_t) {
-  // predict mean
-  f(u_tm1);  // dynamics
-
-  h();  // output
-
-  // recursively calculate esimator gains (or just keep existing values)
-  // (also predicts+updates estimate covariance)
-  RecurseKe();
-
-  // update
-  x_ += Ke_ * (z_t - y_);
-  if (do_adapt_m) {
-    m_ += Ke_m_ * (z_t - y_);  // adaptively estimating disturbance
-  }
-
-  // With new state, estimate output.
-  h();  // --> posterior
-}
-
-void lds::System::Reset() {
-  // reset to initial conditions
-  x_ = x0_;      // mean
-  P_ = P0_;      // cov of state estimate
-  m_ = m0_;      // process disturbance
-  P_m_ = P0_m_;  // cov of disturbance estimate
-  h();
-}
-
-void lds::System::Print() {
-  std::cout << "\n ********** SYSTEM ********** \n";
-  std::cout << "x: \n" << x_ << "\n";
-  std::cout << "P: \n" << P_ << "\n";
-  std::cout << "A: \n" << A_ << "\n";
-  std::cout << "B: \n" << B_ << "\n";
-  std::cout << "g: \n" << g_ << "\n";
-  std::cout << "m: \n" << m_ << "\n";
-  std::cout << "Q: \n" << Q_ << "\n";
-  std::cout << "Q_m: \n" << Q_m_ << "\n";
-  std::cout << "d: \n" << d_ << "\n";
-  std::cout << "C: \n" << C_ << "\n";
-  std::cout << "y: \n" << y_ << "\n";
-}
-
-//******************* SYS_T *******************
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__sys_8h/index.html b/docs/docs/api/files/lds__sys_8h/index.html deleted file mode 100644 index 2014f587..00000000 --- a/docs/docs/api/files/lds__sys_8h/index.html +++ /dev/null @@ -1,441 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_sys.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_sys.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_sys.h - # -

-

LDS base type. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::System
Linear Dynamical System Type.
-

- Detailed Description - # -

-

This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// Limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_SYS_H
-#define LDSCTRLEST_LDS_SYS_H
-
-#include "lds.h"
-
-namespace lds {
-class System {
- public:
-  System() = default;
-
-  System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0,
-         data_t q0 = kDefaultQ0);
-
-  virtual ~System() {}
-
-  void Filter(const Vector& u_tm1, const Vector& z);
-
-  virtual const Vector& Simulate(const Vector& u_tm1) = 0;
-
-  void f(const Vector& u, bool do_add_noise = false) {
-    x_ = A_ * x_ + B_ * (g_ % u) + m_;
-    if (do_add_noise) {
-      x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_);
-    }
-  };
-
-  virtual void h() = 0;
-
-  size_t n_u() const { return n_u_; };
-  size_t n_x() const { return n_x_; };
-  size_t n_y() const { return n_y_; };
-  data_t dt() const { return dt_; };
-
-  const Vector& x() const { return x_; };
-  const Matrix& P() const { return P_; };
-  const Vector& m() const { return m_; };
-  const Matrix& P_m() const { return P_m_; };
-  const Vector& cx() const { return cx_; };
-  const Vector& y() const { return y_; };
-
-  const Vector& x0() const { return x0_; };
-  const Vector& m0() const { return m0_; };
-
-  const Matrix& A() const { return A_; };
-  const Matrix& B() const { return B_; };
-  const Vector& g() const { return g_; };
-  const Matrix& C() const { return C_; };
-  const Vector& d() const { return d_; };
-  const Matrix& Ke() const { return Ke_; };
-  const Matrix& Ke_m() const { return Ke_m_; };
-  const Matrix& Q() { return Q_; };
-  const Matrix& Q_m() { return Q_m_; };
-  const Matrix& P0() { return P0_; };
-  const Matrix& P0_m() { return P0_m_; };
-
-  void set_A(const Matrix& A) { Reassign(A_, A); };
-  void set_B(const Matrix& B) { Reassign(B_, B); };
-  void set_m(const Vector& m, bool do_force_assign=false) {
-    Reassign(m0_, m);
-    if ((!do_adapt_m) || do_force_assign) {
-      Reassign(m_, m);
-    }
-  };
-  void set_g(const Vector& g) { Reassign(g_, g); };
-  void set_Q(const Matrix& Q) { Reassign(Q_, Q); };
-  void set_Q_m(const Matrix& Q_m) { Reassign(Q_m_, Q_m); };
-  void set_x0(const Vector& x0) { Reassign(x0_, x0); };
-  void set_P0(const Matrix& P0) { Reassign(P0_, P0); };
-  void set_P0_m(const Matrix& P0_m) { Reassign(P0_m_, P0_m); };
-  void set_C(const Matrix& C) { Reassign(C_, C); };
-  void set_d(const Vector& d) { Reassign(d_, d); };
-  void set_x(const Vector& x) {
-    Reassign(x_, x);
-    h();
-  };
-
-  void Reset();
-
-  void Print();
-
-  // safe to leave this public and non-const
-  bool do_adapt_m{};  
-
- protected:
-  virtual void RecurseKe() = 0;
-  void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0);
-
-  std::size_t n_x_{};  
-  std::size_t n_u_{};  
-  std::size_t n_y_{};  
-  data_t dt_{};        
-
-  // Signals:
-  Vector x_;    
-  Matrix P_;    
-  Vector m_;    
-  Matrix P_m_;  
-  Vector cx_;   
-  Vector y_;    
-  Vector z_;    
-
-  // Parameters:
-  Vector x0_;    
-  Matrix P0_;    
-  Vector m0_;    
-  Matrix P0_m_;  
-  Matrix A_;     
-  Matrix B_;     
-  Vector g_;     
-  Matrix Q_;     
-  Matrix Q_m_;   
-  Matrix C_;     
-  Vector d_;     
-
-  Matrix Ke_;    
-  Matrix Ke_m_;  
-};               // System
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__uniform__mats_8h/index.html b/docs/docs/api/files/lds__uniform__mats_8h/index.html deleted file mode 100644 index fc67352f..00000000 --- a/docs/docs/api/files/lds__uniform__mats_8h/index.html +++ /dev/null @@ -1,587 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_uniform_mats.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_uniform_mats.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_uniform_mats.h - # -

-

List of uniformly sized matrices. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::UniformMatrixList
-

- Detailed Description - # -

-

This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// Limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H
-#define LDSCTRLEST_LDS_UNIFORM_MATS_H
-
-#include <array>   // std::array
-#include <vector>  // std::vector
-
-#include "lds.h"
-
-namespace lds {
-template <MatrixListFreeDim D = kMatFreeDimNone>
-class UniformMatrixList : public std::vector<Matrix> {
- private:
-  // TODO(mfbolus): would rather *uncomment* the below for sake of conversion
-  // using std::vector<Matrix>::vector;
-  using std::vector<Matrix>::operator=;
-  using std::vector<Matrix>::operator[];
-  using std::vector<Matrix>::begin;
-  using std::vector<Matrix>::end;
-  using std::vector<Matrix>::size;
-
- public:
-  using std::vector<Matrix>::at;
-  UniformMatrixList() = default;
-
-  explicit UniformMatrixList(const std::vector<Matrix>& mats,
-                             std::array<size_t, 2> dim = {0, 0});
-
-  explicit UniformMatrixList(std::vector<Matrix>&& mats,
-                             std::array<size_t, 2> dim = {0, 0});
-
-  UniformMatrixList(std::initializer_list<Matrix> mats,
-                    std::array<size_t, 2> dim = {0, 0});
-
-  UniformMatrixList(const UniformMatrixList<D>& that);
-
-  UniformMatrixList(UniformMatrixList<D>&& that) noexcept;
-
-  ~UniformMatrixList() = default;
-
-  const std::array<size_t, 2>& dim(size_t n = 0) const { return dim_.at(n); }
-
-  size_t size() { return std::vector<Matrix>::size(); };
-
-  const Matrix& at(size_t n) { return std::vector<Matrix>::at(n); };
-
-  void Swap(Matrix& that, size_t n);
-
-  UniformMatrixList<D>& operator=(const UniformMatrixList<D>& that);
-  UniformMatrixList<D>& operator=(UniformMatrixList<D>&& that) noexcept;
-
- private:
-  void CheckDimensions(std::array<size_t, 2> dim);
-  std::vector<std::array<size_t, 2>> dim_;
-};
-
-template <MatrixListFreeDim D>
-inline void UniformMatrixList<D>::Swap(Matrix& that, size_t n) {
-  // make sure request in range
-  if (n >= this->size()) {
-    std::cerr
-        << "Requested UniformMatrixList element out of bounds. Skipping.\n";
-    return;
-  }
-  // check dim
-  bool does_match = true;
-  if (!(D == kMatFreeDim1)) {
-    does_match = does_match && (dim_[0][0] == that.n_rows);
-  }
-  if (!(D == kMatFreeDim2)) {
-    does_match = does_match && (dim_[0][1] == that.n_cols);
-  }
-  if (!does_match) {
-    std::cerr << "Cannot swap a UniformMatrixList element for an element of "
-                 "different size. Skipping.\n";
-    return;
-  }
-  // if checks pass, perform swap
-  Matrix tmp = std::move((*this)[n]);
-  (*this)[n] = std::move(that);
-  that = std::move(tmp);
-
-  if (D == kMatFreeDim1) {
-    this->dim_[n][0] = (*this)[n].n_rows;
-  }
-  if (D == kMatFreeDim2) {
-    this->dim_[n][1] = (*this)[n].n_cols;
-  }
-}
-
-template <MatrixListFreeDim D>
-inline UniformMatrixList<D>& UniformMatrixList<D>::operator=(
-    const UniformMatrixList<D>& that) {
-  // make sure dim_ vector is initialized
-  if (dim_.empty()) {
-    dim_ = std::vector<std::array<size_t, 2>>(that.size(), {0, 0});
-  }
-  // check dimensions
-  if (!this->empty()) {
-    if (this->size() != that.size()) {
-      std::ostringstream ss;
-      ss << "cannot reassign " << this->size() << " matrices with "
-         << that.size() << " matrices";
-      throw std::runtime_error(ss.str());
-    }
-
-    // if dimensions a not zero and do not match, skip move with error message.
-    bool dims_nonzero = true;
-    for (auto d : dim_) {
-      if (!(D == kMatFreeDim1) && d[0] < 1) {
-        dims_nonzero = false;
-        break;
-      }
-      if (!(D == kMatFreeDim2) && d[1] < 1) {
-        dims_nonzero = false;
-        break;
-      }
-    }
-    if (dims_nonzero) {
-      bool does_match = true;
-      if (!(D == kMatFreeDim1)) {
-        does_match = does_match && (dim_[0][0] == that.at(0).n_rows);
-      }
-      if (!(D == kMatFreeDim2)) {
-        does_match = does_match && (dim_[0][1] == that.at(0).n_cols);
-      }
-      if (!does_match) {
-        std::ostringstream ss;
-        ss << "cannot reassign matrices of size " << dim_[0][0] << "x"
-           << dim_[0][1] << " with matrices of size " << that.at(0).n_rows
-           << "x" << that.at(0).n_cols;
-        throw std::runtime_error(ss.str());
-      }
-    }
-  }
-
-  for (size_t k = 0; k < this->size(); k++) {
-    (*this)[k] = that[k];
-    dim_[k] = that.dim(k);
-  }
-
-  return (*this);
-}
-
-template <MatrixListFreeDim D>
-inline UniformMatrixList<D>& UniformMatrixList<D>::operator=(
-    UniformMatrixList<D>&& that) noexcept {
-  // // check dimensions
-  // // if empty, assume a default constructed object and safe to move
-  // if (!this->empty()) {
-  //   if (this->size() != that.size()) {
-  //     std::cerr << "Cannot reassign " << this->size() << " matrices with "
-  //               << that.size() << " matrices. Skipping.\n";
-  //     return (*this);
-  //   }
-  //
-  //   // if dimensions a not zero and do not match, skip move with error
-  //   message. bool dims_nonzero = true; for (auto d : dim_) {
-  //     if (!(D == kMatFreeDim1) && (d[0] < 1)) {
-  //       dims_nonzero = false;
-  //       break;
-  //     }
-  //     if (!(D == kMatFreeDim2) && (d[1] < 1)) {
-  //       dims_nonzero = false;
-  //       break;
-  //     }
-  //   }
-  //
-  //   if (dims_nonzero) {
-  //     bool does_match = true;
-  //     if (!(D == kMatFreeDim1)) {
-  //       does_match = does_match && (dim_[0][0] == that.at(0).n_rows);
-  //     }
-  //
-  //     if (!(D == kMatFreeDim2)) {
-  //       does_match = does_match && (dim_[0][1] == that.at(0).n_cols);
-  //     }
-  //
-  //     if (!does_match) {
-  //       this->at(0).print("this[0] = ");
-  //       that.at(0).print("that[0] = ");
-  //       std::cerr
-  //           << "Cannot move a UniformMatrixList element of size (" <<
-  //           that.at(0).n_rows << "," << that.at(0).n_cols << ") for an
-  //           element of size (" << dim_[0][0] << "," << dim_[0][1] << ").
-  //           Skipping.\n";
-  //       return (*this);
-  //     }
-  //   }
-  // }
-
-  dim_ = that.dim_;
-  std::vector<Matrix>::operator=(std::move(that));
-
-  return (*this);
-}
-
-template <MatrixListFreeDim D>
-UniformMatrixList<D>::UniformMatrixList(const std::vector<Matrix>& mats,
-                                        std::array<size_t, 2> dim)
-    : vector(mats) {
-  CheckDimensions(dim);
-}
-
-template <MatrixListFreeDim D>
-UniformMatrixList<D>::UniformMatrixList(std::vector<Matrix>&& mats,
-                                        std::array<size_t, 2> dim)
-    : vector(std::move(mats)) {
-  CheckDimensions(dim);
-};
-
-template <MatrixListFreeDim D>
-UniformMatrixList<D>::UniformMatrixList(std::initializer_list<Matrix> mats,
-                                        std::array<size_t, 2> dim)
-    : vector(mats) {
-  CheckDimensions(dim);
-};
-
-template <MatrixListFreeDim D>
-UniformMatrixList<D>::UniformMatrixList(const UniformMatrixList<D>& that)
-    : vector(that) {
-  (*this) = that;
-}
-
-template <MatrixListFreeDim D>
-UniformMatrixList<D>::UniformMatrixList(UniformMatrixList<D>&& that) noexcept
-    : vector(std::move(that)) {
-  for (size_t k = 0; k < this->size(); k++) {
-    std::array<size_t, 2> dim_k({this->at(k).n_rows, this->at(k).n_cols});
-    dim_.push_back(dim_k);
-  }
-}
-
-template <MatrixListFreeDim D>
-void UniformMatrixList<D>::CheckDimensions(std::array<size_t, 2> dim) {
-  // change behavior based on free dim D
-  if ((dim[0] == 0) && !(D == kMatFreeDim1)) {
-    dim[0] = this->at(0).n_rows;
-  }
-  if ((dim[1] == 0) && !(D == kMatFreeDim2)) {
-    dim[1] = this->at(0).n_cols;
-  }
-
-  // make sure dimensiolaties are all uniform
-  bool does_match(true);
-  for (const Matrix& mat : *this) {
-    if (!(D == kMatFreeDim1)) {
-      does_match = does_match && (mat.n_rows == dim[0]);
-    }
-    if (!(D == kMatFreeDim2)) {
-      does_match = does_match && (mat.n_cols == dim[1]);
-    }
-    if (!does_match) {
-      throw std::runtime_error(
-          "Dimensionality of one or more input matrices are not uniform.");
-    }
-  }
-
-  dim_ = std::vector<std::array<size_t, 2>>(this->size(), dim);
-  for (size_t k = 0; k < this->size(); k++) {
-    dim_[k][0] = (*this)[k].n_rows;
-    dim_[k][1] = (*this)[k].n_cols;
-  }
-}
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__uniform__systems_8h/index.html b/docs/docs/api/files/lds__uniform__systems_8h/index.html deleted file mode 100644 index 52dcbf2b..00000000 --- a/docs/docs/api/files/lds__uniform__systems_8h/index.html +++ /dev/null @@ -1,525 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_uniform_systems.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_uniform_systems.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_uniform_systems.h - # -

-

List of uniformly sized Systems. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::UniformSystemList
-

- Detailed Description - # -

-

This file provides a container for uniformly sized Systems.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// Limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H
-#define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H
-
-#include <array>   // std::array
-#include <vector>  // std::vector
-
-// namespace
-#include "lds.h"
-// System type
-#include "lds_sys.h"
-
-namespace lds {
-template <typename System>
-class UniformSystemList : public std::vector<System> {
-  static_assert(std::is_base_of<lds::System, System>::value,
-                "System must be derived from lds::System type.");
-
- private:
-  // TODO(mfbolus): would rather *uncomment* the below for sake of conversion
-  // using std::vector<System>::vector;
-  using std::vector<System>::operator=;
-  using std::vector<System>::operator[];
-  using std::vector<System>::at;
-  using std::vector<System>::begin;
-  using std::vector<System>::end;
-  using std::vector<System>::size;
-
- public:
-  UniformSystemList() = default;
-
-  explicit UniformSystemList(const std::vector<System>& systems,
-                             std::array<size_t, 3> dim = {0, 0, 0});
-
-  explicit UniformSystemList(std::vector<System>&& systems,
-                             std::array<size_t, 3> dim = {0, 0, 0});
-
-  UniformSystemList(std::initializer_list<System> systems,
-                    std::array<size_t, 3> dim = {0, 0, 0});
-
-  UniformSystemList(const UniformSystemList& that);
-  UniformSystemList(UniformSystemList&& that) noexcept;
-
-  ~UniformSystemList() = default;
-
-  const std::array<size_t, 3>& dim() const { return dim_; }
-
-  size_t size() { return std::vector<System>::size(); };
-
-  const System& at(size_t n) { return std::vector<System>::at(n); };
-
-  void Swap(System& that, size_t n);
-
-  UniformSystemList& operator=(const UniformSystemList& that);
-  UniformSystemList& operator=(UniformSystemList&& that) noexcept;
-
- private:
-  void CheckDimensions(std::array<size_t, 3> dim);
-  std::array<size_t, 3> dim_{};
-};
-
-template <typename System>
-inline void UniformSystemList<System>::Swap(System& that, size_t n) {
-  // make sure request in range
-  if (n >= this->size()) {
-    std::cerr
-        << "Requested UniformSystemList element out of bounds. Skipping.\n";
-    return;
-  }
-  // check dim
-  bool does_match = (dim_[0] == that.n_u()) && (dim_[1] == that.n_x()) &&
-                    (dim_[2] == that.n_y());
-  if (!does_match) {
-    std::cerr << "Cannot swap a UniformSystemList element for an element of "
-                 "different size. Skipping.\n";
-    return;
-  }
-  // if checks pass, perform swap
-  System tmp = std::move((*this)[n]);
-  (*this)[n] = std::move(that);
-  that = std::move(tmp);
-}
-
-template <typename System>
-inline UniformSystemList<System>& UniformSystemList<System>::operator=(
-    const UniformSystemList& that) {
-  // check dimensions
-  if (!this->empty()) {
-    if (this->size() != that.size()) {
-      std::ostringstream ss;
-      ss << "cannot reassign " << this->size() << " systems with "
-         << that.size() << " systems";
-      throw std::runtime_error(ss.str());
-    }
-
-    if (dim_[0] + dim_[1] + dim_[2]) {
-      std::array<size_t, 3> other_dim(that.dim());
-      if (dim_ != other_dim) {
-        std::ostringstream ss;
-        ss << "cannot reassign systems of size " << dim_[0] << "x" << dim_[1]
-           << "x" << dim_[2] << " with systems of size " << other_dim[0] << "x"
-           << other_dim[1] << "x" << dim_[2];
-        throw std::runtime_error(ss.str());
-      }
-    }
-  }
-
-  for (size_t k = 0; k < this->size(); k++) {
-    (*this)[k] = that[k];
-  }
-  return (*this);
-}
-
-template <typename System>
-inline UniformSystemList<System>& UniformSystemList<System>::operator=(
-    UniformSystemList&& that) noexcept {
-  // // check dimensions
-  // // if empty, assume a default constructed object and safe to move
-  // if (!this->empty()) {
-  //   if (this->size() != that.size()) {
-  //     std::cerr << "Cannot reassign " << this->size() << " systems with "
-  //               << that.size() << " systems. Skipping.\n";
-  //     return (*this);
-  //   }
-  //
-  //   // if dimensions a not zero and do not match, skip move with error
-  //   message. if (dim_[0] + dim_[1] + dim_[2]) {
-  //     bool does_match = (dim_[0] == that.at(0).n_u()) &&
-  //                       (dim_[1] == that.at(0).n_x()) &&
-  //                       (dim_[2] == that.at(0).n_y());
-  //     if (!does_match) {
-  //       std::cerr
-  //           << "Cannot move a UniformSystemList element for an element of "
-  //              "different size. Skipping.\n";
-  //       return (*this);
-  //     }
-  //   }
-  // }
-
-  dim_ = that.dim_;
-  std::vector<System>::operator=(std::move(that));
-
-  return (*this);
-}
-
-template <typename System>
-UniformSystemList<System>::UniformSystemList(const std::vector<System>& systems,
-                                             std::array<size_t, 3> dim)
-    : std::vector<System>(systems) {
-  CheckDimensions(dim);
-}
-
-template <typename System>
-UniformSystemList<System>::UniformSystemList(std::vector<System>&& systems,
-                                             std::array<size_t, 3> dim)
-    : std::vector<System>(std::move(systems)) {
-  CheckDimensions(dim);
-};
-
-template <typename System>
-UniformSystemList<System>::UniformSystemList(
-    std::initializer_list<System> systems, std::array<size_t, 3> dim)
-    : std::vector<System>(systems) {
-  CheckDimensions(dim);
-};
-
-template <typename System>
-UniformSystemList<System>::UniformSystemList(const UniformSystemList& that)
-    : std::vector<System>(that) {
-  (*this) = that;
-}
-
-template <typename System>
-UniformSystemList<System>::UniformSystemList(UniformSystemList&& that) noexcept
-    : std::vector<System>(std::move(that)) {
-  this->dim_[0] = this->at(0).n_u();
-  this->dim_[1] = this->at(0).n_x();
-  this->dim_[2] = this->at(0).n_y();
-}
-
-template <typename System>
-void UniformSystemList<System>::CheckDimensions(std::array<size_t, 3> dim) {
-  if (dim[0] + dim[1] + dim[2]) {
-    dim_ = dim;
-  } else {
-    dim_[0] = this->at(0).n_u();
-    dim_[1] = this->at(0).n_x();
-    dim_[2] = this->at(0).n_y();
-  }
-
-  // make sure dimensiolaties are all uniform
-  bool does_match(true);
-  for (const System& sys : *this) {
-    does_match = does_match && (sys.n_u() == dim_[0]);
-    does_match = does_match && (sys.n_x() == dim_[1]);
-    does_match = does_match && (sys.n_y() == dim_[2]);
-    if (!does_match) {
-      throw std::runtime_error(
-          "Dimensionality of one or more input systems are not uniform.");
-    }
-  }
-}
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__uniform__vecs_8cpp/index.html b/docs/docs/api/files/lds__uniform__vecs_8cpp/index.html deleted file mode 100644 index bf57154e..00000000 --- a/docs/docs/api/files/lds__uniform__vecs_8cpp/index.html +++ /dev/null @@ -1,350 +0,0 @@ - - - - - - - - - - - - - -src/lds_uniform_vecs.cpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - src/lds_uniform_vecs.cpp - - -
- - - - - - -
- - - -

- src/lds_uniform_vecs.cpp - # -

-

Uniformly sized vectors. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Detailed Description - # -

-

This file provides a container for uniformly sized vectors.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// Limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#include <ldsCtrlEst_h/lds_uniform_vecs.h>
-
-namespace lds {
-
-UniformVectorList::UniformVectorList(const std::vector<Vector>& vecs,
-                                     size_t dim)
-    : vector(vecs) {
-  CheckDimensions(dim);
-}
-
-UniformVectorList::UniformVectorList(std::vector<Vector>&& vecs, size_t dim)
-    : vector(std::move(vecs)) {
-  CheckDimensions(dim);
-};
-
-UniformVectorList::UniformVectorList(std::initializer_list<Vector> vecs,
-                                     size_t dim)
-    : vector(vecs) {
-  CheckDimensions(dim);
-};
-
-UniformVectorList::UniformVectorList(const UniformVectorList& that)
-    : vector(that) {
-  (*this) = that;
-}
-
-UniformVectorList::UniformVectorList(UniformVectorList&& that) noexcept
-    : vector(std::move(that)) {
-  this->dim_ = this->at(0).n_elem;
-}
-
-void UniformVectorList::CheckDimensions(size_t dim) {
-  if (dim) {
-    dim_ = dim;
-  } else {
-    dim_ = this->at(0).n_elem;
-  }
-
-  // make sure dimensiolaties are all uniform
-  bool does_match(true);
-  for (const Vector& vec : *this) {
-    does_match = does_match && (vec.n_elem == dim_);
-    if (!does_match) {
-      throw std::runtime_error(
-          "Dimensionality of one or more input matrices are not uniform.");
-    }
-  }
-}
-
-}  // namespace lds
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/lds__uniform__vecs_8h/index.html b/docs/docs/api/files/lds__uniform__vecs_8h/index.html deleted file mode 100644 index b9c17b0b..00000000 --- a/docs/docs/api/files/lds__uniform__vecs_8h/index.html +++ /dev/null @@ -1,449 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/lds_uniform_vecs.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/lds_uniform_vecs.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/lds_uniform_vecs.h - # -

-

List of uniformly sized vectors. More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
lds
Linear Dynamical Systems (LDS) namespace.
-

- Classes - # -

- - - - - - - - - - - - - -
Name
classlds::UniformVectorList
-

- Detailed Description - # -

-

This file provides a container for uniformly sized vectors.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// Limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H
-#define LDSCTRLEST_LDS_UNIFORM_VECS_H
-
-#include <array>   // std::array
-#include <vector>  // std::vector
-
-#include "lds.h"
-
-namespace lds {
-
-class UniformVectorList : public std::vector<Vector> {
- private:
-  // TODO(mfbolus): would rather *uncomment* the below for sake of conversion
-  // using std::vector<Vector>::vector;
-  using std::vector<Vector>::operator=;
-  using std::vector<Vector>::operator[];
-  using std::vector<Vector>::at;
-  using std::vector<Vector>::begin;
-  using std::vector<Vector>::end;
-  using std::vector<Vector>::size;
-
- public:
-  UniformVectorList() = default;
-
-  explicit UniformVectorList(const std::vector<Vector>& vecs, size_t dim = 0);
-
-  explicit UniformVectorList(std::vector<Vector>&& vecs, size_t dim = 0);
-
-  UniformVectorList(std::initializer_list<Vector> vecs, size_t dim = 0);
-
-  UniformVectorList(const UniformVectorList& that);
-  UniformVectorList(UniformVectorList&& that) noexcept;
-  ~UniformVectorList() = default;
-
-  size_t dim() const { return dim_; }
-
-  size_t size() { return std::vector<Vector>::size(); };
-
-  const Vector& at(size_t n) { return std::vector<Vector>::at(n); };
-
-  void Swap(Vector& that, size_t n);
-
-  UniformVectorList& operator=(const UniformVectorList& that);
-  UniformVectorList& operator=(UniformVectorList&& that) noexcept;
-
- private:
-  void CheckDimensions(size_t dim);
-  size_t dim_{};
-};
-
-inline void UniformVectorList::Swap(Vector& that, size_t n) {
-  // make sure request in range
-  if (n >= this->size()) {
-    std::cerr
-        << "Requested UniformMatrixList element out of bounds. Skipping.\n";
-    return;
-  }
-  // check dim
-  bool does_match = dim_ == that.n_elem;
-  if (!does_match) {
-    std::cerr << "Cannot swap a UniformMatrixList element for an element of "
-                 "different size. Skipping.\n";
-    return;
-  }
-  // if checks pass, perform swap
-  Vector tmp = std::move((*this)[n]);
-  (*this)[n] = std::move(that);
-  that = std::move(tmp);
-}
-
-inline UniformVectorList& UniformVectorList::operator=(
-    const UniformVectorList& that) {
-  // check dimensions
-  if (!this->empty()) {
-    if (this->size() != that.size()) {
-      std::ostringstream ss;
-      ss << "cannot reassign " << this->size() << " vectors with "
-         << that.size() << " vectors";
-      throw std::runtime_error(ss.str());
-    }
-
-    if (dim_) {
-      size_t other_dim(that.dim());
-      if (dim_ != other_dim) {
-        std::ostringstream ss;
-        ss << "cannot reassign vectors of size " << dim_
-           << " with vectors of size " << other_dim;
-        throw std::runtime_error(ss.str());
-      }
-    }
-  }
-
-  for (size_t k = 0; k < this->size(); k++) {
-    (*this)[k] = that[k];
-  }
-
-  return (*this);
-}
-
-inline UniformVectorList& UniformVectorList::operator=(
-    UniformVectorList&& that) noexcept {
-  // // check dimensions
-  // if (!this->empty()) {
-  //   if (this->size() != that.size()) {
-  //     std::cerr << "Cannot reassign " << this->size() << " vectors with "
-  //               << that.size() << " vectors. Skipping.\n";
-  //     return (*this);
-  //   }
-  //
-  //   if (dim_) {
-  //     size_t other_dim(that.dim());
-  //     if (dim_ != other_dim) {
-  //       std::cerr << "Cannot reassign vectors of size " << dim_
-  //                 << " with matrices of size " << other_dim << ".
-  //                 Skipping.\n";
-  //       return (*this);
-  //     }
-  //   }
-  // }
-
-  dim_ = that.dim_;
-  std::vector<Vector>::operator=(std::move(that));
-
-  return (*this);
-}
-
-}  // namespace lds
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/mex__c__util_8h/index.html b/docs/docs/api/files/mex__c__util_8h/index.html deleted file mode 100644 index 99b4c192..00000000 --- a/docs/docs/api/files/mex__c__util_8h/index.html +++ /dev/null @@ -1,375 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/mex_c_util.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/mex_c_util.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/mex_c_util.h - # -

-

arma <-> mex interoperability utilities (Matlab C API) More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
armamexc
arma/mex interface using Matlab C API
-

- Detailed Description - # -

-

This file defines utility functions for interoperability between armadillo and Matlab/Octave’s C mex API.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_MEXC_UTIL_H
-#define LDSCTRLEST_MEXC_UTIL_H
-
-#include <ldsCtrlEst>
-
-#include "mex.h"
-
-// // If Matlab_FOUND, include matrix.h.
-// // (Octave does not need/have it.)
-// #ifdef Matlab_FOUND
-// #include "matrix.h"
-// #endif
-
-namespace armamexc {
-template <class T>
-inline auto m2T_scalar(const mxArray *matlab_scalar) -> T {
-  if (mxGetData(matlab_scalar)) {
-    return static_cast<T>(mxGetScalar(matlab_scalar));
-  }
-  mexErrMsgTxt("No data available.");
-  return 0;
-}
-
-template <class T>
-inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false,
-                    bool strict = true) -> arma::Mat<T> {
-  if (mxGetData(matlab_mat)) {
-    const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat);
-    if (n_dim == 2) {
-      return arma::Mat<T>(static_cast<T *>(mxGetData(matlab_mat)),
-                          mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem,
-                          strict);
-    }
-    mexErrMsgTxt("Number of dimensions must be 2.");
-    return arma::Mat<T>();
-  }
-  mexErrMsgTxt("No data available.");
-  return arma::Mat<T>();
-}
-
-// TODO(mfbolus): make these templated.
-
-template <typename T>
-inline auto a2m_mat(arma::Mat<T> const &arma_mat) -> mxArray * {
-  mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols,
-                                              mxDOUBLE_CLASS, mxREAL);
-  if (matlab_mat) {
-    auto *dst_pointer = static_cast<T *>(mxGetData(matlab_mat));
-    const auto *src_pointer = const_cast<T *>(arma_mat.memptr());
-    // TODO(mfbolus): I just want to MOVE the data, not copy.
-    std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem);
-    return matlab_mat;
-  }
-  mexErrMsgTxt("Failed to create matlab mat from arma::Mat.");
-  return nullptr;
-}
-
-template <typename T>
-inline auto a2m_vec(arma::Col<T> const &arma_vec) -> mxArray * {
-  mxArray *matlab_mat =
-      mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL);
-  if (matlab_mat) {
-    auto *dst_pointer = static_cast<T *>(mxGetData(matlab_mat));
-    const auto *src_pointer = const_cast<T *>(arma_vec.memptr());
-    // TODO(mfbolus): I just want to MOVE the data, not copy.
-    std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem);
-    return matlab_mat;
-  }
-  mexErrMsgTxt("Failed to create matlab mat from arma::Col.");
-  return nullptr;
-}
-
-}  // namespace armamexc
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/files/mex__cpp__util_8h/index.html b/docs/docs/api/files/mex__cpp__util_8h/index.html deleted file mode 100644 index 35f91009..00000000 --- a/docs/docs/api/files/mex__cpp__util_8h/index.html +++ /dev/null @@ -1,396 +0,0 @@ - - - - - - - - - - - - - -ldsCtrlEst_h/mex_cpp_util.h | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - ldsCtrlEst_h/mex_cpp_util.h - - -
- - - - - - -
- - - -

- ldsCtrlEst_h/mex_cpp_util.h - # -

-

arma <-> mex interoperability utilities (Matlab C++ API) More…

-

- Namespaces - # -

- - - - - - - - - - - -
Name
armamexcpp
arma/mex interface using Matlab C++ API
-

- Detailed Description - # -

-

This file defines utility functions for interoperability between armadillo and Matlab’s C++ mex API.

-

- Source code - # -

-
//===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===//
-//
-// Copyright 2021 Michael Bolus
-// Copyright 2021 Georgia Institute of Technology
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//===----------------------------------------------------------------------===//
-//===----------------------------------------------------------------------===//
-
-#ifndef LDSCTRLEST_MEXCPP_UTIL_H
-#define LDSCTRLEST_MEXCPP_UTIL_H
-
-#include <ldsCtrlEst>
-
-#include "mex.hpp"
-#include "mexAdapter.hpp"
-
-namespace armamexcpp {
-template <class T>
-std::vector<arma::Mat<T>> m2a_cellmat(matlab::data::CellArray& matlab_cell) {
-  size_t n_cells = matlab_cell.getNumberOfElements();
-  std::vector<arma::Mat<T>> arma_mat(n_cells,
-                                     arma::Mat<T>(1, 1, arma::fill::zeros));
-  for (size_t k = 0; k < n_cells; k++) {
-    matlab::data::TypedArray<T> matlab_mat = matlab_cell[k];
-    auto dims = matlab_mat.getDimensions();
-    arma_mat[k] = arma::Mat<T>(matlab_mat.release().get(), dims[0], dims[1]);
-  }
-  return arma_mat;
-};
-
-template <class T>
-std::vector<T> m2s_vec(matlab::data::TypedArray<T>& matlab_array) {
-  size_t n_elem = matlab_array.getNumberOfElements();
-  T* ptr = matlab_array.release().get();
-  std::vector<T> vec(ptr, ptr + n_elem);
-  return vec;
-};
-
-template <class T>
-arma::Col<T> m2a_vec(matlab::data::TypedArray<T> matlab_array) {
-  size_t n_elem = matlab_array.getNumberOfElements();
-  // T* ptr = matlab_array.release().get();
-  // arma::Col<T> vec(ptr, n_elem);  //, false);
-  // TODO(mfbolus): for some reason, using the above pointer at times leads to
-  // getting garbage values. matlab array values may be stored in non-contiguous
-  // memory?
-  arma::Col<T> vec(n_elem, arma::fill::zeros);
-  for (size_t k = 0; k < n_elem; k++) {
-    vec[k] = matlab_array[k];
-  }
-  return vec;
-};
-
-template <class T>
-arma::Mat<T> m2a_mat(matlab::data::TypedArray<T> matlab_array) {
-  // ArrayDimensions == std::vector<size_t>
-  auto dims = matlab_array.getDimensions();
-  // T* ptr = matlab_array.release().get();
-  // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false)
-  // arma::Mat<T> mat(ptr, dims[0], dims[1]);  //, false);
-
-  // TODO(mfbolus): for some reason, using the above pointer at times leads to
-  // getting garbage values. matlab array values may be stored in non-contiguous
-  // memory?
-  //
-  // armadillo and matlab both use column-major ordering, so this should work:
-  size_t n_elem = dims[0] * dims[1];
-  arma::Mat<T> mat(dims[0], dims[1], arma::fill::zeros);
-  size_t k(0);
-  for (auto m: matlab_array) {
-    mat[k] = m;
-    k++;
-  }
-  return mat;
-};
-
-template <class T>
-matlab::data::TypedArray<T> a2m_mat(const arma::Mat<T>& arma_mat,
-                                    matlab::data::ArrayFactory& factory) {
-  const matlab::data::TypedArray<T> matlab_mat = factory.createArray<T>(
-      {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(),
-      arma_mat.memptr() + arma_mat.n_elem);
-  return matlab_mat;
-};
-
-template <class T>
-matlab::data::TypedArray<T> a2m_vec(const arma::Col<T>& arma_vec,
-                                    matlab::data::ArrayFactory& factory) {
-  const matlab::data::TypedArray<T> matlab_mat =
-      factory.createArray<T>({arma_vec.n_elem, 1}, arma_vec.memptr(),
-                             arma_vec.memptr() + arma_vec.n_elem);
-  return matlab_mat;
-};
-
-template <class T>
-matlab::data::TypedArray<T> s2m_vec(const std::vector<T>& std_vec,
-                                    matlab::data::ArrayFactory& factory) {
-  const matlab::data::TypedArray<T> matlab_mat = factory.createArray<T>(
-      {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size());
-  return matlab_mat;
-};
-}  // namespace armamexcpp
-
-#endif
-

-

Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/modules/group__control__masks/index.html b/docs/docs/api/modules/group__control__masks/index.html deleted file mode 100644 index a9d1027c..00000000 --- a/docs/docs/api/modules/group__control__masks/index.html +++ /dev/null @@ -1,321 +0,0 @@ - - - - - - - - - - - - - -Control Mode Bit Masks | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - Control Mode Bit Masks - - -
- - - - - - -
- - - -

- Control Mode Bit Masks - # -

-

provides fill types for constructing new armadillo vectors, matrices

More… -

-

- Attributes - # -

- - - - - - - - - - - - - - - - - - - - - -
Name
const std::size_tkControlTypeDeltaU
control designed to penalize change in input
const std::size_tkControlTypeIntY
control using integral action
const std::size_tkControlTypeAdaptM
adapt control setpoint with re-estimated disturbance m
-

- Detailed Description - # -

-

Control mode bit masks. These can be bit-wise OR’d to use in combination.

-

- Attribute Details - # -

-

- kControlTypeDeltaU - # -

-
static const std::size_t kControlTypeDeltaU = 0x1;
-

Control was designed to penalize change in input (i.e., the state was augmented with input u)

-

- kControlTypeIntY - # -

-
static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1;
-

Control using integral action (i.e., the state was augmented with output y during design)

-

- kControlTypeAdaptM - # -

-
static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2;
-

Adapt control setpoint adapted with re-estimated process disturbance m.

-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/modules/group__defaults/index.html b/docs/docs/api/modules/group__defaults/index.html deleted file mode 100644 index 7424ab00..00000000 --- a/docs/docs/api/modules/group__defaults/index.html +++ /dev/null @@ -1,322 +0,0 @@ - - - - - - - - - - - - - -Defaults | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - Defaults - - -
- - - - - - -
- - - -

- Defaults - # -

-



More… -

-

- Attributes - # -

- - - - - - - - - - - - - - - - - - - - - -
Name
const data_tkDefaultP0
default state estimate covar
const data_tkDefaultQ0
default process noise covar
const data_tkDefaultR0
default output noise covar
-

- Detailed Description - # -

-

Default values for common variables (e.g., default diagonal elements of covariances)

-

- Attribute Details - # -

-

- kDefaultP0 - # -

-
static const data_t kDefaultP0 = 1e-6;
-

- kDefaultQ0 - # -

-
static const data_t kDefaultQ0 = 1e-6;
-

- kDefaultR0 - # -

-
static const data_t kDefaultR0 = 1e-2;
-

-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/modules/index.html b/docs/docs/api/modules/index.html deleted file mode 100644 index 63b2f28b..00000000 --- a/docs/docs/api/modules/index.html +++ /dev/null @@ -1,251 +0,0 @@ - - - - - - - - - - - -Modules | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - Modules - - -
- - - - - - -
- - - -

- Modules - # -

- -
-

Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/modules/index.xml b/docs/docs/api/modules/index.xml deleted file mode 100644 index d30dddf3..00000000 --- a/docs/docs/api/modules/index.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - Modules on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/ - Recent content in Modules on LDS C&E - Hugo -- gohugo.io - - Control Mode Bit Masks - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/ - provides fill types for constructing new armadillo vectors, matrices - - - - Defaults - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/ - Defaults # -More&hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances) -Attribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time - - - - diff --git a/docs/docs/api/namespaces/index.html b/docs/docs/api/namespaces/index.html deleted file mode 100644 index 40b028ec..00000000 --- a/docs/docs/api/namespaces/index.html +++ /dev/null @@ -1,259 +0,0 @@ - - - - - - - - - - - -Namespaces | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - Namespaces - - -
- - - - - - -
- - - -

- Namespaces - # -

-
    -
  • -

    armamexc
    arma/mex interface using Matlab C API

    -
  • -
  • -

    armamexcpp
    arma/mex interface using Matlab C++ API

    -
  • -
  • -

    lds::gaussian
    Linear Dynamical Systems with Gaussian observations.

    -
  • -
  • -

    lds::poisson
    Linear Dynamical Systems with Poisson observations.

    -
  • -
-
-

Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/namespaces/index.xml b/docs/docs/api/namespaces/index.xml deleted file mode 100644 index c804d933..00000000 --- a/docs/docs/api/namespaces/index.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - Namespaces on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/ - Recent content in Namespaces on LDS C&E - Hugo -- gohugo.io - - armamexc - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/ - arma/mex interface using Matlab C API - - - - armamexcpp - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/ - arma/mex interface using Matlab C++ API - - - - lds - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/ - Linear Dynamical Systems (LDS) namespace. - - - - lds::gaussian - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/ - Linear Dynamical Systems with Gaussian observations. - - - - lds::poisson - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/ - Linear Dynamical Systems with Poisson observations. - - - - diff --git a/docs/docs/api/namespaces/namespacearmamexc/index.html b/docs/docs/api/namespaces/namespacearmamexc/index.html deleted file mode 100644 index b9f07594..00000000 --- a/docs/docs/api/namespaces/namespacearmamexc/index.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - - - - - - - - - -armamexc | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - armamexc - - -
- - - - - - -
- - - -

- armamexc - # -

-

arma/mex interface using Matlab C API

More… -

-

- Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - -
Name
template <class T >
auto
m2T_scalar(const mxArray * matlab_scalar)
Convert Matlab mxArray to scalar of type T.
template <class T >
auto
m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)
Convert matlab matrix to armadillo.
template <typename T >
auto
a2m_mat(arma::Mat< T > const & arma_mat)
Convert armadillo to matlab matrix.
template <typename T >
auto
a2m_vec(arma::Col< T > const & arma_vec)
Convert armadillo to matlab vector.
-

- Detailed Description - # -

-

Utilities for arma/mex interface using Matlab C API

-

- Function Details - # -

-

- m2T_scalar - # -

-
template <class T >
-inline auto m2T_scalar(
-    const mxArray * matlab_scalar
-)
-

Parameters:

-
    -
  • matlab_scalar matlab scalar
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: scalar of type T

-

- m2a_mat - # -

-
template <class T >
-inline auto m2a_mat(
-    const mxArray * matlab_mat,
-    bool copy_aux_mem =false,
-    bool strict =true
-)
-

Parameters:

-
    -
  • matlab_mat matlab matrix
  • -
  • copy_aux_mem [optional] whether to copy auxiliary memory
  • -
  • strict [optional] strictly enforce the above
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: armadillo matrix of type T

-

- a2m_mat - # -

-
template <typename T >
-inline auto a2m_mat(
-    arma::Mat< T > const & arma_mat
-)
-

Parameters:

-
    -
  • arma_mat armadillo matrix
  • -
-

Return: matlab matrix

-

- a2m_vec - # -

-
template <typename T >
-inline auto a2m_vec(
-    arma::Col< T > const & arma_vec
-)
-

Parameters:

-
    -
  • arma_vec armadillo vector
  • -
-

Return: matlab vector

-
-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/namespaces/namespacearmamexcpp/index.html b/docs/docs/api/namespaces/namespacearmamexcpp/index.html deleted file mode 100644 index a40c54b2..00000000 --- a/docs/docs/api/namespaces/namespacearmamexcpp/index.html +++ /dev/null @@ -1,452 +0,0 @@ - - - - - - - - - - - - - -armamexcpp | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - armamexcpp - - -
- - - - - - -
- - - -

- armamexcpp - # -

-

arma/mex interface using Matlab C++ API

More… -

-

- Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
template <class T >
std::vector< arma::Mat< T > >
m2a_cellmat(matlab::data::CellArray & matlab_cell)
Convert matlab cell array to vector of armadillo matrices.
template <class T >
std::vector< T >
m2s_vec(matlab::data::TypedArray< T > & matlab_array)
Convert matlab matrix to a vector of scalars.
template <class T >
arma::Col< T >
m2a_vec(matlab::data::TypedArray< T > matlab_array)
Convert matlab to armadillo vector.
template <class T >
arma::Mat< T >
m2a_mat(matlab::data::TypedArray< T > matlab_array)
Convert matlab to armadillo matrix.
template <class T >
matlab::data::TypedArray< T >
a2m_mat(const arma::Mat< T > & arma_mat, matlab::data::ArrayFactory & factory)
Convert armadillo to matlab matrix.
template <class T >
matlab::data::TypedArray< T >
a2m_vec(const arma::Col< T > & arma_vec, matlab::data::ArrayFactory & factory)
Convert armadillo to matlab vector.
template <class T >
matlab::data::TypedArray< T >
s2m_vec(const std::vector< T > & std_vec, matlab::data::ArrayFactory & factory)
Convert vector of scalar T to matlab matrix.
-

- Detailed Description - # -

-

utilities for arma/mex interface using Matlab C++ API

-

- Function Details - # -

-

- m2a_cellmat - # -

-
template <class T >
-std::vector< arma::Mat< T > > m2a_cellmat(
-    matlab::data::CellArray & matlab_cell
-)
-

Parameters:

-
    -
  • matlab_cell matlab cell
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: vector of armadillo matrices of type T

-

- m2s_vec - # -

-
template <class T >
-std::vector< T > m2s_vec(
-    matlab::data::TypedArray< T > & matlab_array
-)
-

Parameters:

-
    -
  • matlab_array matlab array
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: vector of type T

-

- m2a_vec - # -

-
template <class T >
-arma::Col< T > m2a_vec(
-    matlab::data::TypedArray< T > matlab_array
-)
-

Parameters:

-
    -
  • matlab_array matlab array
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: armadillo vector of type T

-

- m2a_mat - # -

-
template <class T >
-arma::Mat< T > m2a_mat(
-    matlab::data::TypedArray< T > matlab_array
-)
-

Parameters:

-
    -
  • matlab_array matlab matrix
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: armadillo matrix of type T

-

- a2m_mat - # -

-
template <class T >
-matlab::data::TypedArray< T > a2m_mat(
-    const arma::Mat< T > & arma_mat,
-    matlab::data::ArrayFactory & factory
-)
-

Parameters:

-
    -
  • arma_mat arma matrix
  • -
  • factory matlab “array factory”
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: matlab matrix

-

- a2m_vec - # -

-
template <class T >
-matlab::data::TypedArray< T > a2m_vec(
-    const arma::Col< T > & arma_vec,
-    matlab::data::ArrayFactory & factory
-)
-

Parameters:

-
    -
  • arma_vec armadillo vector
  • -
  • factory matlab “array factory”
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: matlab matrix

-

- s2m_vec - # -

-
template <class T >
-matlab::data::TypedArray< T > s2m_vec(
-    const std::vector< T > & std_vec,
-    matlab::data::ArrayFactory & factory
-)
-

Parameters:

-
    -
  • std_vec standard vector
  • -
  • factory matlab “array factory”
  • -
-

Template Parameters:

-
    -
  • T type
  • -
-

Return: matlab matrix

-
-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/namespaces/namespacelds/index.html b/docs/docs/api/namespaces/namespacelds/index.html deleted file mode 100644 index 8898c13b..00000000 --- a/docs/docs/api/namespaces/namespacelds/index.html +++ /dev/null @@ -1,792 +0,0 @@ - - - - - - - - - - - - - -lds | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds - - -
- - - - - - -
- - - -

- lds - # -

-

Linear Dynamical Systems (LDS) namespace.

-

- Namespaces - # -

- - - - - - - - - - - - - - -
Name
lds::gaussian
Linear Dynamical Systems with Gaussian observations.
lds::poisson
Linear Dynamical Systems with Poisson observations.
-

- Classes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
classlds::Controller
classlds::EM
classlds::Fit
LDS Fit Type.
classlds::SSID
classlds::SwitchedController
SwitchedController Type.
classlds::System
Linear Dynamical System Type.
classlds::UniformMatrixList
classlds::UniformSystemList
classlds::UniformVectorList
-

- Types - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
enumSSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}
weighting options for SSID
enumMatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2}
using doubledata_t
using arma::Col< data_t >Vector
using arma::Mat< data_t >Matrix
using arma::Cube< data_t >Cube
using arma::subview< data_t >View
-

- Functions - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
voidLimit(std::vector< data_t > & x, data_t lb, data_t ub)
voidLimit(Vector & x, data_t lb, data_t ub)
voidLimit(Matrix & x, data_t lb, data_t ub)
voidReassign(Vector & some, const Vector & other, const std::string & parenthetical =“Reassign”)
reassigns contents of some Vector in place
voidReassign(Matrix & some, const Matrix & other, const std::string & parenthetical =“Reassign”)
reassigns contents of some Matrix in place
voidForceSymPD(Matrix & X)
forces matrix to be symmetric positive-definite
voidForceSymMinEig(Matrix & X, data_t eig_min =0)
forces matrix to be symmetric and have a minimum eigenvalue
voidlq(Matrix & L, Matrix & Qt, const Matrix & X)
LQ decomposition.
MatrixcalcCov(const Matrix & A, const Matrix & B)
Calculate covariance matrix.
-

- Attributes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
const std::size_tkControlTypeDeltaU
control designed to penalize change in input
const std::size_tkControlTypeIntY
control using integral action
const std::size_tkControlTypeAdaptM
adapt control setpoint with re-estimated disturbance m
const data_tkInf
Some useful numbers.
const data_tkPi
const data_tkDefaultP0
default state estimate covar
const data_tkDefaultQ0
default process noise covar
const data_tkDefaultR0
default output noise covar
-

- Type Details - # -

-

- SSIDWt - # -

- - - - - - - - - - - - - - - - - - - - - - - - - -
EnumeratorValueDescription
kSSIDNoneNone.
kSSIDMOESPMOESP (AKA “robust method” in van Overschee 1996)
kSSIDCVACVA “Canonical Variate Analysis”.
-

Weighting options for singular value decomposition performed during subspace identification (SSID)

-

Reference:

-

van Overschee, de Moor. 1996. Subspace Identification for Linear Systems.

-

- MatrixListFreeDim - # -

- - - - - - - - - - - - - - - - - - - - - - - - - -
EnumeratorValueDescription
kMatFreeDimNoneneither dim free to be hetero in mat list
kMatFreeDim1allow 1st dim of mats in list to be hetero
kMatFreeDim2allow 2nd dim of mats in list to be hetero
-

- data_t - # -

-
using lds::data_t = typedef double;
-

Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.

-

- Vector - # -

-
using lds::Vector = typedef arma::Col<data_t>;
-

- Matrix - # -

-
using lds::Matrix = typedef arma::Mat<data_t>;
-

- Cube - # -

-
using lds::Cube = typedef arma::Cube<data_t>;
-

- View - # -

-
using lds::View = typedef arma::subview<data_t>;
-

- Function Details - # -

-

- Limit - # -

-
inline void Limit(
-    std::vector< data_t > & x,
-    data_t lb,
-    data_t ub
-)
-

- Limit - # -

-
inline void Limit(
-    Vector & x,
-    data_t lb,
-    data_t ub
-)
-

- Limit - # -

-
inline void Limit(
-    Matrix & x,
-    data_t lb,
-    data_t ub
-)
-

- Reassign - # -

-
inline void Reassign(
-    Vector & some,
-    const Vector & other,
-    const std::string & parenthetical ="Reassign"
-)
-

Parameters:

-
    -
  • some some Vector
  • -
  • other other Vector
  • -
  • parenthetical optional description provided by caller to ease debugging
  • -
-

- Reassign - # -

-
inline void Reassign(
-    Matrix & some,
-    const Matrix & other,
-    const std::string & parenthetical ="Reassign"
-)
-

Parameters:

-
    -
  • some some Matrix
  • -
  • other other Matrix
  • -
  • parenthetical optional description provided by caller to ease debugging
  • -
-

- ForceSymPD - # -

-
void ForceSymPD(
-    Matrix & X
-)
-

Parameters:

-
    -
  • X mutated matrix
  • -
-

- ForceSymMinEig - # -

-
void ForceSymMinEig(
-    Matrix & X,
-    data_t eig_min =0
-)
-

Parameters:

-
    -
  • X mutated matrix
  • -
  • eig_min [optional] minimum eigen value
  • -
-

- lq - # -

-
void lq(
-    Matrix & L,
-    Matrix & Qt,
-    const Matrix & X
-)
-

Parameters:

-
    -
  • L lower triangle matrix
  • -
  • Qt orthonormal matrix (transposed cf QR decomp)
  • -
  • X matrix being decomposed
  • -
-

- calcCov - # -

-
Matrix calcCov(
-    const Matrix & A,
-    const Matrix & B
-)
-

Parameters:

-
    -
  • A some matrix
  • -
  • B some other matrix
  • -
-

Return: covariance

-

- Attribute Details - # -

-

- kControlTypeDeltaU - # -

-
static const std::size_t kControlTypeDeltaU = 0x1;
-

Control was designed to penalize change in input (i.e., the state was augmented with input u)

-

- kControlTypeIntY - # -

-
static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1;
-

Control using integral action (i.e., the state was augmented with output y during design)

-

- kControlTypeAdaptM - # -

-
static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2;
-

Adapt control setpoint adapted with re-estimated process disturbance m.

-

- kInf - # -

-
static const data_t kInf = std::numeric_limits<data_t>::infinity();
-

- kPi - # -

-
static const data_t kPi = arma::datum::pi;
-

- kDefaultP0 - # -

-
static const data_t kDefaultP0 = 1e-6;
-

- kDefaultQ0 - # -

-
static const data_t kDefaultQ0 = 1e-6;
-

- kDefaultR0 - # -

-
static const data_t kDefaultR0 = 1e-2;
-

-

Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html b/docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html deleted file mode 100644 index 600c834f..00000000 --- a/docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html +++ /dev/null @@ -1,289 +0,0 @@ - - - - - - - - - - - - - -lds::gaussian | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::gaussian - - -
- - - - - - -
- - - -

- lds::gaussian - # -

-

Linear Dynamical Systems with Gaussian observations.

-

- Classes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
classlds::gaussian::Controller
Gaussian-observation Controller Type.
classlds::gaussian::Fit
GLDS Fit Type.
classlds::gaussian::FitEM
GLDS E-M Fit Type.
classlds::gaussian::FitSSID
Subspace Identification (SSID) for GLDS.
classlds::gaussian::SwitchedController
Gaussian-observation SwitchedController Type.
classlds::gaussian::System
Gaussian LDS Type.
-
-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/namespaces/namespacelds_1_1poisson/index.html b/docs/docs/api/namespaces/namespacelds_1_1poisson/index.html deleted file mode 100644 index 59f41e03..00000000 --- a/docs/docs/api/namespaces/namespacelds_1_1poisson/index.html +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - - - - - - - -lds::poisson | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - lds::poisson - - -
- - - - - - -
- - - -

- lds::poisson - # -

-

Linear Dynamical Systems with Poisson observations.

-

- Classes - # -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Name
classlds::poisson::Controller
PLDS Controller Type.
classlds::poisson::Fit
PLDS Fit Type.
classlds::poisson::FitEM
PLDS E-M Fit Type.
classlds::poisson::FitSSID
Subspace Identification (SSID) for PLDS.
classlds::poisson::SwitchedController
Poisson-observation SwitchedController Type.
classlds::poisson::System
Poisson System type.
-

- Attributes - # -

- - - - - - - - - - - - - - - - - -
Name
std::random_devicerd
random device for simulating poisson data
std::mt19937rng
random number generator for simulating poisson data
-

- Attribute Details - # -

-

- rd - # -

-
static std::random_device rd;
-

- rng - # -

-
static std::mt19937 rng = std::mt19937(
-    rd());
-

-

Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/pages/index.html b/docs/docs/api/pages/index.html deleted file mode 100644 index 5294f06c..00000000 --- a/docs/docs/api/pages/index.html +++ /dev/null @@ -1,241 +0,0 @@ - - - - - - - - - - - -Pages | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - Pages - - -
- - - - - - -
- - - -

- Pages - # -

-
-

Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/api/pages/index.xml b/docs/docs/api/pages/index.xml deleted file mode 100644 index 30916924..00000000 --- a/docs/docs/api/pages/index.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - Pages on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/api/pages/ - Recent content in Pages on LDS C&E - Hugo -- gohugo.io - - diff --git a/docs/docs/getting-started/getting-started/index.html b/docs/docs/getting-started/getting-started/index.html deleted file mode 100644 index 5f3de8bf..00000000 --- a/docs/docs/getting-started/getting-started/index.html +++ /dev/null @@ -1,397 +0,0 @@ - - - - - - - - - - - - - -Getting Started | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - Getting Started - - -
- - - - - - -
- - - -

- Getting Started - # -

-

This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.

-

ldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.

-

- Tested Configurations - # -

-

Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):

-
    -
  • Ubuntu 18.04 with GCC 7.5 compiler
  • -
  • macOS 11 (Big Sur) with Apple Clang 12 compiler
  • -
  • Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler
  • -
-

That being said, if you want to debug a build for a single platform, here are some things you can try:

-
    -
  • Use different compilers (or even different versions of a single compiler)
  • -
  • Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule)
  • -
-

- Mac Pre-requisities - # -

-

Xcode Command Line Tools will get you clang, gcc, make, and git:

-
xcode-select --install
-

Homebrew is “The Missing Package Manager for macOS” which will make installing lots of things easy. Install like this:

-
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
-

You can then use it to install CMake, gfortran, and pkg-config:

-
brew install cmake gfortran pkg-config
-

- Linux Pre-requisites - # -

-

You’ll need Git, CMake, GCC, gfortran, etc.

-
sudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build
-

- Windows Installation - # -

-

Look here for Windows-specific instructions.

-

- Downloading the Library - # -

-

First, clone the repository along with submodules:

-
git clone https://github.com/cloctools/lds-ctrl-est.git 
-cd lds-ctrl-est
-git submodule update --init
-

- Compilation + Installation - # -

-

Now generate the cache and build using your IDE or from the command line as follows.

-
mkdir build && cd build
-cmake ..
-cmake --build .
-

The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.

-

If you want to use vcpkg set up somewhere besides this repo’s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE’s settings.

-

You can verify the build is working by running ctest from the build folder, which runs all the example scripts.

-

- Options - # -

-

This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.

-
    -
  1. LDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree
  2. -
  3. LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation
  4. -
  5. LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use
  6. -
-

n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.

-

Below are example usages of cmake to configure/build the library.

-
    -
  • -

    For basic project build & install

    -
    cd /path/to/repository
    -mkdir build && cd build
    -cmake .. #configure build
    -cmake --build #build the project
    -sudo make install #[optional] installs to default location (OS-specific)
    -
  • -
  • -

    To set the install prefix

    -
    cd /path/to/repository
    -mkdir build && cd build
    -cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location
    -cmake --build #build the project
    -make install #install to /your/install/prefix
    -
  • -
  • -

    To build the bare bones project, excluding fit code and Matlab mex code.

    -
    cd /path/to/repository
    -mkdir build && cd build
    -cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library
    -make #build the project
    -

    n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.

    -
      -
    1. LD_LIBRARY_PATH: search path for dynamically loaded libraries
    2. -
    3. PKG_CONFIG_PATH: search path for pkg-config tool
    4. -
    -
  • -
-

On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.

-

- Python bindings package ldsctrlest - # -

-

With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn’t use an undesired version. That environment needs to have NumPy installed.

-
cmake --build . --target python_modules
-

The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).

-

See python/ldsctrlest/README.md for usage details.

-

Also, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.

-

- Common issues - # -

-
    -
  1. “I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.”
  2. -
-

If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.

-
export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix
-export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix
-
    -
  1. vcpkg fails on configuration
  2. -
-

Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn’t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).

-
    -
  1. -

    Could not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)

    -

    Make sure NumPy is installed in the Python environment you specified. If CMake still can’t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'

    -
  2. -
-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/getting-started/linux-macos/index.html b/docs/docs/getting-started/linux-macos/index.html deleted file mode 100644 index 24c31408..00000000 --- a/docs/docs/getting-started/linux-macos/index.html +++ /dev/null @@ -1,324 +0,0 @@ - - - - - - - - - - - - - -Linux Macos | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - Linux Macos - - -
- - - - - - -
- - - -

- Downloading the Library - # -

-

The source code for this library can be downloaded from stanley-rozell/ldsCtrlEst either by downloading a snapshot or cloning the repository via git.

-
git clone https://github.com/stanley-rozell/ldsCtrlEst.git
-

By default, this would check out the master branch. In most cases, we suggest downloading or checking out the latest release instead.

-
cd /path/to/ldsCtrlEst
-git checkout 0.6
-

- Dependencies - # -

-

Note that the primary dependencies of this project listed below must be installed along with their header files and with CMake config files or pkg-config files. The latter files are used to configure this project’s build. It is strongly encouraged to install the dependencies below using a package manager (e.g., apt, pacman, macports).

-
    -
  • For project configuration, install cmake as well as pkg-config. The latter is optional.
  • -
  • The linear algebra library armadillo is used throughout this repository.
  • -
  • The HDF5 library is used to save output from example test programs.
  • -
  • For use of this library in Matlab executables (mex) on Linux operating systems, you will need OpenBlas, ensuring the static library libopenblas.a is installed. You will also need to install gfortran.
  • -
-

- Compilation + Installation - # -

-

This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.

-
    -
  1. LDSCTRLEST_BUILD_EXAMPLES : [default= ON] whether to build example programs located under examples/ in the source tree
  2. -
  3. LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation
  4. -
  5. LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use
  6. -
-

n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.

-

Below are example usages of cmake/make to configure/build the library.

-
    -
  • -

    For basic project build & install

    -
    cd /path/to/repository
    -mkdir build && cd build
    -cmake .. #configure build
    -make #build the project
    -sudo make install #[optional] installs to default location (OS-specific)
    -
  • -
  • -

    To set the install prefix

    -
    cd /path/to/repository
    -mkdir build && cd build
    -cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location
    -make #build the project
    -make install #install to /your/install/prefix
    -
  • -
  • -

    To build the bare bones project, excluding fit code and Matlab mex code.

    -
    cd /path/to/repository
    -mkdir build && cd build
    -cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library
    -make #build the project
    -

    n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.

    -
      -
    1. LD_LIBRARY_PATH: search path for dynamically loaded libraries
    2. -
    3. PKG_CONFIG_PATH: search path for pkg-config tool
    4. -
    5. CMAKE_PREFIX_PATH: search path of prefix where CMake will look for package config files
    6. -
    -

    e.g., Assuming you set -DCMAKE_INSTALL_PREFIX=/your/install/prefix during project configuration and your login shell uses the ~/.profile startup file, open ~/.profile in a text editor and add …

    -
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/your/install/prefix/lib
    -export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix/lib/pkgconfig
    -export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix
    -
  • -
-

- Common issues - # -

-
    -
  1. “I have installed all the dependencies including gfortran with a package manager as suggested; however, cmake complains it cannot find the gfortran library.”
  2. -
-

When gfortran is installed, its library is usually not installed in a standard location like /usr/lib. gfortran is part of the gcc suite, so their libraries are organized together. e.g., When you install gfortran on Ubuntu using apt, its location is /usr/lib/gcc/x86_64-linux-gnu/9, in the case that gcc version 9 is installed. The build configuration script in ldsCtrlEst is written to add LD_LIBRARY_PATH (Unix) or PATH (Windows) to the CMake library search path on Unix or Windows systems, respectively. Therefore, to fix this issue, simply add the directory in which libgfortran was installed to the OS-appropriate environment variable. Continuing with the Ubuntu example above and assuming a Unix login shell whose startup file is ~/.profile, add the following to the file.

-
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/gcc/x86_64-linux-gnu/9
-
    -
  1. “I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.”
  2. -
-

If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.

-
export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix
-export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix
-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/getting-started/windows/index.html b/docs/docs/getting-started/windows/index.html deleted file mode 100644 index 8449a330..00000000 --- a/docs/docs/getting-started/windows/index.html +++ /dev/null @@ -1,309 +0,0 @@ - - - - - - - - - - - - - -Windows | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - Windows - - -
- - - - - - -
- - - -

- Windows Installation - # -

-

- Windows Pre-requisites - # -

-

Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:

-
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time
-iwr get.scoop.sh | Invoke-Expression
-

Install Git and CMake if you don’t already have them:

-
scoop install git cmake
-

If that didn’t work, follow more detailed instructions here.

-

The easiest way to compile C++ project on Windows is with Visual Studio’s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on “Desktop development with C++.” If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the “Installation details” sidebar under optional features.

-

And the easiest way to use Visual Studio’s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.

-

- Downloading the Library - # -

-

First, clone the repository, either from VS Code or the command line:

-
git clone https://github.com/cloctools/lds-ctrl-est.git 
-cd lds-ctrl-est
-

You’ll need to initialize the submodules from the command line after the repo is cloned:

-
git submodule update --init
-

- Installation - # -

-

When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you’ll be prompted when you configure–else there’s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then “cmake select kit”). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don’t need to build Python bindings.)

-

Follow along with the “Getting Started” instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click “workspace”, then search for “CMake: Configure Args” and enter each of your desired arguments as a separate item.

-

To configure, use Ctrl+Shift+P and search for the “CMake: Configure” command. To build, click the “Build” button on the bottom bar. Then click the “CTest” button to run the example scripts.

-

- Considerations - # -

-

Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don’t want to work on a different machine.

-

Compilation has been successfully tested in VS Code using the following kit, using the “RelWithDebInfo” config:

-
Clang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64)
-

- Troubleshooting - # -

-
    -
  1. The build appears to work, but tests fail with code 0xc0000135 OR “I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.”
  2. -
-

Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn’t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for “Edit the system environment variables”).

-
    -
  1. On Windows, “Generate CMake Cache” step errs because creating symbolic links is not permitted.
  2. -
-

Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -> Administrative Tools -> Local Policies -> User Rights Assignment -> Create Symbolic Links.

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/index.html b/docs/docs/index.html deleted file mode 100644 index b8187f50..00000000 --- a/docs/docs/index.html +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - - - - - -LDS C+E Documentation | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - LDS C+E Documentation - - -
- - - - - - -
- - - -

- LDS Control & Estimation Documentation - # -

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/index.xml b/docs/docs/index.xml deleted file mode 100644 index 72a2c125..00000000 --- a/docs/docs/index.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - - LDS C+E Documentation on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/ - Recent content in LDS C+E Documentation on LDS C&E - Hugo -- gohugo.io - - - https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/ - Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows. -ldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation. -Tested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. - - - - - https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/ - Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this: -Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don&rsquo;t already have them: -scoop install git cmake If that didn&rsquo;t work, follow more detailed instructions here. -The easiest way to compile C++ project on Windows is with Visual Studio&rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e. - - - - C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/ - Control &amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated. - - - - Models - https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/ - Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \[ \mathbf{x}_{t&#43;1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} &#43; \mathbf{B} \mathbf{v}_{t} &#43; \mathbf{m}_{t} &#43; \mathbf{w}_{t} \] \[ \mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right) \] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e. - - - - diff --git a/docs/docs/terminology/control-estimation/index.html b/docs/docs/terminology/control-estimation/index.html deleted file mode 100644 index c297cbec..00000000 --- a/docs/docs/terminology/control-estimation/index.html +++ /dev/null @@ -1,363 +0,0 @@ - - - - - - - - - - - - - -C&E | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - C&E - - -
- - - - - - -
- - - -

- Control & Estimation - # -

-

The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.

-

- State estimation - # -

-

In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by - - - - - \[ -\widehat{\mathbf{x}}_{t|t-1} = \mathbf{A}\widehat{\mathbf{x}}_{t-1|t-1} + \mathbf{B} u_{t-1} + \mathbf{m}_{t-1} \;, \] - -

- - \[ -\widehat{\mathbf{x}}_{t|t} = \widehat{\mathbf{x}}_{t|t-1} + \mathbf{K}^{\rm e}_t \left(\mathbf{z}_t - \widehat{\mathbf{y}}_{t|t-1}\right)\;, \] - - -

where - \( \hat{\left(\cdot\right)}_{t|j} \) - - indicates an estimate at time - \( t \) - - given data up to time - \( j \) - - inclusive, - \( \mathbf{K}^{\rm e} \) - - is the estimator gain, and

- - \[ \widehat{\mathbf{y}}_{t|t-1} = h\left( \widehat{\mathbf{x}}_{t|t-1} \right) \; .\] - - -

In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.

-

In the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.

-

- Adaptive estimation of process disturbance - # -

-

Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.

-

When parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk - - \[ -\mathbf{m}_{t} = \mathbf{m}_{t-1} + \mathbf{w}^m_{t-1} \;, \] - - -where - \( \mathbf{w}^m \sim \mathcal{N}\left(0, \mathbf{Q}_m\right)\) - -. Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.

-

- Control - # -

-

Given the estimated state, the controller updates the inputs to the system according to the following law: - - \[ -\mathbf{u}_{t} = \mathbf{u}^{\rm ref}_t - \mathbf{K}^c_x \left( \widehat{\mathbf{x}}_t - \mathbf{x}^{\rm ref}_t\right)\;, \] - -

-

where - \( \left( \cdot \right)^{\rm ref} \) - - correspond to reference/target signals and - \( \mathbf{K}^c_x \) - - is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.

-

If users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as

- - \[ -\mathbf{u}_{t} = \mathbf{u}^{\rm ref}_t - \mathbf{K}^c_x \left( \widehat{\mathbf{x}}_t - \mathbf{x}^{\rm ref}_t\right) - \mathbf{K}^c_{\rm inty} \sum_{j=1}^{t}\left( \widehat{\mathbf{y}}_j - \mathbf{y}^{\rm ref}_j \right) \;. \] - - -

An additional option available to users is a control law that updates the change in u,

- - \[ -\Delta\mathbf{u}_{t} = -\mathbf{K}^c_u \left(\mathbf{u}_{t-1} - \mathbf{u}^{\rm ref}_{t-1} \right) - \mathbf{K}^c_x \left( \widehat{\mathbf{x}}_t - \mathbf{x}^{\rm ref}_t\right)\;, \] - - - - \[ -\mathbf{u}_{t} = \mathbf{u}_{t-1} + \Delta\mathbf{u}_{t} \; . \] - - -

Notice that this takes the form of a first-order difference equation for updating control (i.e., - \( \Delta\mathbf{u}_{t} = -\mathbf{K}^c_u \mathbf{u}_{t-1} + \epsilon_{t-1} \) - -), effectively low-pass filtering the input depending on the characteristics of - \( \mathbf{K}^c_u \) - -. This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.

-

Integral action and the - \( \Delta \mathbf{u} \) - - control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR’d to use in combination.

-

- Calculating reference state-control from output - # -

-

In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce - \( \mathbf{x}^{\rm ref} \) - - and - \( \mathbf{u}^{\rm ref} \) - -. Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller<System>::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/terminology/model/index.html b/docs/docs/terminology/model/index.html deleted file mode 100644 index 9b996916..00000000 --- a/docs/docs/terminology/model/index.html +++ /dev/null @@ -1,323 +0,0 @@ - - - - - - - - - - - - - -Models | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - Models - - -
- - - - - - -
- - - -

- Model Definitions - # -

-

This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: - - - - - \[ -\mathbf{x}_{t+1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} + \mathbf{B} \mathbf{v}_{t} + \mathbf{m}_{t} + \mathbf{w}_{t} \] - -

- - \[ -\mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right) \] - - -
t           : time index
-x           : system state
-v = g%u     : input (e.g., in physical units used for model fit)
-u           : control signal sent to actuator (e.g., in Volts)
-y           : system output
-m           : process disturbance
-w ~ N(0, Q) : process noise/disturbance
-
-A           : state matrix
-B           : input coupling matrix
-g           : input gain (e.g., for converting to control signal actuator voltage)
-              n.b., assumes this conversion is linear
-Q           : process noise covariance
-
-%           : element-wise multiplication
-
-

- LDS with Gaussian Observations - # -

-

For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.

- - \[ -\mathbf{y}_{t} = \mathbf{C} \mathbf{x}_{t} + \mathbf{d} \] - - - - \[ -\mathbf{z}_{t} \sim \mathcal{N}\left(\mathbf{y}_{t} , \mathbf{R} \right) \] - - -
z           : measurement
-
-C           : output matrix
-d           : output bias
-R           : measurement noise covariance
-
-

- LDS with Poisson Observations - # -

-

For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.

- - \[ -y_{t}^{i} = \exp \left(\mathbf{c}^i \mathbf{x}_{t} + d^i\right) \] - - - - \[ -z_{t}^i \sim \rm{Poisson} \left(y_{t}^i \right) \] - - -
i           : output index
-
-z           : measurement (count data)
-
-c           : i^th row of output matrix (C)
-d           : output bias
-
-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/tutorials/eg_glds_control/index.html b/docs/docs/tutorials/eg_glds_control/index.html deleted file mode 100644 index fc584945..00000000 --- a/docs/docs/tutorials/eg_glds_control/index.html +++ /dev/null @@ -1,435 +0,0 @@ - - - - - - - - - - - - - -GLDS Control | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - GLDS Control - - -
- - - - - - -
- - - -

- GLDS Control Tutorial - # -

-

This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.

-

The full code for this can be found here.

-

- Preamble - # -

-

In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.

-
#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-

Note that lds::Matrix and lds::Vector are typedefs for arma::Mat<data_t> and arma::Col<data_t>, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).

-

- Creating a simulated system - # -

-

A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.

-
  // Make 1st-order SISO system, sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(5.0 / dt);
-

When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.

-
  // construct ground truth system to be controlled...
-  // initializes to random walk model with top-most n_y state observed
-  lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);
-

This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, - - - - - \[ -x_{t+1} = x_t + w_t \] - -

- - \[ -y_{t} = x_t \] - - -

where - \( w_{t} \sim \mathcal{N}\left( 0, Q \right) \) - -.

-

Now, create non-default parameters for this model.

-
  // Ground-truth parameters for the controlled system
-  // (stand-in for physical system to be controlled)
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.01);
-  Matrix b_true = Matrix(n_x, n_u).fill(2e-4);
-  // control signal to model input unit conversion e.g., V -> mW/mm2:
-  Vector g_true = Vector(n_y).fill(10.0);
-
-  // output noise covariance
-  Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;
-

As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.

-
  /// Going to simulate a switching disturbance (m) acting on system
-  size_t which_m = 0;  // whether low or high disturbance (0, 1)
-  data_t m_low = 5 * dt * (1 - a_true[0]);
-  data_t pr_lo2hi = 1e-3;  // probability of going from low to high disturb.
-  data_t m_high = 20 * dt * (1 - a_true[0]);
-  data_t pr_hi2lo = pr_lo2hi;
-
-  // initially let m be low
-  Vector m0_true = Vector(n_y).fill(m_low);
-

Finally, assign the parameters using corresponding set-methods.

-
 // Assign params.
-  controlled_system.set_A(a_true);
-  controlled_system.set_B(b_true);
-  controlled_system.set_m(m0_true);
-  controlled_system.set_g(g_true);
-  controlled_system.set_R(r_true);
-

- Creating the controller - # -

-

Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.

-

For the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.

-
  // make a controller
-  lds::gaussian::Controller controller;
-  {
-    // Create **incorrect** model used for control.
-    // (e.g., imperfect model fitting)
-    Matrix b_controller = b_true / 2;
-
-    // let's assume zero process disturbance initially
-    // (will be re-estimating)
-    Vector m_controller = Vector(n_x, arma::fill::zeros);
-
-    // for this demo, just use arbitrary default R
-    Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0;
-
-    lds::gaussian::System controller_system(controlled_system);
-    controller_system.set_B(b_controller);
-    controller_system.set_m(m_controller);
-    controller_system.set_R(r_controller);
-    controller_system.Reset();  // reset to new m
-
-    // going to adaptively re-estimate the disturbance
-    controller_system.do_adapt_m = true;
-
-    // set adaptation rate by changing covariance of assumed process noise
-    // acting on random-walk evolution of m
-    Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6;
-    controller_system.set_Q_m(q_m);
-
-    // create controller
-    // lower and upper bounds on control signal (e.g., in Volts)
-    data_t u_lb = 0.0;  // [=] V
-    data_t u_ub = 5.0;  // [=] V
-    controller = std::move(
-        lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub));
-  }
-

Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.

-

With the controller constructed, control variables may be set.

-
  // Control variables:
-  // if following enabled, adapts set point with re-estimated process
-  // disturbance n.b., should not need integral action if this is enabled as the
-  // adaptive estimator minimizes DC error
-  bool do_adaptive_set_point = false;
-
-  // Reference/target output, controller gains
-  Vector y_ref0 = Vector(n_y).fill(20.0 * dt);
-  Matrix k_x = Matrix(n_u, n_x).fill(100);     // gains on state error
-  Matrix k_inty = Matrix(n_u, n_y).fill(1e3);  // gains on integrated err
-
-  // setting initial state to target to avoid error at onset:
-  Vector x0 = Vector(n_x).fill(y_ref0[0]);
-
-  // set up controller type bit mask so controller knows how to proceed
-  size_t control_type = 0;
-  if (do_adaptive_set_point) {
-    // adapt set point with estimated disturbance
-    control_type = control_type | lds::kControlTypeAdaptM;
-  } else {
-    // use integral action to minimize DC error
-    control_type = control_type | lds::kControlTypeIntY;
-  }
-
-  // set controller type
-  controller.set_control_type(control_type);
-
-  // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2):
-  Vector g_design = Vector(n_u).fill(9);
-
-  // Set params.
-  // **n.b. using arbitrary defaults for Q, R in this example. Really, these
-  // should be set by users, as they tune characteristics of Kalman filter.
-  // Users can also choose not to recursively calculate the estimator gain and
-  // supply it (setKe) instead of covariances.**
-  controller.set_y_ref(y_ref0);
-  controller.set_Kc(k_x);
-  controller.set_Kc_inty(k_inty);
-  controller.set_g_design(g_design);
-

- Simulating control - # -

-

In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.

-

The control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.

-
  // Simulate the true system.
-  z.col(t) = controlled_system.Simulate(u_tm1);
-
-  // This method uses a steady-state solution to control problem to calculate
-  // x_ref, u_ref from reference output y_ref. Therefore, it is only
-  // applicable to regulation problems or cases where reference trajectory
-  // changes slowly compared to system dynamics.
-  u.col(t) = controller.ControlOutputReference(z.col(t));
-

- Example simulation result - # -

-

Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller’s online estimates of the output, state, and disturbance are given in purple.

-

example control output

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/tutorials/eg_plds_state_estimation/index.html b/docs/docs/tutorials/eg_plds_state_estimation/index.html deleted file mode 100644 index 8751945d..00000000 --- a/docs/docs/tutorials/eg_plds_state_estimation/index.html +++ /dev/null @@ -1,366 +0,0 @@ - - - - - - - - - - - - - -PLDS State Estimation | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - PLDS State Estimation - - -
- - - - - - -
- - - -

- PLDS State Estimation Tutorial - # -

-

This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.

-

The full code for this can be found here.

-

- Preamble - # -

-

In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.

-
#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-

Note that lds::Matrix and lds::Vector are typedefs for arma::Mat<data_t> and arma::Col<data_t>, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).

-

- Creating a simulated system - # -

-

A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.

-
  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;                           // no. inputs
-  size_t n_x = 1;                           // no. states
-  size_t n_y = 1;                           // no. outputs
-  auto n_t = static_cast<size_t>(30 / dt);  // no time steps for simulation.
-

When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.

-
  // construct ground truth system...
-  lds::poisson::System system_true(n_u, n_x, n_y, dt);
-

This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, - - - - - \[ -x_{t+1} = x_t + w_t \] - -

- - \[ -y_{t} = \exp\left(x_t\right) \] - - -

where - \( w_{t} \sim \mathcal{N}\left( 0, Q \right) \) - -.

-

Now, create non-default parameters for this model.

-
  // Model parameters
-  Matrix a_true(n_x, n_x, arma::fill::eye);
-  a_true[0] = exp(-dt / 0.075);
-  Matrix b_true = Matrix(n_x, n_u).fill(1e-2);
-  Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2);  // disturbance
-  Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) -
-                                        a_true);  // initial state
-

Finally, assign the parameters using corresponding set-methods.

-
  // Assign params.
-  system_true.set_A(a_true);
-  system_true.set_B(b_true);
-  system_true.set_x0(x0_true);
-  system_true.set_m(m0_true);
-  system_true.Reset();
-

- Creating the estimator - # -

-

Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.

-
  // Construct system for estimation
-  // e.g., will create a model with incorrect disturbance
-  lds::poisson::System system_estimator(n_u, n_x, n_y, dt);
-
-  // Can copy parameters from another system object
-  system_estimator = system_true;
-
-  // wrong disturbance
-  Vector m0_est = m0_true * 2;
-  system_estimator.set_m(m0_est);
-
-  // set new initial conditions
-  Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) -
-                                      a_true);  // initial state
-  system_estimator.set_x0(x0_est);
-  system_estimator.Reset();  // reset to initial condition.
-

To ensure robust estimates, adaptively re-estimate the process disturbance.

-
  // turn on adaptive disturbance estimation
-  system_estimator.do_adapt_m = true;
-
-  // set adaptation rate by changing covariance of assumed process noise acting
-  // on random-walk evolution of m
-  Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6;
-  system_estimator.set_Q_m(q_m);
-

- Simulating estimation - # -

-

In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.

-
    // Simlate the true system.
-    z.col(t) = system_true.Simulate(u.col(t - 1));
-
-    // Filter (predict -> update)
-    system_estimator.Filter(u.col(t - 1), z.col(t));
-

- Example simulation result - # -

-

Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.

-

example estimator output

-

With this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.

-

example estimator output histogram

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/tutorials/eg_switched_plds_control/index.html b/docs/docs/tutorials/eg_switched_plds_control/index.html deleted file mode 100644 index d87a6bc0..00000000 --- a/docs/docs/tutorials/eg_switched_plds_control/index.html +++ /dev/null @@ -1,408 +0,0 @@ - - - - - - - - - - - - - -PLDS Switched Control | LDS C&E - - - - - - - - - - - -
- - -
-
- -
- - - PLDS Switched Control - - -
- - - - - - -
- - - -

- PLDS Switched Control Tutorial - # -

-

This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.

-

In the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.

-

The full code for this can be found here.

-

- Preamble - # -

-

In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.

-
#include <ldsCtrlEst>
-
-using lds::Matrix;
-using lds::Vector;
-using lds::data_t;
-using std::cout;
-

Note that lds::Matrix and lds::Vector are typedefs for arma::Mat<data_t> and arma::Col<data_t>, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).

-

- Creating the simulated system - # -

-

A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.

-
  // whether to do switched control
-  bool do_switch_ctrl = true;
-
-  // Make SISO system sampled at 1kHz
-  data_t dt = 1e-3;
-  size_t n_u = 1;
-  size_t n_x = 1;
-  size_t n_y = 1;
-
-  // no time steps for simulation.
-  auto n_t = static_cast<size_t>(30.0 / dt);
-

The system’s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.

-
  // for simulating switching
-  size_t which_mode = 1;
-  data_t pr_21 = 1e-3;   // prob mode 1 -> 2
-  data_t pr_12 = pr_21;  // prob mode 2 -> 1
-

Initially, the system will be in “mode” 1, where B = b1.

-
  // simulated system being controlled
-  lds::poisson::System controlled_system(n_u, n_x, n_y, dt);
-
-  // **Assume the system is not well characterized by one LDS, but is well
-  // characterized by two LDS models with different input matrices.**
-  data_t scale_sys_b = 2;
-
-  Matrix a(n_x, n_x, arma::fill::eye);
-  a[0] = 0.985;
-  Matrix b1 = Matrix(n_x, n_u).fill(0.05);
-  Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));
-
-  controlled_system.set_A(a);
-  controlled_system.set_B(b1);
-  controlled_system.set_d(d);
-  controlled_system.Reset();  // reset to initial conditions
-

See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.

-

- Creating the controller - # -

-

Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.

-

Similar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.

-

Moreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.

-

Putting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.

-
  // create switched controller
-  lds::poisson::SwitchedController switched_controller;
-  lds::UniformMatrixList<> k_x; // feedback controller gains
-  {
-    // create switched controller sub-systems
-    // system 1
-    lds::poisson::System sys1(controlled_system);
-
-    // set process noise covariance
-    Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3;
-    sys1.set_Q(q_controller);
-
-    // adaptively estimate process disturbance (m)
-    // n.b. using arbitrary default value for process noise if enabled.
-    sys1.do_adapt_m = true;
-
-    // setting initial mode to target to avoid large error at onset:
-    Vector x0_controller = arma::log(y_ref0) - d;
-    sys1.set_x0(x0_controller);
-    sys1.Reset();  // reset to initial conditions
-
-    // system 2
-    lds::poisson::System sys2 = sys1;
-
-    // set parameters
-    sys2.set_B(b2);
-
-    lds::UniformSystemList<lds::poisson::System> systems({sys1, sys2});
-
-    // controller gains for underlying systems:
-    Matrix k_x1(n_u, n_x, arma::fill::ones);
-    Matrix k_x2 = scale_sys_b * k_x1;  // system2 is x-times less sensitive.
-    k_x = lds::UniformMatrixList<>({k_x1, k_x2});
-
-    data_t u_lb = 0.0;
-    data_t u_ub = 5.0;
-    switched_controller = std::move(
-        lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub));
-  }
-

Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.

-

Now that the SwitchedController is instantiated, assign its parameters.

-
    // Control variables
-  size_t control_type = 0;  // no integral action, etc
-  switched_controller.set_control_type(control_type);
-  switched_controller.set_Kc(std::move(k_x));
-  switched_controller.set_y_ref(y_ref0);
-

- Simulating control - # -

-

In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).

-

The control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.

-
  // Let the controlled system stochastically change gain
-  // Assume another algorithm decodes this mode change and signals the
-  // switched_controller
-  Vector chance(1, arma::fill::randu);
-  if (which_mode == 1)  // mode1
-  {
-    if (chance[0] < pr_21) {
-      which_mode = 2;
-      controlled_system.set_B(b2);
-      if (do_switch_ctrl) {
-        switched_controller.Switch(1);
-      }
-    }
-  } else {  // mode2
-    if (chance[0] < pr_12) {
-      which_mode = 1;
-      controlled_system.set_B(b1);
-      if (do_switch_ctrl) {
-        switched_controller.Switch(0);
-      }
-    }
-  }
-
-  // Simulate the true system.
-  z.col(t) = controlled_system.Simulate(u.col(t - 1));
-
-  // perform control
-  u.col(t) = switched_controller.ControlOutputReference(z.col(t));
-

Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system’s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.

-

- Example simulation result - # -

-

Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller’s online estimates of the output and state are shown in purple.

-

example control output

-

Note that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/tutorials/index.html b/docs/docs/tutorials/index.html deleted file mode 100644 index 3fc04b7e..00000000 --- a/docs/docs/tutorials/index.html +++ /dev/null @@ -1,239 +0,0 @@ - - - - - - - - - - - -LDS C+E Examples | LDS C&E - - - - - - - - - - - - -
- - -
-
- -
- - - LDS C+E Examples - - -
- - - - - - -
- - - -

- Examples - # -

-
- - - -
- -
- - - - - -
- - - - - - - - -
- - - - - - -
- - - - -
- - - - - - - - - - - - - - - - diff --git a/docs/docs/tutorials/index.xml b/docs/docs/tutorials/index.xml deleted file mode 100644 index a0a87c3e..00000000 --- a/docs/docs/tutorials/index.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - LDS C+E Examples on LDS C&E - https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/ - Recent content in LDS C+E Examples on LDS C&E - Hugo -- gohugo.io - - GLDS Control - https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/ - GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. - - - - PLDS State Estimation - https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/ - PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated. -The full code for this can be found here. - - - - PLDS Switched Control - https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/ - Mon, 01 Jan 0001 00:00:00 +0000 - - https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/ - PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear. -In the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. - - - - diff --git a/docs/eg_glds_ctrl_output.png b/docs/eg_glds_ctrl_output.png deleted file mode 100644 index 9e7d9b202f4f59aaf05ddcaa8c57ffbecb774455..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169071 zcmeFZg;!Ty6fKH~0wN(D(h5jQNQcthAuZjYfTV=dEz;fH-Kd15q=2-5bcdwCTln2~ z-+N=+JI4D1?ioWxIOqJ%*?X_G=A3J8f)wQ?(C-o7LqI@4my#4!MnFI+zxjiL2;VUl zc@z)-puU#WbU;8Lz`pr&%PegK&O$(Tl#&xeUcOC;`53)Zen1_*^~h1|rK5_SsiU)j zy$OQc3su<{!i*HE<|Y(!29D+$cJ@{dj1*F~M$G ?kas+;pg>`e?DE$nP5R4r^w z5ZE}F4(kLY)f#{{Z6zcL%5hX=Dc8Xs|mP|5XW(uJ|D7e>nv>b4~M89YK|rW0qT`w*T% zcaN&**O&6a-H0xC{!|`^mHwxUY-uNbab*krJcImZu@0%bRm60-_in}jj`ObaD)#@r zL`b=Pi}K%>-l@XRQT~0Eo`;3?-bihIuw zU!tbE+T5AqzgS^G@+Hk{ZM_O%X^m$$8z?tu{}xV4f{pztGLrJ(r%IkYIuV=k<@ulO zM3H|N3^9C5Nx9`QJTud5KAbr-qa!R?rq{AF+vL3QqX14X(QR^CX!C39>Rmo4`+U_X zQi=NSIyWRHUsB_)aMFf`hVk)nR8&+VA|kVaWX)>x2b7O!XmT?%Kb(9lEG(?9u4dA% zMY?-8RnVi+=n(DS)wASUD16;;KWP-#KVupaVZ>r6tel*BHI`%hOI>zz%?YuwoOW~k z%OV3Qe0A^64#EfM{$03+4)6DZIZnUPF$imMcX!a5gN-eb-}T^ldwMXLr=0}L&CRV; zv&vsk@ZD+o>8*f&S0NZuP;8-ec6R!HBolBu+MQ2p@fb>f;rlT&lWHzYM@PrJPFc9S z#r@=eWW||!MoSl6Ge+k>4S{9Q=R`6y}WEQ-XftS3h%iQdcm{CCIa3S&Qh=+wW?C@+VyhnRBrze>h` zK+nLiY|rB^^XAPPU0u@M0d#b96BCodPlfsU^{$6Bw9g7lOH1LR+FJF^cjIGYJ;rR2 zJe0fwEW(m%YB75lTkmT3Wfzp*|MxLuQX&BuDG&yOLf2dC>+8RMy|S_*!NOA1*C!_> z-QL>TT3j@=u;4N2jplRSc^Z%`=y6_*VNobabTCoC#R^WC?;kMM}hy%>7D@%NxMI~k=a&JS?k7Z z)5-hW$ZKh~wzdwd12NJ$gMt^uqEu4S(%1t*wu2$LvB}9>%gf8Zem!^QezS-~X$&z4 z56n+#3^##wH`nZ1wU;1#lI^{dQ&7;;-kw!ZPynL_6O!_!y4#sP$^-cGQ5u0hK)}57C64bHP^?}c5OVv+pd3h zW~Y05d;9yX`{P+}GG|;I0cA%|PeEy^?aoXCwM6(M-}?`_U!NbX3tj((_uh#8;*7`p z>HVTt4Dyfw$G?TYys|<}LgMVC2Sbpa=HdbrcHD=r~{BV4Ngd1qxMSCLNed@T!RGy=08*X+jn7*hl;T2(J3~w_rF~O}L`3@POp`oGQzu*7;`$U<(zN%`rA{}IS zyP0|=UELC_31LYTDS7!^uY0Sab*o}3)V|SiaqNSoS~bB~mCn0*e!`K-$+9Rx@O%oW zX$7_!e&r!w2>+#%mRE56HD`PJRPn7cc95^Xc63mX)L^QB1xkl?2Ij%R0Scwi-#s5n zG}0H&e>T*NjFw=HZES47@SmTbPyPG}3&uw(TmQ4?Zu6nMh)8>H3_4W1L z-Q5ENH^t_8SH^xsKtNYKs}Z+C*h8L5h_v~6ApwEeo*s*lY$*Wn`^U$g>p5}p9+_EL z`tSBTv$L}yN8r%PThBH^?k|C*@@u`-?!%9f*(efJ)-j$H@i2BfGFG& zm6n$F>C=aQ>FKEV*H<4@$}EWpQ_O5eCME%o^P}_^KH(2}KBcDaY;L;$?tTEXAsi^2 z@oH)aYFn9Z)A8>ph1N?5rq7p7Ha6Rd૞`1l40F*I&^=jzHj}A|!T#Il6Q&So? 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 Dff{T@ diff --git a/docs/eg_plds_ctrl_output.png b/docs/eg_plds_ctrl_output.png deleted file mode 100644 index c3f7346baee2c2d6a7d23061c68205bf79a0ada3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217595 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%0D1WbS8e-o!u7dF9y@ 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 Yd*GY3hX+T%YvSn~($v$)Qn$SEA2o%2F#rGn diff --git a/docs/eg_plds_est_output.png b/docs/eg_plds_est_output.png deleted file mode 100644 index 6008362171c47023d581ec3defb02b3ac0d45569..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 148988 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 ZGsML|l*2$VS&=Aw8R?np7E>M0{y%7YwGsdT diff --git a/docs/eg_plds_est_output_hist.png b/docs/eg_plds_est_output_hist.png deleted file mode 100644 index 992aecaf8d2cfd2c260474348b540256e2b90d27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29070 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 J9KFNg{{clw1DOB- diff --git a/docs/eg_plds_switched_ctrl_output.png b/docs/eg_plds_switched_ctrl_output.png deleted file mode 100644 index 6fd77957e1ef90b23a3b03aa477c5d3d259742c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156078 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 diff --git a/docs/en.search-data.min.1e85f10c5ecc4f42108eae9ec00bea53e75365846aca80f6f94cdec83d505cec.json b/docs/en.search-data.min.1e85f10c5ecc4f42108eae9ec00bea53e75365846aca80f6f94cdec83d505cec.json deleted file mode 100644 index 94c00e43..00000000 --- a/docs/en.search-data.min.1e85f10c5ecc4f42108eae9ec00bea53e75365846aca80f6f94cdec83d505cec.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":"LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":"Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":"Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":"Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone --recurse-submodules https://github.com/stanley-rozell/lds-ctrl-est.git cd lds-ctrl-est\r# use git submodule update --init if you clone the repo without --recurse-submodules\rCompilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build .\rThe first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\n LDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\n For basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\n LD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\nThe bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory.\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build might not work for both the standalone library and the Python package (especially on Windows), since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nConsiderations for Windows # First of all, development on Windows has been more prone to bugs, so if you encounter many problems, consider using a Unix-based system—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nYou will likely need to get compiler tools through the Visual Studio installer. Compilation was tested in VS Code using the following kit:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64)\r"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/linux-macos/","title":"Linux Macos","section":"LDS C+E Documentation","content":"Downloading the Library # The source code for this library can be downloaded from stanley-rozell/ldsCtrlEst either by downloading a snapshot or cloning the repository via git.\ngit clone https://github.com/stanley-rozell/ldsCtrlEst.git By default, this would check out the master branch. In most cases, we suggest downloading or checking out the latest release instead.\ncd /path/to/ldsCtrlEst git checkout 0.6 Dependencies # Note that the primary dependencies of this project listed below must be installed along with their header files and with CMake config files or pkg-config files. The latter files are used to configure this project\u0026rsquo;s build. It is strongly encouraged to install the dependencies below using a package manager (e.g., apt, pacman, macports).\n For project configuration, install cmake as well as pkg-config. The latter is optional. The linear algebra library armadillo is used throughout this repository. The HDF5 library is used to save output from example test programs. For use of this library in Matlab executables (mex) on Linux operating systems, you will need OpenBlas, ensuring the static library libopenblas.a is installed. You will also need to install gfortran. Compilation + Installation # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\n LDSCTRLEST_BUILD_EXAMPLES : [default= ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake/make to configure/build the library.\n For basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build make #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location make #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\n LD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool CMAKE_PREFIX_PATH: search path of prefix where CMake will look for package config files e.g., Assuming you set -DCMAKE_INSTALL_PREFIX=/your/install/prefix during project configuration and your login shell uses the ~/.profile startup file, open ~/.profile in a text editor and add \u0026hellip;\nexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/your/install/prefix/lib export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix/lib/pkgconfig export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix Common issues # \u0026ldquo;I have installed all the dependencies including gfortran with a package manager as suggested; however, cmake complains it cannot find the gfortran library.\u0026rdquo; When gfortran is installed, its library is usually not installed in a standard location like /usr/lib. gfortran is part of the gcc suite, so their libraries are organized together. e.g., When you install gfortran on Ubuntu using apt, its location is /usr/lib/gcc/x86_64-linux-gnu/9, in the case that gcc version 9 is installed. The build configuration script in ldsCtrlEst is written to add LD_LIBRARY_PATH (Unix) or PATH (Windows) to the CMake library search path on Unix or Windows systems, respectively. Therefore, to fix this issue, simply add the directory in which libgfortran was installed to the OS-appropriate environment variable. Continuing with the Ubuntu example above and assuming a Unix login shell whose startup file is ~/.profile, add the following to the file.\nexport LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/gcc/x86_64-linux-gnu/9 \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix "},{"id":5,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":"Dependencies # To configure and compile the library, you will need Microsoft Visual Studio. After Visual Studio, install the \u0026ldquo;Desktop Development with C++\u0026rdquo; extension under Tools-\u0026gt;Get Tools and Features, ensuring that the \u0026ldquo;C++ Clang Tools for Windows\u0026rdquo; and \u0026ldquo;CMake C++ Tools for Windows\u0026rdquo; options are checked.\nIn addition to the dependencies mentioned in Linux/MacOS, on Windows, this library opts to use Intel\u0026rsquo;s Math Kernel Library (MKL) for linear algebra, rather than OpenBLAS/gfotran. MKL is part of the OneAPI suite. When using the installation wizard, users may either install the entire base toolkit or just the MKL portion. After installing MKL as well as HDF5, add these libraries to the user\u0026rsquo;s PATH environment variable (System Properties-\u0026gt;Advanced-\u0026gt;Environment Variables). Your installation location may vary, but for example, add the following lines to PATH: C:\\Program Files (x86)\\Intel\\oneAPI\\mkl\\latest\\redist\\intel64, C:\\Program Files\\HDF_Group\\HDF5\\1.12.0\\lib .\nOnce HDF5, MKL, and Visual Studio have been installed, download armadillo as detailed previously for Linux/MacOS. Open the source code in Visual Studio, configure the build (Project -\u0026gt; Generate CMake Cache), and install (Build -\u0026gt; Install). Take note of the install location and add this to an environment variable called CMAKE_PREFIX_PATH; create this variable if it does not already exist.\nCompilation # After installing the dependencies above and downloading the ldsCtrlEst library source code (see Linux/MacOS), open this directory in Visual Studio. Configure the build (Project -\u0026gt; Generate CMake Cache) and build/install the targets (Build -\u0026gt; Install). Finally, take note of the install location and add this to the environment variable PATH.\nCommon issues # \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":6,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":"Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":"armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; auto m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; auto m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; auto a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; auto a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline auto m2T_scalar( const mxArray * matlab_scalar ) Parameters:\n matlab_scalar matlab scalar Template Parameters:\n T type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline auto m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\n matlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline auto a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\n arma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline auto a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\n arma_vec armadillo vector Return: matlab vector\n Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":8,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":"armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\n matlab_cell matlab cell Template Parameters:\n T type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab matrix Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n std_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\n Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":9,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":"Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\r\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\r\\] \\[\r\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\r\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n \\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\r\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\r\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\r\\] An additional option available to users is a control law that updates the change in u,\n \\[\r\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] \\[\r\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\r\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":"Classes # lds::Controller\n lds::EM\n lds::Fit LDS Fit Type.\n lds::SSID\n lds::SwitchedController SwitchedController Type.\n lds::System Linear Dynamical System Type.\n lds::UniformMatrixList\n lds::UniformSystemList\n lds::UniformVectorList\n lds::gaussian::Controller Gaussian-observation Controller Type.\n lds::gaussian::Fit GLDS Fit Type.\n lds::gaussian::FitEM GLDS E-M Fit Type.\n lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\n lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\n lds::gaussian::System Gaussian LDS Type.\n lds::poisson::Controller PLDS Controller Type.\n lds::poisson::Fit PLDS Fit Type.\n lds::poisson::FitEM PLDS E-M Fit Type.\n lds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\n lds::poisson::SwitchedController Poisson-observation SwitchedController Type.\n lds::poisson::System Poisson System type.\n Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":"Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":"Defaults # \nMore\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":"eg_glds_ctrl.cpp # Example GLDS Control\n//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":"eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\n//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":"eg_plds_ctrl.cpp # Example PLDS Control\n//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":"eg_plds_est.cpp # Example PLDS Estimation\n//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":17,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":"eg_plds_switched_ctrl.cpp # Example Switched PLDS Control\n//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":"Examples # eg_glds_ctrl.cpp\n eg_glds_du_plds_ctrl.cpp\n eg_plds_ctrl.cpp\n eg_plds_est.cpp\n eg_plds_switched_ctrl.cpp\n Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":"examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":"examples/eg_glds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":"examples/eg_glds_du_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":"examples/eg_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":"examples/eg_plds_est.cpp # Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Function Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":"examples/eg_plds_switched_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":25,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":"Files # examples/eg_glds_ctrl.cpp\n examples/eg_glds_du_plds_ctrl.cpp\n examples/eg_plds_ctrl.cpp\n examples/eg_plds_est.cpp\n examples/eg_plds_switched_ctrl.cpp\n ldsCtrlEst_h/lds.h lds namespace\n ldsCtrlEst_h/lds_ctrl.h Controller.\n ldsCtrlEst_h/lds_fit.h LDS base fit type.\n ldsCtrlEst_h/lds_fit_em.h subspace identification\n ldsCtrlEst_h/lds_fit_ssid.h subspace identification\n ldsCtrlEst_h/lds_gaussian.h glds namespace\n ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\n ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\n ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\n ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\n ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\n ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\n ldsCtrlEst_h/lds_poisson.h plds namespace\n ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\n ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\n ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\n ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\n ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\n ldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\n ldsCtrlEst_h/lds_sctrl.h SwitchedController type.\n ldsCtrlEst_h/lds_sys.h LDS base type.\n ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\n ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\n ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\n ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\n ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\n src/lds.cpp misc lds namespace functions\n src/lds_gaussian_sys.cpp GLDS base type.\n src/lds_poisson_sys.cpp PLDS base type.\n src/lds_sys.cpp LDS base type.\n src/lds_uniform_vecs.cpp Uniformly sized vectors.\n Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":26,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":"GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":"include # Directories # Name ldsCtrlEst_h Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":"lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m const data_t kInf Some useful numbers. const data_t kPi const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = typedef double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = typedef arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = typedef arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = typedef arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = typedef arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\n X mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\n X mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\n L lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\n A some matrix B some other matrix Return: covariance\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nkInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1controller/","title":"lds::Controller","section":"Classes","content":"lds::Controller # More\u0026hellip;\nInherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController\u0026lt; System \u0026gt;\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\n ControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\n sys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::poisson::SwitchedController::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::gaussian::Controller::set_y_ref\n set_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\n control_type control type bit mask Template Parameters:\n System type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\n u_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\n u_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # **sys_** # System sys_; **u_** # Vector u_; **u_return_** # Vector u_return_; **g_design_** # Vector g_design_; **u_ref_** # Vector u_ref_; **u_ref_prev_** # Vector u_ref_prev_; **x_ref_** # Vector x_ref_; **y_ref_** # Vector y_ref_; **cx_ref_** # Vector cx_ref_; **Kc_** # Matrix Kc_; **Kc_u_** # Matrix Kc_u_; **Kc_inty_** # Matrix Kc_inty_; **du_ref_** # Vector du_ref_; **dv_ref_** # Vector dv_ref_; **v_ref_** # Vector v_ref_; **dv_** # Vector dv_; **v_** # Vector v_; **int_e_** # Vector int_e_; **int_e_awu_adjust_** # Vector int_e_awu_adjust_; **u_sat_** # Vector u_sat_; **do_control_prev_** # bool do_control_prev_ = false; **do_lock_control_prev_** # bool do_lock_control_prev_ = false; **u_saturated_** # bool u_saturated_ = false; **u_lb_** # data_t u_lb_ {}; **u_ub_** # data_t u_ub_ {}; **tau_awu_** # data_t tau_awu_ {}; **k_awu_** # data_t k_awu_ = 0; **t_since_control_onset_** # data_t t_since_control_onset_ = 0; **control_type_** # size_t control_type_ {}; Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1em/","title":"lds::EM","section":"Classes","content":"lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n n_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n fit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\n calc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n x # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\n force_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\n calc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\n MaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\n Smooth # void Smooth( bool force_common_initial ) Parameters:\n force_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\n Ke estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\n Reset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\n Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **x_** # std::vector\u0026lt; Matrix \u0026gt; x_; **P_** # std::vector\u0026lt; Cube \u0026gt; P_; **P_t_tm1_** # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; **y_** # std::vector\u0026lt; Matrix \u0026gt; y_; **diag_y_** # Matrix diag_y_; **sum_E_x_t_x_t_** # Matrix sum_E_x_t_x_t_; **sum_E_xu_tm1_xu_tm1_** # Matrix sum_E_xu_tm1_xu_tm1_; **sum_E_xu_t_xu_tm1_** # Matrix sum_E_xu_t_xu_tm1_; **fit_** # Fit fit_; **theta_** # Vector theta_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1fit/","title":"lds::Fit","section":"Classes","content":"lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\n set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\n set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\n x state estimate (over time) u input (over time) t time index Return: view of updated state\n f # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\n x_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\n h # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::poisson::Fit::h, lds::gaussian::Fit::h\n Protected Attribute Details # **dt_** # data_t dt_ {}; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **m_** # Vector m_; **Q_** # Matrix Q_; **C_** # Matrix C_; **d_** # Vector d_; **R_** # Matrix R_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":"lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/","title":"lds::gaussian::Controller","section":"Classes","content":"lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/","title":"lds::gaussian::Fit","section":"Classes","content":"lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/","title":"lds::gaussian::FitEM","section":"Classes","content":"lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/","title":"lds::gaussian::FitSSID","section":"Classes","content":"lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":"lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/","title":"lds::gaussian::System","section":"Classes","content":"lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\n n_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n R # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\n Protected Attribute Details # **R_** # Matrix R_; **do_recurse_Ke_** # bool do_recurse_Ke_ {}; Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":"lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/","title":"lds::poisson::Controller","section":"Classes","content":"lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/","title":"lds::poisson::Fit","section":"Classes","content":"lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/","title":"lds::poisson::FitEM","section":"Classes","content":"lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/","title":"lds::poisson::FitSSID","section":"Classes","content":"lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/","title":"lds::poisson::SwitchedController","section":"Classes","content":"lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/","title":"lds::poisson::System","section":"Classes","content":"lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\n Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/","title":"lds::SSID","section":"Classes","content":"lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\n n_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\n ssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n Protected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\n t_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\n DecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::poisson::FitSSID::DecomposeData, lds::gaussian::FitSSID::DecomposeData\n CalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\n ssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\n wt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **D_** # Matrix D_; **fit_** # Fit fit_; **g_dc_** # Matrix g_dc_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_h_** # size_t n_h_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; **L_** # Matrix L_; **s_** # Vector s_; **ext_obs_t_** # Matrix ext_obs_t_; Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/","title":"lds::SwitchedController","section":"Classes","content":"lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\n idx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # **systems_** # std::vector\u0026lt; System \u0026gt; systems_; **n_sys_** # size_t n_sys_ {}; **idx_** # size_t idx_ {}; **Kc_list_** # UniformMatrixList Kc_list_; **Kc_inty_list_** # UniformMatrixList Kc_inty_list_; **Kc_u_list_** # UniformMatrixList Kc_u_list_; **g_design_list_** # UniformVectorList g_design_list_; Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1system/","title":"lds::System","section":"Classes","content":"lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\n u_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\n Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\n u_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::poisson::System::Simulate, lds::gaussian::System::Simulate\n f # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\n u input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::poisson::System::h, lds::gaussian::System::h\n n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::poisson::System::RecurseKe, lds::gaussian::System::RecurseKe\n InitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # **n_x_** # std::size_t n_x_ {}; **n_u_** # std::size_t n_u_ {}; **n_y_** # std::size_t n_y_ {}; **dt_** # data_t dt_ {}; **x_** # Vector x_; **P_** # Matrix P_; **m_** # Vector m_; **P_m_** # Matrix P_m_; **cx_** # Vector cx_; **y_** # Vector y_; **z_** # Vector z_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **m0_** # Vector m0_; **P0_m_** # Matrix P0_m_; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **Q_** # Matrix Q_; **Q_m_** # Matrix Q_m_; **C_** # Matrix C_; **d_** # Vector d_; **Ke_** # Matrix Ke_; **Ke_m_** # Matrix Ke_m_; Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/","title":"lds::UniformMatrixList","section":"Classes","content":"lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\n n [optional] index in list of matrices Return: dimensions\n size # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\n that input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/","title":"lds::UniformSystemList","section":"Classes","content":"lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System\u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\n that input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n operator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/","title":"lds::UniformVectorList","section":"Classes","content":"lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\n that input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n operator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n Updated on 4 May 2022 at 16:34:51 Eastern Daylight Time\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":"ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":"ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34;// system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_gaussian_sys.h\u0026#34;// controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34;// switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_,R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_,Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_,Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system type #include \u0026#34;lds_poisson_sys.h\u0026#34;// control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34;#include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34;#include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 4 May 2022 at 16:34:52 Eastern Daylight Time\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign=false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; public: using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Matrix tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34;// System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":"ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":78,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":"ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34;#include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":79,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":"Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\r\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\r\\] \\[\r\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\r\\] t : time index\rx : system state\rv = g%u : input (e.g., in physical units used for model fit)\ru : control signal sent to actuator (e.g., in Volts)\ry : system output\rm : process disturbance\rw ~ N(0, Q) : process noise/disturbance\rA : state matrix\rB : input coupling matrix\rg : input gain (e.g., for converting to control signal actuator voltage)\rn.b., assumes this conversion is linear\rQ : process noise covariance\r% : element-wise multiplication\r LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n \\[\r\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\r\\] \\[\r\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\r\\] z : measurement\rC : output matrix\rd : output bias\rR : measurement noise covariance\r LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n \\[\ry_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\r\\] \\[\rz_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\r\\] i : output index\rz : measurement (count data)\rc : i^th row of output matrix (C)\rd : output bias\r "},{"id":80,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":"Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\n Defaults\n Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":"Namespaces # armamexc arma/mex interface using Matlab C API\n armamexcpp arma/mex interface using Matlab C++ API\n lds::gaussian Linear Dynamical Systems with Gaussian observations.\n lds::poisson Linear Dynamical Systems with Poisson observations.\n Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":82,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":"Pages # Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":"PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = \\exp\\left(x_t\\right)\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":84,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":"PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":"src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":"src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":"src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":"src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":"src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":"src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 4 May 2022 at 16:34:53 Eastern Daylight Time\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.799bfc19f929cf1e231669cf06cae550679543dfef2bae61c8d816ffca12f5c7.json b/docs/en.search-data.min.799bfc19f929cf1e231669cf06cae550679543dfef2bae61c8d816ffca12f5c7.json deleted file mode 100644 index e32f676e..00000000 --- a/docs/en.search-data.min.799bfc19f929cf1e231669cf06cae550679543dfef2bae61c8d816ffca12f5c7.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":"LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":"Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":"Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":"Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone --recurse-submodules https://github.com/stanley-rozell/lds-ctrl-est.git cd lds-ctrl-est\r# use git submodule update --init if you clone the repo without --recurse-submodules\rCompilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build .\rThe first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\n LDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\n For basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\n LD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\nThe bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory.\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build might not work for both the standalone library and the Python package (especially on Windows), since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nConsiderations for Windows # First of all, development on Windows has been more prone to bugs, so if you encounter many problems, consider using a Unix-based system—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nYou will likely need to get compiler tools through the Visual Studio installer. Compilation was tested in VS Code using the following kit:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64)\rCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix On Windows, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":"Dependencies # To configure and compile the library, you will need Microsoft Visual Studio. After Visual Studio, install the \u0026ldquo;Desktop Development with C++\u0026rdquo; extension under Tools-\u0026gt;Get Tools and Features, ensuring that the \u0026ldquo;C++ Clang Tools for Windows\u0026rdquo; and \u0026ldquo;CMake C++ Tools for Windows\u0026rdquo; options are checked.\nIn addition to the dependencies mentioned in Linux/MacOS, on Windows, this library opts to use Intel\u0026rsquo;s Math Kernel Library (MKL) for linear algebra, rather than OpenBLAS/gfotran. MKL is part of the OneAPI suite. When using the installation wizard, users may either install the entire base toolkit or just the MKL portion. After installing MKL as well as HDF5, add these libraries to the user\u0026rsquo;s PATH environment variable (System Properties-\u0026gt;Advanced-\u0026gt;Environment Variables). Your installation location may vary, but for example, add the following lines to PATH: C:\\Program Files (x86)\\Intel\\oneAPI\\mkl\\latest\\redist\\intel64, C:\\Program Files\\HDF_Group\\HDF5\\1.12.0\\lib .\nOnce HDF5, MKL, and Visual Studio have been installed, download armadillo as detailed previously for Linux/MacOS. Open the source code in Visual Studio, configure the build (Project -\u0026gt; Generate CMake Cache), and install (Build -\u0026gt; Install). Take note of the install location and add this to an environment variable called CMAKE_PREFIX_PATH; create this variable if it does not already exist.\nCompilation # After installing the dependencies above and downloading the ldsCtrlEst library source code (see Linux/MacOS), open this directory in Visual Studio. Configure the build (Project -\u0026gt; Generate CMake Cache) and build/install the targets (Build -\u0026gt; Install). Finally, take note of the install location and add this to the environment variable PATH.\nCommon issues # \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":"Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":"armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; auto m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; auto m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; auto a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; auto a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline auto m2T_scalar( const mxArray * matlab_scalar ) Parameters:\n matlab_scalar matlab scalar Template Parameters:\n T type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline auto m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\n matlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline auto a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\n arma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline auto a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\n arma_vec armadillo vector Return: matlab vector\n Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":"armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\n matlab_cell matlab cell Template Parameters:\n T type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab matrix Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n std_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\n Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":"Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\r\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\r\\] \\[\r\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\r\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n \\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\r\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\r\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\r\\] An additional option available to users is a control law that updates the change in u,\n \\[\r\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] \\[\r\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\r\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":"Classes # lds::Controller\n lds::EM\n lds::Fit LDS Fit Type.\n lds::SSID\n lds::SwitchedController SwitchedController Type.\n lds::System Linear Dynamical System Type.\n lds::UniformMatrixList\n lds::UniformSystemList\n lds::UniformVectorList\n lds::gaussian::Controller Gaussian-observation Controller Type.\n lds::gaussian::Fit GLDS Fit Type.\n lds::gaussian::FitEM GLDS E-M Fit Type.\n lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\n lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\n lds::gaussian::System Gaussian LDS Type.\n lds::poisson::Controller PLDS Controller Type.\n lds::poisson::Fit PLDS Fit Type.\n lds::poisson::FitEM PLDS E-M Fit Type.\n lds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\n lds::poisson::SwitchedController Poisson-observation SwitchedController Type.\n lds::poisson::System Poisson System type.\n Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":"Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":"Defaults # \nMore\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":"eg_glds_ctrl.cpp # Example GLDS Control\n//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":"eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\n//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":"eg_plds_ctrl.cpp # Example PLDS Control\n//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":"eg_plds_est.cpp # Example PLDS Estimation\n//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":"eg_plds_switched_ctrl.cpp # Example Switched PLDS Control\n//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":17,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":"Examples # eg_glds_ctrl.cpp\n eg_glds_du_plds_ctrl.cpp\n eg_plds_ctrl.cpp\n eg_plds_est.cpp\n eg_plds_switched_ctrl.cpp\n Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":"examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":"examples/eg_glds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":"examples/eg_glds_du_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":"examples/eg_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":"examples/eg_plds_est.cpp # Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Function Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":"examples/eg_plds_switched_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":"Files # examples/eg_glds_ctrl.cpp\n examples/eg_glds_du_plds_ctrl.cpp\n examples/eg_plds_ctrl.cpp\n examples/eg_plds_est.cpp\n examples/eg_plds_switched_ctrl.cpp\n ldsCtrlEst_h/lds.h lds namespace\n ldsCtrlEst_h/lds_ctrl.h Controller.\n ldsCtrlEst_h/lds_fit.h LDS base fit type.\n ldsCtrlEst_h/lds_fit_em.h subspace identification\n ldsCtrlEst_h/lds_fit_ssid.h subspace identification\n ldsCtrlEst_h/lds_gaussian.h glds namespace\n ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\n ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\n ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\n ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\n ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\n ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\n ldsCtrlEst_h/lds_poisson.h plds namespace\n ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\n ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\n ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\n ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\n ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\n ldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\n ldsCtrlEst_h/lds_sctrl.h SwitchedController type.\n ldsCtrlEst_h/lds_sys.h LDS base type.\n ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\n ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\n ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\n ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\n ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\n src/lds.cpp misc lds namespace functions\n src/lds_gaussian_sys.cpp GLDS base type.\n src/lds_poisson_sys.cpp PLDS base type.\n src/lds_sys.cpp LDS base type.\n src/lds_uniform_vecs.cpp Uniformly sized vectors.\n Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":"GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":"include # Directories # Name ldsCtrlEst_h Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":"lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m const data_t kInf Some useful numbers. const data_t kPi const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = typedef double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = typedef arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = typedef arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = typedef arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = typedef arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\n X mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\n X mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\n L lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\n A some matrix B some other matrix Return: covariance\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nkInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1controller/","title":"lds::Controller","section":"Classes","content":"lds::Controller # More\u0026hellip;\nInherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController\u0026lt; System \u0026gt;\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\n ControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\n sys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::poisson::SwitchedController::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::gaussian::Controller::set_y_ref\n set_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\n control_type control type bit mask Template Parameters:\n System type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\n u_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\n u_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # **sys_** # System sys_; **u_** # Vector u_; **u_return_** # Vector u_return_; **g_design_** # Vector g_design_; **u_ref_** # Vector u_ref_; **u_ref_prev_** # Vector u_ref_prev_; **x_ref_** # Vector x_ref_; **y_ref_** # Vector y_ref_; **cx_ref_** # Vector cx_ref_; **Kc_** # Matrix Kc_; **Kc_u_** # Matrix Kc_u_; **Kc_inty_** # Matrix Kc_inty_; **du_ref_** # Vector du_ref_; **dv_ref_** # Vector dv_ref_; **v_ref_** # Vector v_ref_; **dv_** # Vector dv_; **v_** # Vector v_; **int_e_** # Vector int_e_; **int_e_awu_adjust_** # Vector int_e_awu_adjust_; **u_sat_** # Vector u_sat_; **do_control_prev_** # bool do_control_prev_ = false; **do_lock_control_prev_** # bool do_lock_control_prev_ = false; **u_saturated_** # bool u_saturated_ = false; **u_lb_** # data_t u_lb_ {}; **u_ub_** # data_t u_ub_ {}; **tau_awu_** # data_t tau_awu_ {}; **k_awu_** # data_t k_awu_ = 0; **t_since_control_onset_** # data_t t_since_control_onset_ = 0; **control_type_** # size_t control_type_ {}; Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1em/","title":"lds::EM","section":"Classes","content":"lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n n_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n fit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\n calc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n x # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\n force_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\n calc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\n MaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\n Smooth # void Smooth( bool force_common_initial ) Parameters:\n force_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\n Ke estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\n Reset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\n Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **x_** # std::vector\u0026lt; Matrix \u0026gt; x_; **P_** # std::vector\u0026lt; Cube \u0026gt; P_; **P_t_tm1_** # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; **y_** # std::vector\u0026lt; Matrix \u0026gt; y_; **diag_y_** # Matrix diag_y_; **sum_E_x_t_x_t_** # Matrix sum_E_x_t_x_t_; **sum_E_xu_tm1_xu_tm1_** # Matrix sum_E_xu_tm1_xu_tm1_; **sum_E_xu_t_xu_tm1_** # Matrix sum_E_xu_t_xu_tm1_; **fit_** # Fit fit_; **theta_** # Vector theta_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1fit/","title":"lds::Fit","section":"Classes","content":"lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\n set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\n set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\n x state estimate (over time) u input (over time) t time index Return: view of updated state\n f # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\n x_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\n h # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::poisson::Fit::h, lds::gaussian::Fit::h\n Protected Attribute Details # **dt_** # data_t dt_ {}; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **m_** # Vector m_; **Q_** # Matrix Q_; **C_** # Matrix C_; **d_** # Vector d_; **R_** # Matrix R_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":"lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/","title":"lds::gaussian::Controller","section":"Classes","content":"lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/","title":"lds::gaussian::Fit","section":"Classes","content":"lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/","title":"lds::gaussian::FitEM","section":"Classes","content":"lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/","title":"lds::gaussian::FitSSID","section":"Classes","content":"lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":"lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/","title":"lds::gaussian::System","section":"Classes","content":"lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\n n_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n R # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\n Protected Attribute Details # **R_** # Matrix R_; **do_recurse_Ke_** # bool do_recurse_Ke_ {}; Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":"lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/","title":"lds::poisson::Controller","section":"Classes","content":"lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/","title":"lds::poisson::Fit","section":"Classes","content":"lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/","title":"lds::poisson::FitEM","section":"Classes","content":"lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/","title":"lds::poisson::FitSSID","section":"Classes","content":"lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/","title":"lds::poisson::SwitchedController","section":"Classes","content":"lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/","title":"lds::poisson::System","section":"Classes","content":"lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\n Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/","title":"lds::SSID","section":"Classes","content":"lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\n n_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\n ssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n Protected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\n t_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\n DecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::poisson::FitSSID::DecomposeData, lds::gaussian::FitSSID::DecomposeData\n CalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\n ssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\n wt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **D_** # Matrix D_; **fit_** # Fit fit_; **g_dc_** # Matrix g_dc_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_h_** # size_t n_h_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; **L_** # Matrix L_; **s_** # Vector s_; **ext_obs_t_** # Matrix ext_obs_t_; Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/","title":"lds::SwitchedController","section":"Classes","content":"lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\n idx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # **systems_** # std::vector\u0026lt; System \u0026gt; systems_; **n_sys_** # size_t n_sys_ {}; **idx_** # size_t idx_ {}; **Kc_list_** # UniformMatrixList Kc_list_; **Kc_inty_list_** # UniformMatrixList Kc_inty_list_; **Kc_u_list_** # UniformMatrixList Kc_u_list_; **g_design_list_** # UniformVectorList g_design_list_; Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1system/","title":"lds::System","section":"Classes","content":"lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\n u_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\n Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\n u_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::poisson::System::Simulate, lds::gaussian::System::Simulate\n f # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\n u input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::poisson::System::h, lds::gaussian::System::h\n n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::poisson::System::RecurseKe, lds::gaussian::System::RecurseKe\n InitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # **n_x_** # std::size_t n_x_ {}; **n_u_** # std::size_t n_u_ {}; **n_y_** # std::size_t n_y_ {}; **dt_** # data_t dt_ {}; **x_** # Vector x_; **P_** # Matrix P_; **m_** # Vector m_; **P_m_** # Matrix P_m_; **cx_** # Vector cx_; **y_** # Vector y_; **z_** # Vector z_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **m0_** # Vector m0_; **P0_m_** # Matrix P0_m_; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **Q_** # Matrix Q_; **Q_m_** # Matrix Q_m_; **C_** # Matrix C_; **d_** # Vector d_; **Ke_** # Matrix Ke_; **Ke_m_** # Matrix Ke_m_; Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/","title":"lds::UniformMatrixList","section":"Classes","content":"lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\n n [optional] index in list of matrices Return: dimensions\n size # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\n that input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/","title":"lds::UniformSystemList","section":"Classes","content":"lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System\u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\n that input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n operator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/","title":"lds::UniformVectorList","section":"Classes","content":"lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\n that input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n operator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n Updated on 4 May 2022 at 16:57:50 Eastern Daylight Time\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":"ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":"ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34;// system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_gaussian_sys.h\u0026#34;// controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34;// switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_,R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_,Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_,Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system type #include \u0026#34;lds_poisson_sys.h\u0026#34;// control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34;#include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34;#include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign=false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; public: using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Matrix tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34;// System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 4 May 2022 at 16:57:51 Eastern Daylight Time\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":"ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":"ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34;#include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":"Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\r\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\r\\] \\[\r\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\r\\] t : time index\rx : system state\rv = g%u : input (e.g., in physical units used for model fit)\ru : control signal sent to actuator (e.g., in Volts)\ry : system output\rm : process disturbance\rw ~ N(0, Q) : process noise/disturbance\rA : state matrix\rB : input coupling matrix\rg : input gain (e.g., for converting to control signal actuator voltage)\rn.b., assumes this conversion is linear\rQ : process noise covariance\r% : element-wise multiplication\r LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n \\[\r\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\r\\] \\[\r\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\r\\] z : measurement\rC : output matrix\rd : output bias\rR : measurement noise covariance\r LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n \\[\ry_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\r\\] \\[\rz_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\r\\] i : output index\rz : measurement (count data)\rc : i^th row of output matrix (C)\rd : output bias\r "},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":"Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\n Defaults\n Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":"Namespaces # armamexc arma/mex interface using Matlab C API\n armamexcpp arma/mex interface using Matlab C++ API\n lds::gaussian Linear Dynamical Systems with Gaussian observations.\n lds::poisson Linear Dynamical Systems with Poisson observations.\n Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":"Pages # Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":"PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = \\exp\\left(x_t\\right)\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":"PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":"src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":"src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":"src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":"src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":"src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":"src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 4 May 2022 at 16:57:52 Eastern Daylight Time\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.9ed82488f1147302e58d3c3e65dd55018a6e72b6e0fd48f0583a54196c1ecb52.json b/docs/en.search-data.min.9ed82488f1147302e58d3c3e65dd55018a6e72b6e0fd48f0583a54196c1ecb52.json deleted file mode 100644 index cab699e7..00000000 --- a/docs/en.search-data.min.9ed82488f1147302e58d3c3e65dd55018a6e72b6e0fd48f0583a54196c1ecb52.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":"LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":"Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":"Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":"Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone --recurse-submodules https://github.com/stanley-rozell/lds-ctrl-est.git cd lds-ctrl-est\r# use git submodule update --init if you clone the repo without --recurse-submodules\rCompilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build .\rThe first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\n LDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\n For basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\n LD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\nThe bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory.\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build might not work for both the standalone library and the Python package (especially on Windows), since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nConsiderations for Windows # First of all, development on Windows has been more prone to bugs, so if you encounter many problems, consider using a Unix-based system—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nYou will likely need to get compiler tools through the Visual Studio installer. Compilation was tested in VS Code using the following kit:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64)\rCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix On Windows, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":4,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":"Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":5,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":"armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; auto m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; auto m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; auto a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; auto a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline auto m2T_scalar( const mxArray * matlab_scalar ) Parameters:\n matlab_scalar matlab scalar Template Parameters:\n T type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline auto m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\n matlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline auto a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\n arma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline auto a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\n arma_vec armadillo vector Return: matlab vector\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":"armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\n matlab_cell matlab cell Template Parameters:\n T type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab matrix Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n std_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":7,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":"Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\r\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\r\\] \\[\r\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\r\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n \\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\r\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\r\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\r\\] An additional option available to users is a control law that updates the change in u,\n \\[\r\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] \\[\r\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\r\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":8,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":"Classes # lds::Controller\n lds::EM\n lds::Fit LDS Fit Type.\n lds::SSID\n lds::SwitchedController SwitchedController Type.\n lds::System Linear Dynamical System Type.\n lds::UniformMatrixList\n lds::UniformSystemList\n lds::UniformVectorList\n lds::gaussian::Controller Gaussian-observation Controller Type.\n lds::gaussian::Fit GLDS Fit Type.\n lds::gaussian::FitEM GLDS E-M Fit Type.\n lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\n lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\n lds::gaussian::System Gaussian LDS Type.\n lds::poisson::Controller PLDS Controller Type.\n lds::poisson::Fit PLDS Fit Type.\n lds::poisson::FitEM PLDS E-M Fit Type.\n lds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\n lds::poisson::SwitchedController Poisson-observation SwitchedController Type.\n lds::poisson::System Poisson System type.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":"Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":"Defaults # \nMore\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":"eg_glds_ctrl.cpp # Example GLDS Control\n//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":"eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\n//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":"eg_plds_ctrl.cpp # Example PLDS Control\n//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":"eg_plds_est.cpp # Example PLDS Estimation\n//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":"eg_plds_switched_ctrl.cpp # Example Switched PLDS Control\n//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":"Examples # eg_glds_ctrl.cpp\n eg_glds_du_plds_ctrl.cpp\n eg_plds_ctrl.cpp\n eg_plds_est.cpp\n eg_plds_switched_ctrl.cpp\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":"examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":"examples/eg_glds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":"examples/eg_glds_du_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":"examples/eg_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":"examples/eg_plds_est.cpp # Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Function Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":"examples/eg_plds_switched_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":"Files # examples/eg_glds_ctrl.cpp\n examples/eg_glds_du_plds_ctrl.cpp\n examples/eg_plds_ctrl.cpp\n examples/eg_plds_est.cpp\n examples/eg_plds_switched_ctrl.cpp\n ldsCtrlEst_h/lds.h lds namespace\n ldsCtrlEst_h/lds_ctrl.h Controller.\n ldsCtrlEst_h/lds_fit.h LDS base fit type.\n ldsCtrlEst_h/lds_fit_em.h subspace identification\n ldsCtrlEst_h/lds_fit_ssid.h subspace identification\n ldsCtrlEst_h/lds_gaussian.h glds namespace\n ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\n ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\n ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\n ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\n ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\n ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\n ldsCtrlEst_h/lds_poisson.h plds namespace\n ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\n ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\n ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\n ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\n ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\n ldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\n ldsCtrlEst_h/lds_sctrl.h SwitchedController type.\n ldsCtrlEst_h/lds_sys.h LDS base type.\n ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\n ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\n ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\n ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\n ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\n src/lds.cpp misc lds namespace functions\n src/lds_gaussian_sys.cpp GLDS base type.\n src/lds_poisson_sys.cpp PLDS base type.\n src/lds_sys.cpp LDS base type.\n src/lds_uniform_vecs.cpp Uniformly sized vectors.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":24,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":"GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":25,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":"include # Directories # Name ldsCtrlEst_h Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":"lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m const data_t kInf Some useful numbers. const data_t kPi const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = typedef double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = typedef arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = typedef arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = typedef arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = typedef arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\n X mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\n X mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\n L lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\n A some matrix B some other matrix Return: covariance\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nkInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1controller/","title":"lds::Controller","section":"Classes","content":"lds::Controller # More\u0026hellip;\nInherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController\u0026lt; System \u0026gt;\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\n ControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\n sys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::poisson::SwitchedController::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::gaussian::Controller::set_y_ref\n set_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\n control_type control type bit mask Template Parameters:\n System type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\n u_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\n u_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # **sys_** # System sys_; **u_** # Vector u_; **u_return_** # Vector u_return_; **g_design_** # Vector g_design_; **u_ref_** # Vector u_ref_; **u_ref_prev_** # Vector u_ref_prev_; **x_ref_** # Vector x_ref_; **y_ref_** # Vector y_ref_; **cx_ref_** # Vector cx_ref_; **Kc_** # Matrix Kc_; **Kc_u_** # Matrix Kc_u_; **Kc_inty_** # Matrix Kc_inty_; **du_ref_** # Vector du_ref_; **dv_ref_** # Vector dv_ref_; **v_ref_** # Vector v_ref_; **dv_** # Vector dv_; **v_** # Vector v_; **int_e_** # Vector int_e_; **int_e_awu_adjust_** # Vector int_e_awu_adjust_; **u_sat_** # Vector u_sat_; **do_control_prev_** # bool do_control_prev_ = false; **do_lock_control_prev_** # bool do_lock_control_prev_ = false; **u_saturated_** # bool u_saturated_ = false; **u_lb_** # data_t u_lb_ {}; **u_ub_** # data_t u_ub_ {}; **tau_awu_** # data_t tau_awu_ {}; **k_awu_** # data_t k_awu_ = 0; **t_since_control_onset_** # data_t t_since_control_onset_ = 0; **control_type_** # size_t control_type_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1em/","title":"lds::EM","section":"Classes","content":"lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n n_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n fit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\n calc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n x # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\n force_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\n calc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\n MaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\n Smooth # void Smooth( bool force_common_initial ) Parameters:\n force_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\n Ke estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\n Reset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\n Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **x_** # std::vector\u0026lt; Matrix \u0026gt; x_; **P_** # std::vector\u0026lt; Cube \u0026gt; P_; **P_t_tm1_** # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; **y_** # std::vector\u0026lt; Matrix \u0026gt; y_; **diag_y_** # Matrix diag_y_; **sum_E_x_t_x_t_** # Matrix sum_E_x_t_x_t_; **sum_E_xu_tm1_xu_tm1_** # Matrix sum_E_xu_tm1_xu_tm1_; **sum_E_xu_t_xu_tm1_** # Matrix sum_E_xu_t_xu_tm1_; **fit_** # Fit fit_; **theta_** # Vector theta_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1fit/","title":"lds::Fit","section":"Classes","content":"lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\n set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\n set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\n x state estimate (over time) u input (over time) t time index Return: view of updated state\n f # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\n x_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\n h # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::poisson::Fit::h, lds::gaussian::Fit::h\n Protected Attribute Details # **dt_** # data_t dt_ {}; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **m_** # Vector m_; **Q_** # Matrix Q_; **C_** # Matrix C_; **d_** # Vector d_; **R_** # Matrix R_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":"lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/","title":"lds::gaussian::Controller","section":"Classes","content":"lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/","title":"lds::gaussian::Fit","section":"Classes","content":"lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/","title":"lds::gaussian::FitEM","section":"Classes","content":"lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/","title":"lds::gaussian::FitSSID","section":"Classes","content":"lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":"lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/","title":"lds::gaussian::System","section":"Classes","content":"lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\n n_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n R # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\n Protected Attribute Details # **R_** # Matrix R_; **do_recurse_Ke_** # bool do_recurse_Ke_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":"lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/","title":"lds::poisson::Controller","section":"Classes","content":"lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/","title":"lds::poisson::Fit","section":"Classes","content":"lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/","title":"lds::poisson::FitEM","section":"Classes","content":"lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/","title":"lds::poisson::FitSSID","section":"Classes","content":"lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/","title":"lds::poisson::SwitchedController","section":"Classes","content":"lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/","title":"lds::poisson::System","section":"Classes","content":"lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/","title":"lds::SSID","section":"Classes","content":"lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\n n_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\n ssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n Protected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\n t_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\n DecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::poisson::FitSSID::DecomposeData, lds::gaussian::FitSSID::DecomposeData\n CalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\n ssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\n wt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **D_** # Matrix D_; **fit_** # Fit fit_; **g_dc_** # Matrix g_dc_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_h_** # size_t n_h_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; **L_** # Matrix L_; **s_** # Vector s_; **ext_obs_t_** # Matrix ext_obs_t_; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/","title":"lds::SwitchedController","section":"Classes","content":"lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\n idx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # **systems_** # std::vector\u0026lt; System \u0026gt; systems_; **n_sys_** # size_t n_sys_ {}; **idx_** # size_t idx_ {}; **Kc_list_** # UniformMatrixList Kc_list_; **Kc_inty_list_** # UniformMatrixList Kc_inty_list_; **Kc_u_list_** # UniformMatrixList Kc_u_list_; **g_design_list_** # UniformVectorList g_design_list_; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1system/","title":"lds::System","section":"Classes","content":"lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\n u_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\n Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\n u_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::poisson::System::Simulate, lds::gaussian::System::Simulate\n f # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\n u input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::poisson::System::h, lds::gaussian::System::h\n n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::poisson::System::RecurseKe, lds::gaussian::System::RecurseKe\n InitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # **n_x_** # std::size_t n_x_ {}; **n_u_** # std::size_t n_u_ {}; **n_y_** # std::size_t n_y_ {}; **dt_** # data_t dt_ {}; **x_** # Vector x_; **P_** # Matrix P_; **m_** # Vector m_; **P_m_** # Matrix P_m_; **cx_** # Vector cx_; **y_** # Vector y_; **z_** # Vector z_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **m0_** # Vector m0_; **P0_m_** # Matrix P0_m_; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **Q_** # Matrix Q_; **Q_m_** # Matrix Q_m_; **C_** # Matrix C_; **d_** # Vector d_; **Ke_** # Matrix Ke_; **Ke_m_** # Matrix Ke_m_; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/","title":"lds::UniformMatrixList","section":"Classes","content":"lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\n n [optional] index in list of matrices Return: dimensions\n size # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\n that input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/","title":"lds::UniformSystemList","section":"Classes","content":"lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System\u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\n that input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n operator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/","title":"lds::UniformVectorList","section":"Classes","content":"lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\n that input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n operator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":"ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":"ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34;// system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_gaussian_sys.h\u0026#34;// controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34;// switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_,R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_,Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_,Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system type #include \u0026#34;lds_poisson_sys.h\u0026#34;// control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34;#include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34;#include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign=false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; public: using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Matrix tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34;// System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":"ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":"ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34;#include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":77,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":"Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\r\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\r\\] \\[\r\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\r\\] t : time index\rx : system state\rv = g%u : input (e.g., in physical units used for model fit)\ru : control signal sent to actuator (e.g., in Volts)\ry : system output\rm : process disturbance\rw ~ N(0, Q) : process noise/disturbance\rA : state matrix\rB : input coupling matrix\rg : input gain (e.g., for converting to control signal actuator voltage)\rn.b., assumes this conversion is linear\rQ : process noise covariance\r% : element-wise multiplication\r LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n \\[\r\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\r\\] \\[\r\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\r\\] z : measurement\rC : output matrix\rd : output bias\rR : measurement noise covariance\r LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n \\[\ry_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\r\\] \\[\rz_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\r\\] i : output index\rz : measurement (count data)\rc : i^th row of output matrix (C)\rd : output bias\r "},{"id":78,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":"Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\n Defaults\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":"Namespaces # armamexc arma/mex interface using Matlab C API\n armamexcpp arma/mex interface using Matlab C++ API\n lds::gaussian Linear Dynamical Systems with Gaussian observations.\n lds::poisson Linear Dynamical Systems with Poisson observations.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":"Pages # Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":81,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":"PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = \\exp\\left(x_t\\right)\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":"PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":83,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":"src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":"src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":"src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":"src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":"src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":"src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.cdba5d21934f88c8babfbddd59af83b0207389c62d2c3a11b31bc18fbe5810bb.json b/docs/en.search-data.min.cdba5d21934f88c8babfbddd59af83b0207389c62d2c3a11b31bc18fbe5810bb.json deleted file mode 100644 index 6afe403c..00000000 --- a/docs/en.search-data.min.cdba5d21934f88c8babfbddd59af83b0207389c62d2c3a11b31bc18fbe5810bb.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":"LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":"Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":"Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":"Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\n Ubuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\n Use different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake and gfortran:\nbrew install cmake gfortran Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init\rCompilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build .\rThe first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\n LDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\n For basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\n LD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\n Could not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n "},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":"Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":"Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":"armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; auto m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; auto m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; auto a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; auto a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline auto m2T_scalar( const mxArray * matlab_scalar ) Parameters:\n matlab_scalar matlab scalar Template Parameters:\n T type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline auto m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\n matlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline auto a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\n arma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline auto a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\n arma_vec armadillo vector Return: matlab vector\n Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":"armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\n matlab_cell matlab cell Template Parameters:\n T type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab matrix Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n std_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\n Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":"Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\r\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\r\\] \\[\r\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\r\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n \\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\r\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\r\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\r\\] An additional option available to users is a control law that updates the change in u,\n \\[\r\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] \\[\r\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\r\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":"Classes # lds::Controller\n lds::EM\n lds::Fit LDS Fit Type.\n lds::SSID\n lds::SwitchedController SwitchedController Type.\n lds::System Linear Dynamical System Type.\n lds::UniformMatrixList\n lds::UniformSystemList\n lds::UniformVectorList\n lds::gaussian::Controller Gaussian-observation Controller Type.\n lds::gaussian::Fit GLDS Fit Type.\n lds::gaussian::FitEM GLDS E-M Fit Type.\n lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\n lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\n lds::gaussian::System Gaussian LDS Type.\n lds::poisson::Controller PLDS Controller Type.\n lds::poisson::Fit PLDS Fit Type.\n lds::poisson::FitEM PLDS E-M Fit Type.\n lds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\n lds::poisson::SwitchedController Poisson-observation SwitchedController Type.\n lds::poisson::System Poisson System type.\n Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":"Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":"Defaults # \nMore\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":"eg_glds_ctrl.cpp # Example GLDS Control\n//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":"eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\n//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":"eg_plds_ctrl.cpp # Example PLDS Control\n//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":"eg_plds_est.cpp # Example PLDS Estimation\n//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":"eg_plds_switched_ctrl.cpp # Example Switched PLDS Control\n//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":17,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":"Examples # eg_glds_ctrl.cpp\n eg_glds_du_plds_ctrl.cpp\n eg_plds_ctrl.cpp\n eg_plds_est.cpp\n eg_plds_switched_ctrl.cpp\n Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":"examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":"examples/eg_glds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":"examples/eg_glds_du_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":"examples/eg_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":"examples/eg_plds_est.cpp # Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Function Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":"examples/eg_plds_switched_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":"Files # examples/eg_glds_ctrl.cpp\n examples/eg_glds_du_plds_ctrl.cpp\n examples/eg_plds_ctrl.cpp\n examples/eg_plds_est.cpp\n examples/eg_plds_switched_ctrl.cpp\n ldsCtrlEst_h/lds.h lds namespace\n ldsCtrlEst_h/lds_ctrl.h Controller.\n ldsCtrlEst_h/lds_fit.h LDS base fit type.\n ldsCtrlEst_h/lds_fit_em.h subspace identification\n ldsCtrlEst_h/lds_fit_ssid.h subspace identification\n ldsCtrlEst_h/lds_gaussian.h glds namespace\n ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\n ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\n ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\n ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\n ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\n ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\n ldsCtrlEst_h/lds_poisson.h plds namespace\n ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\n ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\n ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\n ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\n ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\n ldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\n ldsCtrlEst_h/lds_sctrl.h SwitchedController type.\n ldsCtrlEst_h/lds_sys.h LDS base type.\n ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\n ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\n ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\n ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\n ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\n src/lds.cpp misc lds namespace functions\n src/lds_gaussian_sys.cpp GLDS base type.\n src/lds_poisson_sys.cpp PLDS base type.\n src/lds_sys.cpp LDS base type.\n src/lds_uniform_vecs.cpp Uniformly sized vectors.\n Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":"GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":"include # Directories # Name ldsCtrlEst_h Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":"lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m const data_t kInf Some useful numbers. const data_t kPi const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = typedef double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = typedef arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = typedef arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = typedef arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = typedef arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\n X mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\n X mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\n L lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\n A some matrix B some other matrix Return: covariance\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nkInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1controller/","title":"lds::Controller","section":"Classes","content":"lds::Controller # More\u0026hellip;\nInherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController\u0026lt; System \u0026gt;\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\n ControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\n sys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::poisson::SwitchedController::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::gaussian::Controller::set_y_ref\n set_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\n control_type control type bit mask Template Parameters:\n System type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\n u_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\n u_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # **sys_** # System sys_; **u_** # Vector u_; **u_return_** # Vector u_return_; **g_design_** # Vector g_design_; **u_ref_** # Vector u_ref_; **u_ref_prev_** # Vector u_ref_prev_; **x_ref_** # Vector x_ref_; **y_ref_** # Vector y_ref_; **cx_ref_** # Vector cx_ref_; **Kc_** # Matrix Kc_; **Kc_u_** # Matrix Kc_u_; **Kc_inty_** # Matrix Kc_inty_; **du_ref_** # Vector du_ref_; **dv_ref_** # Vector dv_ref_; **v_ref_** # Vector v_ref_; **dv_** # Vector dv_; **v_** # Vector v_; **int_e_** # Vector int_e_; **int_e_awu_adjust_** # Vector int_e_awu_adjust_; **u_sat_** # Vector u_sat_; **do_control_prev_** # bool do_control_prev_ = false; **do_lock_control_prev_** # bool do_lock_control_prev_ = false; **u_saturated_** # bool u_saturated_ = false; **u_lb_** # data_t u_lb_ {}; **u_ub_** # data_t u_ub_ {}; **tau_awu_** # data_t tau_awu_ {}; **k_awu_** # data_t k_awu_ = 0; **t_since_control_onset_** # data_t t_since_control_onset_ = 0; **control_type_** # size_t control_type_ {}; Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1em/","title":"lds::EM","section":"Classes","content":"lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n n_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n fit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\n calc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n x # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\n force_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\n calc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\n MaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\n Smooth # void Smooth( bool force_common_initial ) Parameters:\n force_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\n Ke estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\n Reset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\n Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **x_** # std::vector\u0026lt; Matrix \u0026gt; x_; **P_** # std::vector\u0026lt; Cube \u0026gt; P_; **P_t_tm1_** # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; **y_** # std::vector\u0026lt; Matrix \u0026gt; y_; **diag_y_** # Matrix diag_y_; **sum_E_x_t_x_t_** # Matrix sum_E_x_t_x_t_; **sum_E_xu_tm1_xu_tm1_** # Matrix sum_E_xu_tm1_xu_tm1_; **sum_E_xu_t_xu_tm1_** # Matrix sum_E_xu_t_xu_tm1_; **fit_** # Fit fit_; **theta_** # Vector theta_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1fit/","title":"lds::Fit","section":"Classes","content":"lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\n set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\n set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\n x state estimate (over time) u input (over time) t time index Return: view of updated state\n f # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\n x_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\n h # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::poisson::Fit::h, lds::gaussian::Fit::h\n Protected Attribute Details # **dt_** # data_t dt_ {}; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **m_** # Vector m_; **Q_** # Matrix Q_; **C_** # Matrix C_; **d_** # Vector d_; **R_** # Matrix R_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":"lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/","title":"lds::gaussian::Controller","section":"Classes","content":"lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/","title":"lds::gaussian::Fit","section":"Classes","content":"lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/","title":"lds::gaussian::FitEM","section":"Classes","content":"lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM).\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/","title":"lds::gaussian::FitSSID","section":"Classes","content":"lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":"lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/","title":"lds::gaussian::System","section":"Classes","content":"lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\n n_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n R # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\n Protected Attribute Details # **R_** # Matrix R_; **do_recurse_Ke_** # bool do_recurse_Ke_ {}; Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":"lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/","title":"lds::poisson::Controller","section":"Classes","content":"lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/","title":"lds::poisson::Fit","section":"Classes","content":"lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/","title":"lds::poisson::FitEM","section":"Classes","content":"lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM).\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/","title":"lds::poisson::FitSSID","section":"Classes","content":"lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/","title":"lds::poisson::SwitchedController","section":"Classes","content":"lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/","title":"lds::poisson::System","section":"Classes","content":"lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/","title":"lds::SSID","section":"Classes","content":"lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\n n_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\n ssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n Protected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\n t_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\n DecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::poisson::FitSSID::DecomposeData, lds::gaussian::FitSSID::DecomposeData\n CalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\n ssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\n wt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **D_** # Matrix D_; **fit_** # Fit fit_; **g_dc_** # Matrix g_dc_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_h_** # size_t n_h_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; **L_** # Matrix L_; **s_** # Vector s_; **ext_obs_t_** # Matrix ext_obs_t_; Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/","title":"lds::SwitchedController","section":"Classes","content":"lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\n idx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # **systems_** # std::vector\u0026lt; System \u0026gt; systems_; **n_sys_** # size_t n_sys_ {}; **idx_** # size_t idx_ {}; **Kc_list_** # UniformMatrixList Kc_list_; **Kc_inty_list_** # UniformMatrixList Kc_inty_list_; **Kc_u_list_** # UniformMatrixList Kc_u_list_; **g_design_list_** # UniformVectorList g_design_list_; Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1system/","title":"lds::System","section":"Classes","content":"lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\n u_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\n Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\n u_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::poisson::System::Simulate, lds::gaussian::System::Simulate\n f # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\n u input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::poisson::System::h, lds::gaussian::System::h\n n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::poisson::System::RecurseKe, lds::gaussian::System::RecurseKe\n InitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # **n_x_** # std::size_t n_x_ {}; **n_u_** # std::size_t n_u_ {}; **n_y_** # std::size_t n_y_ {}; **dt_** # data_t dt_ {}; **x_** # Vector x_; **P_** # Matrix P_; **m_** # Vector m_; **P_m_** # Matrix P_m_; **cx_** # Vector cx_; **y_** # Vector y_; **z_** # Vector z_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **m0_** # Vector m0_; **P0_m_** # Matrix P0_m_; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **Q_** # Matrix Q_; **Q_m_** # Matrix Q_m_; **C_** # Matrix C_; **d_** # Vector d_; **Ke_** # Matrix Ke_; **Ke_m_** # Matrix Ke_m_; Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/","title":"lds::UniformMatrixList","section":"Classes","content":"lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\n n [optional] index in list of matrices Return: dimensions\n size # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\n that input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/","title":"lds::UniformSystemList","section":"Classes","content":"lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System\u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\n that input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n operator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/","title":"lds::UniformVectorList","section":"Classes","content":"lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\n that input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n operator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":"ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":"ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34;// system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_gaussian_sys.h\u0026#34;// controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34;// switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_,R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_,Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_,Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system type #include \u0026#34;lds_poisson_sys.h\u0026#34;// control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34;#include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34;#include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign=false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; public: using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Matrix tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34;// System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":"ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":"ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34;#include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":"Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\r\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\r\\] \\[\r\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\r\\] t : time index\rx : system state\rv = g%u : input (e.g., in physical units used for model fit)\ru : control signal sent to actuator (e.g., in Volts)\ry : system output\rm : process disturbance\rw ~ N(0, Q) : process noise/disturbance\rA : state matrix\rB : input coupling matrix\rg : input gain (e.g., for converting to control signal actuator voltage)\rn.b., assumes this conversion is linear\rQ : process noise covariance\r% : element-wise multiplication\r LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n \\[\r\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\r\\] \\[\r\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\r\\] z : measurement\rC : output matrix\rd : output bias\rR : measurement noise covariance\r LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n \\[\ry_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\r\\] \\[\rz_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\r\\] i : output index\rz : measurement (count data)\rc : i^th row of output matrix (C)\rd : output bias\r "},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":"Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\n Defaults\n Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":"Namespaces # armamexc arma/mex interface using Matlab C API\n armamexcpp arma/mex interface using Matlab C++ API\n lds::gaussian Linear Dynamical Systems with Gaussian observations.\n lds::poisson Linear Dynamical Systems with Poisson observations.\n Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":"Pages # Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":"PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = \\exp\\left(x_t\\right)\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":"PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":"src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":"src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":"src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":"src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":"src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":"src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.dd6d5ec1dc04e75b58c8053ebd9a205c919f3fc365f221e7bb9b64945879058e.json b/docs/en.search-data.min.dd6d5ec1dc04e75b58c8053ebd9a205c919f3fc365f221e7bb9b64945879058e.json deleted file mode 100644 index c6932a81..00000000 --- a/docs/en.search-data.min.dd6d5ec1dc04e75b58c8053ebd9a205c919f3fc365f221e7bb9b64945879058e.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":"LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":"Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":"Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":"Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nN.B. # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably:\n Ubuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Mac pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake and gfortran:\nbrew install cmake gfortran Linux pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential Windows installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init\rCompilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build .\rThe first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\n LDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\n For basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\n LD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again.\n Could not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n "},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":"Windows installation # Windows pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":"Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":"armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; auto m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; auto m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; auto a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; auto a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline auto m2T_scalar( const mxArray * matlab_scalar ) Parameters:\n matlab_scalar matlab scalar Template Parameters:\n T type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline auto m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\n matlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline auto a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\n arma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline auto a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\n arma_vec armadillo vector Return: matlab vector\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":"armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\n matlab_cell matlab cell Template Parameters:\n T type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab array Template Parameters:\n T type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\n matlab_array matlab matrix Template Parameters:\n T type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n arma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\n std_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\n T type Return: matlab matrix\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":"Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\r\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\r\\] \\[\r\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\r\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n \\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\r\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\r\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n \\[\r\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\r\\] An additional option available to users is a control law that updates the change in u,\n \\[\r\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\r\\] \\[\r\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\r\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":"Classes # lds::Controller\n lds::EM\n lds::Fit LDS Fit Type.\n lds::SSID\n lds::SwitchedController SwitchedController Type.\n lds::System Linear Dynamical System Type.\n lds::UniformMatrixList\n lds::UniformSystemList\n lds::UniformVectorList\n lds::gaussian::Controller Gaussian-observation Controller Type.\n lds::gaussian::Fit GLDS Fit Type.\n lds::gaussian::FitEM GLDS E-M Fit Type.\n lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\n lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\n lds::gaussian::System Gaussian LDS Type.\n lds::poisson::Controller PLDS Controller Type.\n lds::poisson::Fit PLDS Fit Type.\n lds::poisson::FitEM PLDS E-M Fit Type.\n lds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\n lds::poisson::SwitchedController Poisson-observation SwitchedController Type.\n lds::poisson::System Poisson System type.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":"Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":"Defaults # \nMore\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":"eg_glds_ctrl.cpp # Example GLDS Control\n//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":"eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\n//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":"eg_plds_ctrl.cpp # Example PLDS Control\n//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":"eg_plds_est.cpp # Example PLDS Estimation\n//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":"eg_plds_switched_ctrl.cpp # Example Switched PLDS Control\n//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":17,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":"Examples # eg_glds_ctrl.cpp\n eg_glds_du_plds_ctrl.cpp\n eg_plds_ctrl.cpp\n eg_plds_est.cpp\n eg_plds_switched_ctrl.cpp\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":"examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":"examples/eg_glds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":"examples/eg_glds_du_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":"examples/eg_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":"examples/eg_plds_est.cpp # Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Function Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":"examples/eg_plds_switched_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":"Files # examples/eg_glds_ctrl.cpp\n examples/eg_glds_du_plds_ctrl.cpp\n examples/eg_plds_ctrl.cpp\n examples/eg_plds_est.cpp\n examples/eg_plds_switched_ctrl.cpp\n ldsCtrlEst_h/lds.h lds namespace\n ldsCtrlEst_h/lds_ctrl.h Controller.\n ldsCtrlEst_h/lds_fit.h LDS base fit type.\n ldsCtrlEst_h/lds_fit_em.h subspace identification\n ldsCtrlEst_h/lds_fit_ssid.h subspace identification\n ldsCtrlEst_h/lds_gaussian.h glds namespace\n ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\n ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\n ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\n ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\n ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\n ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\n ldsCtrlEst_h/lds_poisson.h plds namespace\n ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\n ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\n ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\n ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\n ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\n ldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\n ldsCtrlEst_h/lds_sctrl.h SwitchedController type.\n ldsCtrlEst_h/lds_sys.h LDS base type.\n ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\n ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\n ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\n ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\n ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\n src/lds.cpp misc lds namespace functions\n src/lds_gaussian_sys.cpp GLDS base type.\n src/lds_poisson_sys.cpp PLDS base type.\n src/lds_sys.cpp LDS base type.\n src/lds_uniform_vecs.cpp Uniformly sized vectors.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":"GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":"include # Directories # Name ldsCtrlEst_h Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":"lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m const data_t kInf Some useful numbers. const data_t kPi const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = typedef double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = typedef arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = typedef arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = typedef arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = typedef arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\n some some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\n X mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\n X mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\n L lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\n A some matrix B some other matrix Return: covariance\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nkInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1controller/","title":"lds::Controller","section":"Classes","content":"lds::Controller # More\u0026hellip;\nInherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController\u0026lt; System \u0026gt;\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n sys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\n System type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\n ControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\n z measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\n sys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::poisson::SwitchedController::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::gaussian::Controller::set_y_ref\n set_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\n control_type control type bit mask Template Parameters:\n System type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\n u_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\n u_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # **sys_** # System sys_; **u_** # Vector u_; **u_return_** # Vector u_return_; **g_design_** # Vector g_design_; **u_ref_** # Vector u_ref_; **u_ref_prev_** # Vector u_ref_prev_; **x_ref_** # Vector x_ref_; **y_ref_** # Vector y_ref_; **cx_ref_** # Vector cx_ref_; **Kc_** # Matrix Kc_; **Kc_u_** # Matrix Kc_u_; **Kc_inty_** # Matrix Kc_inty_; **du_ref_** # Vector du_ref_; **dv_ref_** # Vector dv_ref_; **v_ref_** # Vector v_ref_; **dv_** # Vector dv_; **v_** # Vector v_; **int_e_** # Vector int_e_; **int_e_awu_adjust_** # Vector int_e_awu_adjust_; **u_sat_** # Vector u_sat_; **do_control_prev_** # bool do_control_prev_ = false; **do_lock_control_prev_** # bool do_lock_control_prev_ = false; **u_saturated_** # bool u_saturated_ = false; **u_lb_** # data_t u_lb_ {}; **u_ub_** # data_t u_ub_ {}; **tau_awu_** # data_t tau_awu_ {}; **k_awu_** # data_t k_awu_ = 0; **t_since_control_onset_** # data_t t_since_control_onset_ = 0; **control_type_** # size_t control_type_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1em/","title":"lds::EM","section":"Classes","content":"lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n n_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\n fit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\n calc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n x # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\n force_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\n calc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\n MaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\n Smooth # void Smooth( bool force_common_initial ) Parameters:\n force_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\n Ke estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\n Reset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\n Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **x_** # std::vector\u0026lt; Matrix \u0026gt; x_; **P_** # std::vector\u0026lt; Cube \u0026gt; P_; **P_t_tm1_** # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; **y_** # std::vector\u0026lt; Matrix \u0026gt; y_; **diag_y_** # Matrix diag_y_; **sum_E_x_t_x_t_** # Matrix sum_E_x_t_x_t_; **sum_E_xu_tm1_xu_tm1_** # Matrix sum_E_xu_tm1_xu_tm1_; **sum_E_xu_t_xu_tm1_** # Matrix sum_E_xu_t_xu_tm1_; **fit_** # Fit fit_; **theta_** # Vector theta_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1fit/","title":"lds::Fit","section":"Classes","content":"lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\n set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\n set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\n x state estimate (over time) u input (over time) t time index Return: view of updated state\n f # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\n x_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\n h # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::poisson::Fit::h, lds::gaussian::Fit::h\n Protected Attribute Details # **dt_** # data_t dt_ {}; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **m_** # Vector m_; **Q_** # Matrix Q_; **C_** # Matrix C_; **d_** # Vector d_; **R_** # Matrix R_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":"lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/","title":"lds::gaussian::Controller","section":"Classes","content":"lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/","title":"lds::gaussian::Fit","section":"Classes","content":"lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/","title":"lds::gaussian::FitEM","section":"Classes","content":"lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/","title":"lds::gaussian::FitSSID","section":"Classes","content":"lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":"lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/","title":"lds::gaussian::System","section":"Classes","content":"lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\n n_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n R # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\n Protected Attribute Details # **R_** # Matrix R_; **do_recurse_Ke_** # bool do_recurse_Ke_ {}; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":"lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/","title":"lds::poisson::Controller","section":"Classes","content":"lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/","title":"lds::poisson::Fit","section":"Classes","content":"lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\n Name virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\n Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\n y output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\n set_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\n R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/","title":"lds::poisson::FitEM","section":"Classes","content":"lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM).\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/","title":"lds::poisson::FitSSID","section":"Classes","content":"lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\n Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/","title":"lds::poisson::SwitchedController","section":"Classes","content":"lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\n Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/","title":"lds::poisson::System","section":"Classes","content":"lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\n Name virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\n Name void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\n Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\n Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\n u_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\n Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\n RecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/","title":"lds::SSID","section":"Classes","content":"lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\n n_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\n ssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\n ReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\n Protected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\n t_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\n DecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::poisson::FitSSID::DecomposeData, lds::gaussian::FitSSID::DecomposeData\n CalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\n ssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\n wt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # **u_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; **z_** # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; **D_** # Matrix D_; **fit_** # Fit fit_; **g_dc_** # Matrix g_dc_; **dt_** # data_t dt_ {}; **n_u_** # size_t n_u_ {}; **n_x_** # size_t n_x_ {}; **n_y_** # size_t n_y_ {}; **n_h_** # size_t n_h_ {}; **n_trials_** # size_t n_trials_ {}; **n_t_** # std::vector\u0026lt; size_t \u0026gt; n_t_; **n_t_tot_** # size_t n_t_tot_ {}; **L_** # Matrix L_; **s_** # Vector s_; **ext_obs_t_** # Matrix ext_obs_t_; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/","title":"lds::SwitchedController","section":"Classes","content":"lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\n Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\n Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates ) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\n systems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\n idx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # **systems_** # std::vector\u0026lt; System \u0026gt; systems_; **n_sys_** # size_t n_sys_ {}; **idx_** # size_t idx_ {}; **Kc_list_** # UniformMatrixList Kc_list_; **Kc_inty_list_** # UniformMatrixList Kc_inty_list_; **Kc_u_list_** # UniformMatrixList Kc_u_list_; **g_design_list_** # UniformVectorList g_design_list_; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1system/","title":"lds::System","section":"Classes","content":"lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\n n_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\n u_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\n Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\n u_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::poisson::System::Simulate, lds::gaussian::System::Simulate\n f # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\n u input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::poisson::System::h, lds::gaussian::System::h\n n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::poisson::System::RecurseKe, lds::gaussian::System::RecurseKe\n InitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # **n_x_** # std::size_t n_x_ {}; **n_u_** # std::size_t n_u_ {}; **n_y_** # std::size_t n_y_ {}; **dt_** # data_t dt_ {}; **x_** # Vector x_; **P_** # Matrix P_; **m_** # Vector m_; **P_m_** # Matrix P_m_; **cx_** # Vector cx_; **y_** # Vector y_; **z_** # Vector z_; **x0_** # Vector x0_; **P0_** # Matrix P0_; **m0_** # Vector m0_; **P0_m_** # Matrix P0_m_; **A_** # Matrix A_; **B_** # Matrix B_; **g_** # Vector g_; **Q_** # Matrix Q_; **Q_m_** # Matrix Q_m_; **C_** # Matrix C_; **d_** # Vector d_; **Ke_** # Matrix Ke_; **Ke_m_** # Matrix Ke_m_; Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/","title":"lds::UniformMatrixList","section":"Classes","content":"lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\n mats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\n n [optional] index in list of matrices Return: dimensions\n size # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\n that input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\n that another UniformMatrixList Return: reference to object\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/","title":"lds::UniformSystemList","section":"Classes","content":"lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System\u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\n systems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\n that input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n operator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformSystemList Return: reference to object\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/","title":"lds::UniformVectorList","section":"Classes","content":"lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\n vecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\n that input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n operator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\n that another UniformVectorList Return: reference to object\n Updated on 4 May 2022 at 16:59:50 Eastern Daylight Time\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":"ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":"ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34;// system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_gaussian_sys.h\u0026#34;// controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34;// switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_,R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_,Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_,Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system type #include \u0026#34;lds_poisson_sys.h\u0026#34;// control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34;#include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34;#include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34;// system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":"ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34;#include \u0026#34;lds_uniform_mats.h\u0026#34;#include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":"ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign=false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; public: using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Matrix tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34;// System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":"ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array#include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":"ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":"ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34;#include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":"Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\r\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\r\\] \\[\r\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\r\\] t : time index\rx : system state\rv = g%u : input (e.g., in physical units used for model fit)\ru : control signal sent to actuator (e.g., in Volts)\ry : system output\rm : process disturbance\rw ~ N(0, Q) : process noise/disturbance\rA : state matrix\rB : input coupling matrix\rg : input gain (e.g., for converting to control signal actuator voltage)\rn.b., assumes this conversion is linear\rQ : process noise covariance\r% : element-wise multiplication\r LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n \\[\r\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\r\\] \\[\r\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\r\\] z : measurement\rC : output matrix\rd : output bias\rR : measurement noise covariance\r LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n \\[\ry_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\r\\] \\[\rz_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\r\\] i : output index\rz : measurement (count data)\rc : i^th row of output matrix (C)\rd : output bias\r "},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":"Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\n Defaults\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":"Namespaces # armamexc arma/mex interface using Matlab C API\n armamexcpp arma/mex interface using Matlab C++ API\n lds::gaussian Linear Dynamical Systems with Gaussian observations.\n lds::poisson Linear Dynamical Systems with Poisson observations.\n Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":"Pages # Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":"PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = \\exp\\left(x_t\\right)\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":"PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":"src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":"src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":"src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":"src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":"src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":"src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 4 May 2022 at 16:59:51 Eastern Daylight Time\n"}] \ No newline at end of file diff --git a/docs/en.search.min.162b7aaf5cd0976fbdb91fda31f53d5c11f852b9340eb3cbaa440fc0fe4ceb79.js b/docs/en.search.min.162b7aaf5cd0976fbdb91fda31f53d5c11f852b9340eb3cbaa440fc0fe4ceb79.js deleted file mode 100644 index 89e02c56..00000000 --- a/docs/en.search.min.162b7aaf5cd0976fbdb91fda31f53d5c11f852b9340eb3cbaa440fc0fe4ceb79.js +++ /dev/null @@ -1 +0,0 @@ -'use strict';(function(){const g='/lds-ctrl-est/en.search-data.min.1e85f10c5ecc4f42108eae9ec00bea53e75365846aca80f6f94cdec83d505cec.json',h=Object.assign({cache:!0},{doc:{id:'id',field:['title','content'],store:['title','href','section']}}),a=document.querySelector('#book-search-input'),b=document.querySelector('#book-search-results');if(!a)return;a.addEventListener('focus',c),a.addEventListener('keyup',d),document.addEventListener('keypress',e);function e(b){if(a===document.activeElement)return;const c=String.fromCharCode(b.charCode);if(!f(c))return;a.focus(),b.preventDefault()}function f(b){const c=a.getAttribute('data-hotkeys')||'';return c.indexOf(b)>=0}function c(){a.removeEventListener('focus',c),a.required=!0,fetch(g).then(a=>a.json()).then(a=>{window.bookSearchIndex=FlexSearch.create('balance',h),window.bookSearchIndex.add(a)}).then(()=>a.required=!1).then(d)}function d(){while(b.firstChild)b.removeChild(b.firstChild);if(!a.value)return;const c=window.bookSearchIndex.search(a.value,10);c.forEach(function(a){const c=i('<li><a href></a><small></small></li>'),d=c.querySelector('a'),e=c.querySelector('small');d.href=a.href,d.textContent=a.title,e.textContent=a.section,b.appendChild(c)})}function i(b){const a=document.createElement('div');return a.innerHTML=b,a.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.2e8815e7b16be886f7411b4891dd22ab5b97100c454eaf8b746c3f9cc7f190f1.js b/docs/en.search.min.2e8815e7b16be886f7411b4891dd22ab5b97100c454eaf8b746c3f9cc7f190f1.js deleted file mode 100644 index 334482a4..00000000 --- a/docs/en.search.min.2e8815e7b16be886f7411b4891dd22ab5b97100c454eaf8b746c3f9cc7f190f1.js +++ /dev/null @@ -1 +0,0 @@ -'use strict';(function(){const g='/lds-ctrl-est/en.search-data.min.dd6d5ec1dc04e75b58c8053ebd9a205c919f3fc365f221e7bb9b64945879058e.json',h=Object.assign({cache:!0},{doc:{id:'id',field:['title','content'],store:['title','href','section']}}),a=document.querySelector('#book-search-input'),b=document.querySelector('#book-search-results');if(!a)return;a.addEventListener('focus',c),a.addEventListener('keyup',d),document.addEventListener('keypress',e);function e(b){if(a===document.activeElement)return;const c=String.fromCharCode(b.charCode);if(!f(c))return;a.focus(),b.preventDefault()}function f(b){const c=a.getAttribute('data-hotkeys')||'';return c.indexOf(b)>=0}function c(){a.removeEventListener('focus',c),a.required=!0,fetch(g).then(a=>a.json()).then(a=>{window.bookSearchIndex=FlexSearch.create('balance',h),window.bookSearchIndex.add(a)}).then(()=>a.required=!1).then(d)}function d(){while(b.firstChild)b.removeChild(b.firstChild);if(!a.value)return;const c=window.bookSearchIndex.search(a.value,10);c.forEach(function(a){const c=i('<li><a href></a><small></small></li>'),d=c.querySelector('a'),e=c.querySelector('small');d.href=a.href,d.textContent=a.title,e.textContent=a.section,b.appendChild(c)})}function i(b){const a=document.createElement('div');return a.innerHTML=b,a.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.557bfc2f9fdca6a8b972ddb85402ebca9cbcdf5bb90e37c4cb24206214b0414e.js b/docs/en.search.min.557bfc2f9fdca6a8b972ddb85402ebca9cbcdf5bb90e37c4cb24206214b0414e.js deleted file mode 100644 index bc22c122..00000000 --- a/docs/en.search.min.557bfc2f9fdca6a8b972ddb85402ebca9cbcdf5bb90e37c4cb24206214b0414e.js +++ /dev/null @@ -1 +0,0 @@ -'use strict';(function(){const g='/lds-ctrl-est/en.search-data.min.9ed82488f1147302e58d3c3e65dd55018a6e72b6e0fd48f0583a54196c1ecb52.json',h=Object.assign({cache:!0},{doc:{id:'id',field:['title','content'],store:['title','href','section']}}),a=document.querySelector('#book-search-input'),b=document.querySelector('#book-search-results');if(!a)return;a.addEventListener('focus',c),a.addEventListener('keyup',d),document.addEventListener('keypress',e);function e(b){if(a===document.activeElement)return;const c=String.fromCharCode(b.charCode);if(!f(c))return;a.focus(),b.preventDefault()}function f(b){const c=a.getAttribute('data-hotkeys')||'';return c.indexOf(b)>=0}function c(){a.removeEventListener('focus',c),a.required=!0,fetch(g).then(a=>a.json()).then(a=>{window.bookSearchIndex=FlexSearch.create('balance',h),window.bookSearchIndex.add(a)}).then(()=>a.required=!1).then(d)}function d(){while(b.firstChild)b.removeChild(b.firstChild);if(!a.value)return;const c=window.bookSearchIndex.search(a.value,10);c.forEach(function(a){const c=i('<li><a href></a><small></small></li>'),d=c.querySelector('a'),e=c.querySelector('small');d.href=a.href,d.textContent=a.title,e.textContent=a.section,b.appendChild(c)})}function i(b){const a=document.createElement('div');return a.innerHTML=b,a.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.8d74b29fa36f931cf38b54736e5a38c14b24e837def6c5f2027bc5a181d2fae0.js b/docs/en.search.min.8d74b29fa36f931cf38b54736e5a38c14b24e837def6c5f2027bc5a181d2fae0.js deleted file mode 100644 index dc634ed9..00000000 --- a/docs/en.search.min.8d74b29fa36f931cf38b54736e5a38c14b24e837def6c5f2027bc5a181d2fae0.js +++ /dev/null @@ -1 +0,0 @@ -'use strict';(function(){const g='/lds-ctrl-est/en.search-data.min.799bfc19f929cf1e231669cf06cae550679543dfef2bae61c8d816ffca12f5c7.json',h=Object.assign({cache:!0},{doc:{id:'id',field:['title','content'],store:['title','href','section']}}),a=document.querySelector('#book-search-input'),b=document.querySelector('#book-search-results');if(!a)return;a.addEventListener('focus',c),a.addEventListener('keyup',d),document.addEventListener('keypress',e);function e(b){if(a===document.activeElement)return;const c=String.fromCharCode(b.charCode);if(!f(c))return;a.focus(),b.preventDefault()}function f(b){const c=a.getAttribute('data-hotkeys')||'';return c.indexOf(b)>=0}function c(){a.removeEventListener('focus',c),a.required=!0,fetch(g).then(a=>a.json()).then(a=>{window.bookSearchIndex=FlexSearch.create('balance',h),window.bookSearchIndex.add(a)}).then(()=>a.required=!1).then(d)}function d(){while(b.firstChild)b.removeChild(b.firstChild);if(!a.value)return;const c=window.bookSearchIndex.search(a.value,10);c.forEach(function(a){const c=i('<li><a href></a><small></small></li>'),d=c.querySelector('a'),e=c.querySelector('small');d.href=a.href,d.textContent=a.title,e.textContent=a.section,b.appendChild(c)})}function i(b){const a=document.createElement('div');return a.innerHTML=b,a.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.93b2d0e7fa872893809358a01a9e6e0c0ce7a384100570924726e75a52c21d85.js b/docs/en.search.min.93b2d0e7fa872893809358a01a9e6e0c0ce7a384100570924726e75a52c21d85.js deleted file mode 100644 index 22a28216..00000000 --- a/docs/en.search.min.93b2d0e7fa872893809358a01a9e6e0c0ce7a384100570924726e75a52c21d85.js +++ /dev/null @@ -1 +0,0 @@ -'use strict';(function(){const g='/lds-ctrl-est/en.search-data.min.cdba5d21934f88c8babfbddd59af83b0207389c62d2c3a11b31bc18fbe5810bb.json',h=Object.assign({cache:!0},{doc:{id:'id',field:['title','content'],store:['title','href','section']}}),a=document.querySelector('#book-search-input'),b=document.querySelector('#book-search-results');if(!a)return;a.addEventListener('focus',c),a.addEventListener('keyup',d),document.addEventListener('keypress',e);function e(b){if(a===document.activeElement)return;const c=String.fromCharCode(b.charCode);if(!f(c))return;a.focus(),b.preventDefault()}function f(b){const c=a.getAttribute('data-hotkeys')||'';return c.indexOf(b)>=0}function c(){a.removeEventListener('focus',c),a.required=!0,fetch(g).then(a=>a.json()).then(a=>{window.bookSearchIndex=FlexSearch.create('balance',h),window.bookSearchIndex.add(a)}).then(()=>a.required=!1).then(d)}function d(){while(b.firstChild)b.removeChild(b.firstChild);if(!a.value)return;const c=window.bookSearchIndex.search(a.value,10);c.forEach(function(a){const c=i('<li><a href></a><small></small></li>'),d=c.querySelector('a'),e=c.querySelector('small');d.href=a.href,d.textContent=a.title,e.textContent=a.section,b.appendChild(c)})}function i(b){const a=document.createElement('div');return a.innerHTML=b,a.firstChild}})() \ No newline at end of file diff --git a/docs/favicon.png b/docs/favicon.png deleted file mode 100644 index 59c7c2a213cc1de508282f86d457b16426bf9c62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC6Hgb%kP61PR}Fa?7&w?6|IB7% zUbBR!+>`asl_*Ar3-i?!3>X>I|1>Z#GR(7(Q*Z$4VZODzZo-MJr)7X_22WQ%mvv4F FO#mg08}a}E diff --git a/docs/favicon.svg b/docs/favicon.svg deleted file mode 100644 index a3c696de..00000000 --- a/docs/favicon.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M3 18h12v-2H3v2zM3 6v2h18V6H3zm0 7h18v-2H3v2z"/></svg> \ No newline at end of file diff --git a/docs/flexsearch.min.js b/docs/flexsearch.min.js deleted file mode 100644 index 984d8c6e..00000000 --- a/docs/flexsearch.min.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - FlexSearch v0.6.30 - Copyright 2019 Nextapps GmbH - Author: Thomas Wilkerling - Released under the Apache 2.0 Licence - https://github.com/nextapps-de/flexsearch -*/ -'use strict';(function(K,R,w){let L;(L=w.define)&&L.amd?L([],function(){return R}):(L=w.modules)?L[K.toLowerCase()]=R:"object"===typeof exports?module.exports=R:w[K]=R})("FlexSearch",function ma(K){function w(a,c){const b=c?c.id:a&&a.id;this.id=b||0===b?b:na++;this.init(a,c);fa(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].c):Object.keys(this.c)});fa(this,"length",function(){return this.index.length})}function L(a,c,b,d){this.u!==this.g&&(this.o=this.o.concat(b),this.u++, -d&&this.o.length>=d&&(this.u=this.g),this.u===this.g&&(this.cache&&this.j.set(c,this.o),this.F&&this.F(this.o)));return this}function S(a){const c=B();for(const b in a)if(a.hasOwnProperty(b)){const d=a[b];F(d)?c[b]=d.slice(0):G(d)?c[b]=S(d):c[b]=d}return c}function W(a,c){const b=a.length,d=O(c),e=[];for(let f=0,h=0;f<b;f++){const g=a[f];if(d&&c(g)||!d&&!c[g])e[h++]=g}return e}function P(a,c,b,d,e,f,h,g,k,l){b=ha(b,h?0:e,g,f,c,k,l);let p;g&&(g=b.page,p=b.next,b=b.result);if(h)c=this.where(h,null, -e,b);else{c=b;b=this.l;e=c.length;f=Array(e);for(h=0;h<e;h++)f[h]=b[c[h]];c=f}b=c;d&&(O(d)||(M=d.split(":"),1<M.length?d=oa:(M=M[0],d=pa)),b.sort(d));b=T(g,p,b);this.cache&&this.j.set(a,b);return b}function fa(a,c,b){Object.defineProperty(a,c,{get:b})}function r(a){return new RegExp(a,"g")}function Q(a,c){for(let b=0;b<c.length;b+=2)a=a.replace(c[b],c[b+1]);return a}function V(a,c,b,d,e,f,h,g){if(c[b])return c[b];e=e?(g-(h||g/1.5))*f+(h||g/1.5)*e:f;c[b]=e;e>=h&&(a=a[g-(e+.5>>0)],a=a[b]||(a[b]=[]), -a[a.length]=d);return e}function ba(a,c){if(a){const b=Object.keys(a);for(let d=0,e=b.length;d<e;d++){const f=b[d],h=a[f];if(h)for(let g=0,k=h.length;g<k;g++)if(h[g]===c){1===k?delete a[f]:h.splice(g,1);break}else G(h[g])&&ba(h[g],c)}}}function ca(a){let c="",b="";var d="";for(let e=0;e<a.length;e++){const f=a[e];if(f!==b)if(e&&"h"===f){if(d="a"===d||"e"===d||"i"===d||"o"===d||"u"===d||"y"===d,("a"===b||"e"===b||"i"===b||"o"===b||"u"===b||"y"===b)&&d||" "===b)c+=f}else c+=f;d=e===a.length-1?"":a[e+ -1];b=f}return c}function qa(a,c){a=a.length-c.length;return 0>a?1:a?-1:0}function pa(a,c){a=a[M];c=c[M];return a<c?-1:a>c?1:0}function oa(a,c){const b=M.length;for(let d=0;d<b;d++)a=a[M[d]],c=c[M[d]];return a<c?-1:a>c?1:0}function T(a,c,b){return a?{page:a,next:c?""+c:null,result:b}:b}function ha(a,c,b,d,e,f,h){let g,k=[];if(!0===b){b="0";var l=""}else l=b&&b.split(":");const p=a.length;if(1<p){const y=B(),t=[];let v,x;var n=0,m;let I;var u=!0;let D,E=0,N,da,X,ea;l&&(2===l.length?(X=l,l=!1):l=ea= -parseInt(l[0],10));if(h){for(v=B();n<p;n++)if("not"===e[n])for(x=a[n],I=x.length,m=0;m<I;m++)v["@"+x[m]]=1;else da=n+1;if(C(da))return T(b,g,k);n=0}else N=J(e)&&e;let Y;for(;n<p;n++){const ra=n===(da||p)-1;if(!N||!n)if((m=N||e&&e[n])&&"and"!==m)if("or"===m)Y=!1;else continue;else Y=f=!0;x=a[n];if(I=x.length){if(u)if(D){var q=D.length;for(m=0;m<q;m++){u=D[m];var A="@"+u;h&&v[A]||(y[A]=1,f||(k[E++]=u))}D=null;u=!1}else{D=x;continue}A=!1;for(m=0;m<I;m++){q=x[m];var z="@"+q;const Z=f?y[z]||0:n;if(!(!Z&& -!d||h&&v[z]||!f&&y[z]))if(Z===n){if(ra){if(!ea||--ea<E)if(k[E++]=q,c&&E===c)return T(b,E+(l||0),k)}else y[z]=n+1;A=!0}else d&&(z=t[Z]||(t[Z]=[]),z[z.length]=q)}if(Y&&!A&&!d)break}else if(Y&&!d)return T(b,g,x)}if(D)if(n=D.length,h)for(m=l?parseInt(l,10):0;m<n;m++)a=D[m],v["@"+a]||(k[E++]=a);else k=D;if(d)for(E=k.length,X?(n=parseInt(X[0],10)+1,m=parseInt(X[1],10)+1):(n=t.length,m=0);n--;)if(q=t[n]){for(I=q.length;m<I;m++)if(d=q[m],!h||!v["@"+d])if(k[E++]=d,c&&E===c)return T(b,n+":"+m,k);m=0}}else!p|| -e&&"not"===e[0]||(k=a[0],l&&(l=parseInt(l[0],10)));c&&(h=k.length,l&&l>h&&(l=0),l=l||0,g=l+c,g<h?k=k.slice(l,g):(g=0,l&&(k=k.slice(l))));return T(b,g,k)}function J(a){return"string"===typeof a}function F(a){return a.constructor===Array}function O(a){return"function"===typeof a}function G(a){return"object"===typeof a}function C(a){return"undefined"===typeof a}function ia(a){const c=Array(a);for(let b=0;b<a;b++)c[b]=B();return c}function B(){return Object.create(null)}function sa(){let a,c;self.onmessage= -function(b){if(b=b.data)if(b.search){const d=c.search(b.content,b.threshold?{limit:b.limit,threshold:b.threshold,where:b.where}:b.limit);self.postMessage({id:a,content:b.content,limit:b.limit,result:d})}else b.add?c.add(b.id,b.content):b.update?c.update(b.id,b.content):b.remove?c.remove(b.id):b.clear?c.clear():b.info?(b=c.info(),b.worker=a,console.log(b)):b.register&&(a=b.id,b.options.cache=!1,b.options.async=!1,b.options.worker=!1,c=(new Function(b.register.substring(b.register.indexOf("{")+1,b.register.lastIndexOf("}"))))(), -c=new c(b.options))}}function ta(a,c,b,d){a=K("flexsearch","id"+a,sa,function(f){(f=f.data)&&f.result&&d(f.id,f.content,f.result,f.limit,f.where,f.cursor,f.suggest)},c);const e=ma.toString();b.id=c;a.postMessage({register:e,options:b,id:c});return a}const H={encode:"icase",f:"forward",split:/\W+/,cache:!1,async:!1,g:!1,D:!1,a:!1,b:9,threshold:0,depth:0},ja={memory:{encode:"extra",f:"strict",threshold:0,b:1},speed:{encode:"icase",f:"strict",threshold:1,b:3,depth:2},match:{encode:"extra",f:"full",threshold:1, -b:3},score:{encode:"extra",f:"strict",threshold:1,b:9,depth:4},balance:{encode:"balance",f:"strict",threshold:0,b:3,depth:3},fast:{encode:"icase",f:"strict",threshold:8,b:9,depth:1}},aa=[];let na=0;const ka={},la={};w.create=function(a,c){return new w(a,c)};w.registerMatcher=function(a){for(const c in a)a.hasOwnProperty(c)&&aa.push(r(c),a[c]);return this};w.registerEncoder=function(a,c){U[a]=c.bind(U);return this};w.registerLanguage=function(a,c){ka[a]=c.filter;la[a]=c.stemmer;return this};w.encode= -function(a,c){return U[a](c)};w.prototype.init=function(a,c){this.v=[];if(c){var b=c.preset;a=c}else a||(a=H),b=a.preset;c={};J(a)?(c=ja[a],a={}):b&&(c=ja[b]);if(b=a.worker)if("undefined"===typeof Worker)a.worker=!1,this.m=null;else{var d=parseInt(b,10)||4;this.C=-1;this.u=0;this.o=[];this.F=null;this.m=Array(d);for(var e=0;e<d;e++)this.m[e]=ta(this.id,e,a,L.bind(this))}this.f=a.tokenize||c.f||this.f||H.f;this.split=C(b=a.split)?this.split||H.split:J(b)?r(b):b;this.D=a.rtl||this.D||H.D;this.async= -"undefined"===typeof Promise||C(b=a.async)?this.async||H.async:b;this.g=C(b=a.worker)?this.g||H.g:b;this.threshold=C(b=a.threshold)?c.threshold||this.threshold||H.threshold:b;this.b=C(b=a.resolution)?b=c.b||this.b||H.b:b;b<=this.threshold&&(this.b=this.threshold+1);this.depth="strict"!==this.f||C(b=a.depth)?c.depth||this.depth||H.depth:b;this.w=(b=C(b=a.encode)?c.encode||H.encode:b)&&U[b]&&U[b].bind(U)||(O(b)?b:this.w||!1);(b=a.matcher)&&this.addMatcher(b);if(b=(c=a.lang)||a.filter){J(b)&&(b=ka[b]); -if(F(b)){d=this.w;e=B();for(var f=0;f<b.length;f++){var h=d?d(b[f]):b[f];e[h]=1}b=e}this.filter=b}if(b=c||a.stemmer){var g;c=J(b)?la[b]:b;d=this.w;e=[];for(g in c)c.hasOwnProperty(g)&&(f=d?d(g):g,e.push(r(f+"($|\\W)"),d?d(c[g]):c[g]));this.stemmer=g=e}this.a=e=(b=a.doc)?S(b):this.a||H.a;this.i=ia(this.b-(this.threshold||0));this.h=B();this.c=B();if(e){this.l=B();a.doc=null;g=e.index={};c=e.keys=[];d=e.field;f=e.tag;h=e.store;F(e.id)||(e.id=e.id.split(":"));if(h){var k=B();if(J(h))k[h]=1;else if(F(h))for(let l= -0;l<h.length;l++)k[h[l]]=1;else G(h)&&(k=h);e.store=k}if(f){this.G=B();h=B();if(d)if(J(d))h[d]=a;else if(F(d))for(k=0;k<d.length;k++)h[d[k]]=a;else G(d)&&(h=d);F(f)||(e.tag=f=[f]);for(d=0;d<f.length;d++)this.G[f[d]]=B();this.I=f;d=h}if(d){let l;F(d)||(G(d)?(l=d,e.field=d=Object.keys(d)):e.field=d=[d]);for(e=0;e<d.length;e++)f=d[e],F(f)||(l&&(a=l[f]),c[e]=f,d[e]=f.split(":")),g[f]=new w(a)}a.doc=b}this.B=!0;this.j=(this.cache=b=C(b=a.cache)?this.cache||H.cache:b)?new ua(b):!1;return this};w.prototype.encode= -function(a){a&&(aa.length&&(a=Q(a,aa)),this.v.length&&(a=Q(a,this.v)),this.w&&(a=this.w(a)),this.stemmer&&(a=Q(a,this.stemmer)));return a};w.prototype.addMatcher=function(a){const c=this.v;for(const b in a)a.hasOwnProperty(b)&&c.push(r(b),a[b]);return this};w.prototype.add=function(a,c,b,d,e){if(this.a&&G(a))return this.A("add",a,c);if(c&&J(c)&&(a||0===a)){var f="@"+a;if(this.c[f]&&!d)return this.update(a,c);if(this.g)return++this.C>=this.m.length&&(this.C=0),this.m[this.C].postMessage({add:!0,id:a, -content:c}),this.c[f]=""+this.C,b&&b(),this;if(!e){if(this.async&&"function"!==typeof importScripts){let t=this;f=new Promise(function(v){setTimeout(function(){t.add(a,c,null,d,!0);t=null;v()})});if(b)f.then(b);else return f;return this}if(b)return this.add(a,c,null,d,!0),b(),this}c=this.encode(c);if(!c.length)return this;b=this.f;e=O(b)?b(c):c.split(this.split);this.filter&&(e=W(e,this.filter));const n=B();n._ctx=B();const m=e.length,u=this.threshold,q=this.depth,A=this.b,z=this.i,y=this.D;for(let t= -0;t<m;t++){var h=e[t];if(h){var g=h.length,k=(y?t+1:m-t)/m,l="";switch(b){case "reverse":case "both":for(var p=g;--p;)l=h[p]+l,V(z,n,l,a,y?1:(g-p)/g,k,u,A-1);l="";case "forward":for(p=0;p<g;p++)l+=h[p],V(z,n,l,a,y?(p+1)/g:1,k,u,A-1);break;case "full":for(p=0;p<g;p++){const v=(y?p+1:g-p)/g;for(let x=g;x>p;x--)l=h.substring(p,x),V(z,n,l,a,v,k,u,A-1)}break;default:if(g=V(z,n,h,a,1,k,u,A-1),q&&1<m&&g>=u)for(g=n._ctx[h]||(n._ctx[h]=B()),h=this.h[h]||(this.h[h]=ia(A-(u||0))),k=t-q,l=t+q+1,0>k&&(k=0),l> -m&&(l=m);k<l;k++)k!==t&&V(h,g,e[k],a,0,A-(k<t?t-k:k-t),u,A-1)}}}this.c[f]=1;this.B=!1}return this};w.prototype.A=function(a,c,b){if(F(c)){var d=c.length;if(d--){for(var e=0;e<d;e++)this.A(a,c[e]);return this.A(a,c[d],b)}}else{var f=this.a.index,h=this.a.keys,g=this.a.tag;e=this.a.store;var k;var l=this.a.id;d=c;for(var p=0;p<l.length;p++)d=d[l[p]];if("remove"===a&&(delete this.l[d],l=h.length,l--)){for(c=0;c<l;c++)f[h[c]].remove(d);return f[h[l]].remove(d,b)}if(g){for(k=0;k<g.length;k++){var n=g[k]; -var m=c;l=n.split(":");for(p=0;p<l.length;p++)m=m[l[p]];m="@"+m}k=this.G[n];k=k[m]||(k[m]=[])}l=this.a.field;for(let u=0,q=l.length;u<q;u++){n=l[u];g=c;for(m=0;m<n.length;m++)g=g[n[m]];n=f[h[u]];m="add"===a?n.add:n.update;u===q-1?m.call(n,d,g,b):m.call(n,d,g)}if(e){b=Object.keys(e);a=B();for(f=0;f<b.length;f++)if(h=b[f],e[h]){h=h.split(":");let u,q;for(l=0;l<h.length;l++)g=h[l],u=(u||c)[g],q=(q||a)[g]=u}c=a}k&&(k[k.length]=c);this.l[d]=c}return this};w.prototype.update=function(a,c,b){if(this.a&& -G(a))return this.A("update",a,c);this.c["@"+a]&&J(c)&&(this.remove(a),this.add(a,c,b,!0));return this};w.prototype.remove=function(a,c,b){if(this.a&&G(a))return this.A("remove",a,c);var d="@"+a;if(this.c[d]){if(this.g)return this.m[this.c[d]].postMessage({remove:!0,id:a}),delete this.c[d],c&&c(),this;if(!b){if(this.async&&"function"!==typeof importScripts){let e=this;d=new Promise(function(f){setTimeout(function(){e.remove(a,null,!0);e=null;f()})});if(c)d.then(c);else return d;return this}if(c)return this.remove(a, -null,!0),c(),this}for(c=0;c<this.b-(this.threshold||0);c++)ba(this.i[c],a);this.depth&&ba(this.h,a);delete this.c[d];this.B=!1}return this};let M;w.prototype.search=function(a,c,b,d){if(G(c)){if(F(c))for(var e=0;e<c.length;e++)c[e].query=a;else c.query=a;a=c;c=1E3}else c&&O(c)?(b=c,c=1E3):c||0===c||(c=1E3);if(this.g){this.F=b;this.u=0;this.o=[];for(var f=0;f<this.g;f++)this.m[f].postMessage({search:!0,limit:c,content:a})}else{var h=[],g=a;if(G(a)&&!F(a)){b||(b=a.callback)&&(g.callback=null);var k= -a.sort;var l=a.page;c=a.limit;f=a.threshold;var p=a.suggest;a=a.query}if(this.a){f=this.a.index;const y=g.where;var n=g.bool||"or",m=g.field;let t=n;let v,x;if(m)F(m)||(m=[m]);else if(F(g)){var u=g;m=[];t=[];for(var q=0;q<g.length;q++)d=g[q],e=d.bool||n,m[q]=d.field,t[q]=e,"not"===e?v=!0:"and"===e&&(x=!0)}else m=this.a.keys;n=m.length;for(q=0;q<n;q++)u&&(g=u[q]),l&&!J(g)&&(g.page=null,g.limit=0),h[q]=f[m[q]].search(g,0);if(b)return b(P.call(this,a,t,h,k,c,p,y,l,x,v));if(this.async){const I=this;return new Promise(function(D){Promise.all(h).then(function(E){D(P.call(I, -a,t,E,k,c,p,y,l,x,v))})})}return P.call(this,a,t,h,k,c,p,y,l,x,v)}f||(f=this.threshold||0);if(!d){if(this.async&&"function"!==typeof importScripts){let y=this;f=new Promise(function(t){setTimeout(function(){t(y.search(g,c,null,!0));y=null})});if(b)f.then(b);else return f;return this}if(b)return b(this.search(g,c,null,!0)),this}if(!a||!J(a))return h;g=a;if(this.cache)if(this.B){if(b=this.j.get(a))return b}else this.j.clear(),this.B=!0;g=this.encode(g);if(!g.length)return h;b=this.f;b=O(b)?b(g):g.split(this.split); -this.filter&&(b=W(b,this.filter));u=b.length;d=!0;e=[];var A=B(),z=0;1<u&&(this.depth&&"strict"===this.f?n=!0:b.sort(qa));if(!n||(q=this.h)){const y=this.b;for(;z<u;z++){let t=b[z];if(t){if(n){if(!m)if(q[t])m=t,A[t]=1;else if(!p)return h;if(p&&z===u-1&&!e.length)n=!1,t=m||t,A[t]=0;else if(!m)continue}if(!A[t]){const v=[];let x=!1,I=0;const D=n?q[m]:this.i;if(D){let E;for(let N=0;N<y-f;N++)if(E=D[N]&&D[N][t])v[I++]=E,x=!0}if(x)m=t,e[e.length]=1<I?v.concat.apply([],v):v[0];else if(!p){d=!1;break}A[t]= -1}}}}else d=!1;d&&(h=ha(e,c,l,p));this.cache&&this.j.set(a,h);return h}};w.prototype.find=function(a,c){return this.where(a,c,1)[0]||null};w.prototype.where=function(a,c,b,d){const e=this.l,f=[];let h=0;let g;var k;let l;if(G(a)){b||(b=c);var p=Object.keys(a);var n=p.length;g=!1;if(1===n&&"id"===p[0])return[e[a.id]];if((k=this.I)&&!d)for(var m=0;m<k.length;m++){var u=k[m],q=a[u];if(!C(q)){l=this.G[u]["@"+q];if(0===--n)return l;p.splice(p.indexOf(u),1);delete a[u];break}}k=Array(n);for(m=0;m<n;m++)k[m]= -p[m].split(":")}else{if(O(a)){c=d||Object.keys(e);b=c.length;for(p=0;p<b;p++)n=e[c[p]],a(n)&&(f[h++]=n);return f}if(C(c))return[e[a]];if("id"===a)return[e[c]];p=[a];n=1;k=[a.split(":")];g=!0}d=l||d||Object.keys(e);m=d.length;for(u=0;u<m;u++){q=l?d[u]:e[d[u]];let A=!0;for(let z=0;z<n;z++){g||(c=a[p[z]]);const y=k[z],t=y.length;let v=q;if(1<t)for(let x=0;x<t;x++)v=v[y[x]];else v=v[y[0]];if(v!==c){A=!1;break}}if(A&&(f[h++]=q,b&&h===b))break}return f};w.prototype.info=function(){if(this.g)for(let a=0;a< -this.g;a++)this.m[a].postMessage({info:!0,id:this.id});else return{id:this.id,items:this.length,cache:this.cache&&this.cache.s?this.cache.s.length:!1,matcher:aa.length+(this.v?this.v.length:0),worker:this.g,threshold:this.threshold,depth:this.depth,resolution:this.b,contextual:this.depth&&"strict"===this.f}};w.prototype.clear=function(){return this.destroy().init()};w.prototype.destroy=function(){this.cache&&(this.j.clear(),this.j=null);this.i=this.h=this.c=null;if(this.a){const a=this.a.keys;for(let c= -0;c<a.length;c++)this.a.index[a[c]].destroy();this.a=this.l=null}return this};w.prototype.export=function(a){const c=!a||C(a.serialize)||a.serialize;if(this.a){const d=!a||C(a.doc)||a.doc;var b=!a||C(a.index)||a.index;a=[];let e=0;if(b)for(b=this.a.keys;e<b.length;e++){const f=this.a.index[b[e]];a[e]=[f.i,f.h,Object.keys(f.c)]}d&&(a[e]=this.l)}else a=[this.i,this.h,Object.keys(this.c)];c&&(a=JSON.stringify(a));return a};w.prototype.import=function(a,c){if(!c||C(c.serialize)||c.serialize)a=JSON.parse(a); -const b=B();if(this.a){var d=!c||C(c.doc)||c.doc,e=0;if(!c||C(c.index)||c.index){c=this.a.keys;const h=c.length;for(var f=a[0][2];e<f.length;e++)b[f[e]]=1;for(e=0;e<h;e++){f=this.a.index[c[e]];const g=a[e];g&&(f.i=g[0],f.h=g[1],f.c=b)}}d&&(this.l=G(d)?d:a[e])}else{d=a[2];for(e=0;e<d.length;e++)b[d[e]]=1;this.i=a[0];this.h=a[1];this.c=b}};const va=function(){const a=r("\\s+"),c=r("[^a-z0-9 ]"),b=[r("[-/]")," ",c,"",a," "];return function(d){return ca(Q(d.toLowerCase(),b))}}(),U={icase:function(a){return a.toLowerCase()}, -simple:function(){const a=r("\\s+"),c=r("[^a-z0-9 ]"),b=r("[-/]"),d=r("[\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5]"),e=r("[\u00e8\u00e9\u00ea\u00eb]"),f=r("[\u00ec\u00ed\u00ee\u00ef]"),h=r("[\u00f2\u00f3\u00f4\u00f5\u00f6\u0151]"),g=r("[\u00f9\u00fa\u00fb\u00fc\u0171]"),k=r("[\u00fd\u0177\u00ff]"),l=r("\u00f1"),p=r("[\u00e7c]"),n=r("\u00df"),m=r(" & "),u=[d,"a",e,"e",f,"i",h,"o",g,"u",k,"y",l,"n",p,"k",n,"s",m," and ",b," ",c,"",a," "];return function(q){q=Q(q.toLowerCase(),u);return" "===q?"":q}}(),advanced:function(){const a= -r("ae"),c=r("ai"),b=r("ay"),d=r("ey"),e=r("oe"),f=r("ue"),h=r("ie"),g=r("sz"),k=r("zs"),l=r("ck"),p=r("cc"),n=r("sh"),m=r("th"),u=r("dt"),q=r("ph"),A=r("pf"),z=r("ou"),y=r("uo"),t=[a,"a",c,"ei",b,"ei",d,"ei",e,"o",f,"u",h,"i",g,"s",k,"s",n,"s",l,"k",p,"k",m,"t",u,"t",q,"f",A,"f",z,"o",y,"u"];return function(v,x){if(!v)return v;v=this.simple(v);2<v.length&&(v=Q(v,t));x||1<v.length&&(v=ca(v));return v}}(),extra:function(){const a=r("p"),c=r("z"),b=r("[cgq]"),d=r("n"),e=r("d"),f=r("[vw]"),h=r("[aeiouy]"), -g=[a,"b",c,"s",b,"k",d,"m",e,"t",f,"f",h,""];return function(k){if(!k)return k;k=this.advanced(k,!0);if(1<k.length){k=k.split(" ");for(let l=0;l<k.length;l++){const p=k[l];1<p.length&&(k[l]=p[0]+Q(p.substring(1),g))}k=k.join(" ");k=ca(k)}return k}}(),balance:va},ua=function(){function a(c){this.clear();this.H=!0!==c&&c}a.prototype.clear=function(){this.cache=B();this.count=B();this.index=B();this.s=[]};a.prototype.set=function(c,b){if(this.H&&C(this.cache[c])){let d=this.s.length;if(d===this.H){d--; -const e=this.s[d];delete this.cache[e];delete this.count[e];delete this.index[e]}this.index[c]=d;this.s[d]=c;this.count[c]=-1;this.cache[c]=b;this.get(c)}else this.cache[c]=b};a.prototype.get=function(c){const b=this.cache[c];if(this.H&&b){var d=++this.count[c];const f=this.index;let h=f[c];if(0<h){const g=this.s;for(var e=h;this.count[g[--h]]<=d&&-1!==h;);h++;if(h!==e){for(d=e;d>h;d--)e=g[d-1],g[d]=e,f[e]=d;g[h]=c;f[c]=h}}}return b};return a}();return w}(function(){const K={},R="undefined"!==typeof Blob&& -"undefined"!==typeof URL&&URL.createObjectURL;return function(w,L,S,W,P){S=R?URL.createObjectURL(new Blob(["("+S.toString()+")()"],{type:"text/javascript"})):w+".min.js";w+="-"+L;K[w]||(K[w]=[]);K[w][P]=new Worker(S);K[w][P].onmessage=W;return K[w][P]}}()),this); diff --git a/docs/fonts/roboto-mono-v13-latin-regular.woff b/docs/fonts/roboto-mono-v13-latin-regular.woff deleted file mode 100644 index f319fbfa46a9c546ad2b4f68e2b6f9267cdfc5d8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15160 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 diff --git a/docs/fonts/roboto-mono-v13-latin-regular.woff2 b/docs/fonts/roboto-mono-v13-latin-regular.woff2 deleted file mode 100644 index ed384d22fd9f566df41a3a37de9f614adc7f630b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12312 zcmV+zFz3&APew8T0RR9105BK;4gdfE09Tv<0581&0RR9100000000000000000000 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 diff --git a/docs/fonts/roboto-v27-latin-700.woff b/docs/fonts/roboto-v27-latin-700.woff deleted file mode 100644 index a5d98fc6202f5cf5fd8b556ca834e8e9dbaafac1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20396 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 diff --git a/docs/fonts/roboto-v27-latin-700.woff2 b/docs/fonts/roboto-v27-latin-700.woff2 deleted file mode 100644 index 01d05fa509b7f91526cabe90c9bafa130e4c118a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15828 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 diff --git a/docs/fonts/roboto-v27-latin-regular.woff b/docs/fonts/roboto-v27-latin-regular.woff deleted file mode 100644 index 86b386372664a278c839b4a6fedbf6a05b396b70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20332 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 Z@IjO83?{s=KqX^`zGvsj4^tdfk^ttt3T6NR diff --git a/docs/fonts/roboto-v27-latin-regular.woff2 b/docs/fonts/roboto-v27-latin-regular.woff2 deleted file mode 100644 index ebe1795f85a661c205e4a4612eaf47d56273e68e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15688 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 diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 2b36063d..00000000 --- a/docs/index.html +++ /dev/null @@ -1,287 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <meta name="generator" content="Hugo 0.85.0" /> - <meta charset="UTF-8"> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> -<meta name="description" content="ldsCtrlEst: LDS Control &amp; Estimation # ldsCtrlEst is a C&#43;&#43; library for estimation and control of linear dynamical systems (LDS) with Gaussian or Poisson observations. It is meant to provide the functionality necessary to implement feedback control of linear dynamical systems experimentally. This library was originally developed for the task of controlling neuronal activity using spike count data as feedback and optogenetic inputs for control. However, the methods are generally applicable."> -<meta name="theme-color" content="#FFFFFF"><meta property="og:title" content="LDS Control &amp; Estimation" /> -<meta property="og:description" content="" /> -<meta property="og:type" content="website" /> -<meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/" /> - -<title>LDS Control &amp; Estimation | LDS C&amp;E</title> -<link rel="manifest" href="/lds-ctrl-est/manifest.json"> -<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> -<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> - <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> - <script defer src="/lds-ctrl-est/en.search.min.93b2d0e7fa872893809358a01a9e6e0c0ce7a384100570924726e75a52c21d85.js" integrity="sha256-k7LQ5/qHKJOAk1igGp5uDAzno4QQBXCSRybnWlLCHYU=" crossorigin="anonymous"></script> -<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/index.xml" title="LDS C&E" /> -<!-- -Made with Book Theme -https://github.com/alex-shpak/hugo-book ---> - -</head> -<body dir="ltr"> - <input type="checkbox" class="hidden toggle" id="menu-control" /> - <input type="checkbox" class="hidden toggle" id="toc-control" /> - <main class="container flex"> - <aside class="book-menu"> - <div class="book-menu-content"> - - <nav> -<h2 class="book-brand"> - <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> - </a> -</h2> - - -<div class="book-search"> - <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> - <div class="book-search-spinner hidden"></div> - <ul id="book-search-results"></ul> -</div> - - - - - - - - - - - - <ul> -<li><strong>Library Terminology</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> -<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> -<ul> -<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><strong>Tutorials</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><strong>API Reference</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> -<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> -<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> -<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> -<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> -</ul> -<p><br /></p> - - - - - - - -<ul> - - <li> - <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> - Github - </a> - </li> - -</ul> - - - - - - -</nav> - - - - - <script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> - - - - </div> - </aside> - - <div class="book-page"> - <header class="book-header"> - - <div class="flex align-center justify-between"> - <label for="menu-control"> - <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> - </label> - - <strong>LDS Control &amp; Estimation</strong> - - <label for="toc-control"> - - <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> - - </label> -</div> - - - - <aside class="hidden clearfix"> - - -<nav id="TableOfContents"> - <ul> - <li><a href="#ldsctrlest-lds-control--estimation"><code>ldsCtrlEst</code>: LDS Control &amp; Estimation</a></li> - <li><a href="#project-scope">Project Scope</a></li> - <li><a href="#repository-design">Repository Design</a></li> - <li><a href="#repository-organization">Repository Organization</a></li> - </ul> -</nav> - - - - </aside> - - - </header> - - - - <article class="markdown"><h1 id="ldsctrlest-lds-control--estimation"> - <code>ldsCtrlEst</code>: LDS Control &amp; Estimation - <a class="anchor" href="#ldsctrlest-lds-control--estimation">#</a> -</h1> -<p><code>ldsCtrlEst</code> is a C++ library for estimation and control of linear dynamical systems (LDS) with Gaussian or Poisson observations. It is meant to provide the functionality necessary to implement feedback control of linear dynamical systems experimentally. This library was originally developed for the task of controlling neuronal activity using spike count data as feedback and optogenetic inputs for control. However, the methods are generally applicable.</p> -<p>This library currently provides three namespaces.</p> -<ul> -<li><code>lds</code> : linear dynamical systems (without output/observations)</li> -<li><code>lds::gaussian</code>: linear dynamical systems with Gaussian observations</li> -<li><code>lds::poisson</code>: linear dynamical systems with Poisson observations</li> -</ul> -<p><em>Future iterations may include an additional namespace for LDS with Bernoulli observations (<code>lds::bernoulli</code>).</em></p> -<h1 id="project-scope"> - Project Scope - <a class="anchor" href="#project-scope">#</a> -</h1> -<p>The goal of this project is to provide necessary functions to implement feedback control of linear dynamical systems experimentally: <em>i.e.</em>, online estimation of state feedback and calculation of control signal updates. Given its intended use in experiments, the library seeks to be <strong>practical</strong> in all things and thus includes optional features such as adaptive estimation of a process disturbance to improve robustness in state estimation and a mechanism for combatting integrator windup with control signal saturation. For cases where the system to be controlled is not adequately modeled as having linear dynamics but has multiple quasi-linear operating modes, a switched control scheme is also implemented. It switches between multiple controllers designed for each operating mode of the physical system as it changes. It also includes options to toggle on/off feedback control and state estimation independently, which can be practically useful when testing the components of the control system. Moreover, to avoid the need for numerical integration of continuous-time models, all state-space models used here are discrete-time.</p> -<p>Generally, the <code>ldsCtrlEst</code> library does <strong>not</strong> endeavor to provide functionality for things that can be carried out offline/before experiments. For example, it does not design controller gains. Given a model of the system to be controlled, these parameters may be optimized before experimental application in most cases, and there are numerous options available to scientists/engineers in languages such as Matlab and Python for design. An exception to this guiding principle to project scope is the included code for fitting state-space models to data. Currently, this fitting portion of the library is a configurable option, but in future releases this may migrate to a separate project as it is not intended for online use.</p> -<p>Among other things, this project also does not provide methods for trajectory optimization, linearization of nonlinear models, or other methods related to nonlinear control, with the exception of the nonlinear state estimator for Poisson-output LDS models.</p> -<h1 id="repository-design"> - Repository Design - <a class="anchor" href="#repository-design">#</a> -</h1> -<p><img src="/lds-ctrl-est/classlds_1_1_system__inherit__graph.png" alt="system class hierarchy" /></p> -<ul> -<li>All dynamical systems <em>with observations</em> (<code>lds::gaussian::System</code>, <code>lds::poisson::System</code>) are derived from a prototypical linear dynamical system abstract type (<code>lds::System</code>).</li> -<li>These Gaussian and Poisson system types include user-accessible functions for one-step filtering for one-step simulation, etc.</li> -</ul> -<p><img src="/lds-ctrl-est/classlds_1_1_controller__inherit__graph.png" alt="controller class hierarchy" /></p> -<ul> -<li>The controller types for Gaussian- and Poisson-output systems (<code>lds::gaussian::Controller</code>, <code>lds::gaussian::SwitchedController</code>, <code>lds::poisson::Controller</code>, <code>lds::poisson::SwitchedController</code>) are derived from an abstract class template (<code>lds::Controller</code>) that is generic over LDS types derived from <code>lds::System</code> (here, <code>lds::gaussian::System</code> and <code>lds::poisson::System</code>). <code>lds::Controller</code> provides functions for one-step updates of the control signal, based on feedback and a target/reference signal. For the common problem of output reference tracking, the controller uses the underlying system model to estimate the control signal required to track the target, effectively providing model-based open-loop control if the estimator is disabled.</li> -<li>In order to ensure dimensionalities always match internally, every property of a system/controller class is <code>protected</code> or <code>private</code>. Get methods provide read-only references for most signals/parameters. Where appropriate, set methods are defined so users can change hidden parameters if and only if the new parameter has the correct dimensions.</li> -</ul> -<h1 id="repository-organization"> - Repository Organization - <a class="anchor" href="#repository-organization">#</a> -</h1> -<ul> -<li>Header files are located under <code>include/ldsEstCtrl_h</code>.</li> -<li>Source files are located under <code>src/</code> (main source code) and <code>src-fit/</code> (model fitting-related source code).</li> -<li>Wrappers for exposing functions to Matlab as executables (mex) are located under <code>matlab/</code>. Currently, only fitting functions of the library are exposed for use in Matlab.</li> -<li>Complimentary Matlab functions for control and estimation are also located under <code>matlab/</code>. They are provided as methods of <code>GLDS</code> and <code>PLDS</code> class definitions.</li> -<li>Example programs and visualization scripts are located under <code>examples/</code>.</li> -<li>Example programs that demonstrate how to use ldsCtrlEst in other projects are provided in <code>misc/</code>. See <code>misc/test-cmake-installation</code> for a project that uses <code>cmake</code> to configure your project build and <code>misc/test-pkgconfig-installation</code> which is the same but uses a hand-written Makefile and calls to pkg-config. As the names suggest, building these programs is a simple way to test your installation of ldsCtrlEst.</li> -</ul> -</article> - - - - <footer class="book-footer"> - - <div class="flex flex-wrap justify-between"> - - - - - -</div> - - - - <script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script> - - - - - </footer> - - - - - - <label for="menu-control" class="hidden book-menu-overlay"></label> - </div> - - - <aside class="book-toc"> - <div class="book-toc-content"> - - -<nav id="TableOfContents"> - <ul> - <li><a href="#ldsctrlest-lds-control--estimation"><code>ldsCtrlEst</code>: LDS Control &amp; Estimation</a></li> - <li><a href="#project-scope">Project Scope</a></li> - <li><a href="#repository-design">Repository Design</a></li> - <li><a href="#repository-organization">Repository Organization</a></li> - </ul> -</nav> - - - - </div> - </aside> - - </main> - - -</body> -</html> - - - - - - - - - - - - diff --git a/docs/index.xml b/docs/index.xml deleted file mode 100644 index 2f568e29..00000000 --- a/docs/index.xml +++ /dev/null @@ -1,792 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> - <channel> - <title>LDS Control &amp; Estimation on LDS C&amp;E</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/</link> - <description>Recent content in LDS Control &amp; Estimation on LDS C&amp;E</description> - <generator>Hugo -- gohugo.io</generator><atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/index.xml" rel="self" type="application/rss+xml" /> - <item> - <title></title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/acknowledgements/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/acknowledgements/</guid> - <description>Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.</description> - </item> - - <item> - <title></title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</guid> - <description>Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows. -ldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation. -Tested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another.</description> - </item> - - <item> - <title></title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</guid> - <description>Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this: -Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don&amp;rsquo;t already have them: -scoop install git cmake If that didn&amp;rsquo;t work, follow more detailed instructions here. -The easiest way to compile C++ project on Windows is with Visual Studio&amp;rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.</description> - </item> - - <item> - <title></title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/</guid> - <description>Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes. -Contributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.</description> - </item> - - <item> - <title>armamexc</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</guid> - <description>arma/mex interface using Matlab C API</description> - </item> - - <item> - <title>armamexcpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</guid> - <description>arma/mex interface using Matlab C++ API</description> - </item> - - <item> - <title>C&amp;E</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</guid> - <description>Control &amp;amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.</description> - </item> - - <item> - <title>Control Mode Bit Masks</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</guid> - <description>provides fill types for constructing new armadillo vectors, matrices</description> - </item> - - <item> - <title>Defaults</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</guid> - <description>Defaults # -More&amp;hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances) -Attribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time</description> - </item> - - <item> - <title>eg_glds_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</guid> - <description>eg_glds_ctrl.cpp # Example GLDS Control -//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</description> - </item> - - <item> - <title>eg_glds_du_plds_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</guid> - <description>eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). -//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.</description> - </item> - - <item> - <title>eg_plds_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</guid> - <description>eg_plds_ctrl.cpp # Example PLDS Control -//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</description> - </item> - - <item> - <title>eg_plds_est.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</guid> - <description>eg_plds_est.cpp # Example PLDS Estimation -//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</description> - </item> - - <item> - <title>eg_plds_switched_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</guid> - <description>eg_plds_switched_ctrl.cpp # Example Switched PLDS Control -//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</description> - </item> - - <item> - <title>examples</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</guid> - <description>examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time</description> - </item> - - <item> - <title>examples/eg_glds_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</guid> - <description>examples/eg_glds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system -Source code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.</description> - </item> - - <item> - <title>examples/eg_glds_du_plds_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</guid> - <description>examples/eg_glds_du_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system -Source code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.</description> - </item> - - <item> - <title>examples/eg_plds_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</guid> - <description>examples/eg_plds_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Going to simulate a switching disturbance (m) acting on system -Source code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.</description> - </item> - - <item> - <title>examples/eg_plds_est.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</guid> - <description>examples/eg_plds_est.cpp # Functions # Name Matrix random_walk(size_t n_t, const Matrix &amp;amp; Q, const Vector &amp;amp; x0) int main() Function Details # random_walk # Matrix random_walk( size_t n_t, const Matrix &amp;amp; Q, const Vector &amp;amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.</description> - </item> - - <item> - <title>examples/eg_plds_switched_ctrl.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</guid> - <description>examples/eg_plds_switched_ctrl.cpp # Functions # Name auto main() Function Details # main # auto main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;); // you may not use this file except in compliance with the License.</description> - </item> - - <item> - <title>GLDS Control</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</guid> - <description>GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system.</description> - </item> - - <item> - <title>include</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</guid> - <description>include # Directories # Name ldsCtrlEst_h Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time</description> - </item> - - <item> - <title>lds</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</guid> - <description>Linear Dynamical Systems (LDS) namespace.</description> - </item> - - <item> - <title>lds::Controller</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1controller/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1controller/</guid> - <description>lds::Controller # More&amp;hellip; -Inherited by lds::gaussian::Controller, lds::poisson::Controller, lds::SwitchedController&amp;lt; System &amp;gt; -Public Functions # Name Controller() =default -Constructs a new Controller. Controller(const System &amp;amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0) -Constructs a new Controller. Controller(System &amp;amp;&amp;amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0) -Constructs a new Controller by moving the system object.</description> - </item> - - <item> - <title>lds::EM</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1em/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1em/</guid> - <description>lds::EM # More&amp;hellip; -Inherited by lds::gaussian::FitEM, lds::poisson::FitEM -Public Functions # Name EM() =default -Constructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt; &amp;amp;&amp;amp; u_train, UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt; &amp;amp;&amp;amp; z_train) -Constructs a new EMFit type. EM(const Fit &amp;amp; fit0, UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt; &amp;amp;&amp;amp; u_train, UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt; &amp;amp;&amp;amp; z_train) -Constructs a new EMFit type.</description> - </item> - - <item> - <title>lds::Fit</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1fit/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1fit/</guid> - <description>LDS Fit Type.</description> - </item> - - <item> - <title>lds::gaussian</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</guid> - <description>Linear Dynamical Systems with Gaussian observations.</description> - </item> - - <item> - <title>lds::gaussian::Controller</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/</guid> - <description>Gaussian-observation Controller Type.</description> - </item> - - <item> - <title>lds::gaussian::Fit</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/</guid> - <description>GLDS Fit Type.</description> - </item> - - <item> - <title>lds::gaussian::FitEM</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/</guid> - <description>GLDS E-M Fit Type.</description> - </item> - - <item> - <title>lds::gaussian::FitSSID</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/</guid> - <description>Subspace Identification (SSID) for GLDS.</description> - </item> - - <item> - <title>lds::gaussian::SwitchedController</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/</guid> - <description>Gaussian-observation SwitchedController Type.</description> - </item> - - <item> - <title>lds::gaussian::System</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/</guid> - <description>Gaussian LDS Type.</description> - </item> - - <item> - <title>lds::poisson</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</guid> - <description>Linear Dynamical Systems with Poisson observations.</description> - </item> - - <item> - <title>lds::poisson::Controller</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/</guid> - <description>PLDS Controller Type.</description> - </item> - - <item> - <title>lds::poisson::Fit</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/</guid> - <description>PLDS Fit Type.</description> - </item> - - <item> - <title>lds::poisson::FitEM</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/</guid> - <description>PLDS E-M Fit Type.</description> - </item> - - <item> - <title>lds::poisson::FitSSID</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/</guid> - <description>Subspace Identification (SSID) for PLDS.</description> - </item> - - <item> - <title>lds::poisson::SwitchedController</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/</guid> - <description>Poisson-observation SwitchedController Type.</description> - </item> - - <item> - <title>lds::poisson::System</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/</guid> - <description>Poisson System type.</description> - </item> - - <item> - <title>lds::SSID</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/</guid> - <description>lds::SSID # More&amp;hellip; -Inherited by lds::gaussian::FitSSID, lds::poisson::FitSSID -Public Functions # Name SSID() =default -Constructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt; &amp;amp;&amp;amp; u_train, UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt; &amp;amp;&amp;amp; z_train, const Vector &amp;amp; d =Vector(1).fill(-kInf)) -Constructs a new SSIDFit type. std::tuple&amp;lt; Fit, Vector &amp;gt; Run(SSIDWt ssid_wt) -Runs fitting by subspace identification (SSID) std::tuple&amp;lt; UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt;, UniformMatrixList&amp;lt; kMatFreeDim2 &amp;gt; &amp;gt; ReturnData()</description> - </item> - - <item> - <title>lds::SwitchedController</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/</guid> - <description>SwitchedController Type.</description> - </item> - - <item> - <title>lds::System</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1system/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1system/</guid> - <description>Linear Dynamical System Type.</description> - </item> - - <item> - <title>lds::UniformMatrixList</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/</guid> - <description>lds::UniformMatrixList # More&amp;hellip; -Inherits from std::vector&amp;lt; Matrix &amp;gt; -Public Functions # Name UniformMatrixList() =default -Constructs a new UniformMatrixList. UniformMatrixList(const std::vector&amp;lt; Matrix &amp;gt; &amp;amp; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0}) -Constructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector&amp;lt; Matrix &amp;gt; &amp;amp;&amp;amp; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0})</description> - </item> - - <item> - <title>lds::UniformSystemList</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/</guid> - <description>lds::UniformSystemList # More&amp;hellip; -Inherits from std::vector&amp;lt; System &amp;gt; -Public Functions # Name UniformSystemList() =default -Constructs a new UniformSystemList. UniformSystemList(const std::vector&amp;lt; System &amp;gt; &amp;amp; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0}) -Constructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector&amp;lt; System &amp;gt; &amp;amp;&amp;amp; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0})</description> - </item> - - <item> - <title>lds::UniformVectorList</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/</guid> - <description>lds::UniformVectorList # Inherits from std::vector&amp;lt; Vector &amp;gt; -Public Functions # Name UniformVectorList() =default -Constructs a new UniformVectorList. UniformVectorList(const std::vector&amp;lt; Vector &amp;gt; &amp;amp; vecs, size_t dim =0) -Constructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector&amp;lt; Vector &amp;gt; &amp;amp;&amp;amp; vecs, size_t dim =0) -Constructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent.</description> - </item> - - <item> - <title>ldsCtrlEst_h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</guid> - <description>ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</guid> - <description>lds namespace</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_ctrl.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</guid> - <description>Controller.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_fit.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</guid> - <description>LDS base fit type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_fit_em.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</guid> - <description>subspace identification</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_fit_ssid.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</guid> - <description>subspace identification</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_gaussian.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</guid> - <description>glds namespace</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_gaussian_ctrl.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</guid> - <description>GLDS Controller.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_gaussian_fit.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</guid> - <description>GLDS fit type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_gaussian_fit_em.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</guid> - <description>GLDS E-M fit type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_gaussian_fit_ssid.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</guid> - <description>GLDS SSID fit type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_gaussian_sctrl.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</guid> - <description>GLDS switched controller type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_gaussian_sys.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</guid> - <description>GLDS base type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_poisson.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</guid> - <description>plds namespace</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_poisson_ctrl.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</guid> - <description>PLDS controller type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_poisson_fit.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</guid> - <description>PLDS base fit type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_poisson_fit_em.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</guid> - <description>PLDS E-M fit type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_poisson_fit_ssid.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</guid> - <description>PLDS SSID fit type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_poisson_sctrl.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</guid> - <description>PLDS switched controller type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_poisson_sys.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</guid> - <description>PLDS base type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_sctrl.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</guid> - <description>SwitchedController type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_sys.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</guid> - <description>LDS base type.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_uniform_mats.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</guid> - <description>List of uniformly sized matrices.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_uniform_systems.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</guid> - <description>List of uniformly sized Systems.</description> - </item> - - <item> - <title>ldsCtrlEst_h/lds_uniform_vecs.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</guid> - <description>List of uniformly sized vectors.</description> - </item> - - <item> - <title>ldsCtrlEst_h/mex_c_util.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</guid> - <description>arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C API)</description> - </item> - - <item> - <title>ldsCtrlEst_h/mex_cpp_util.h</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</guid> - <description>arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C++ API)</description> - </item> - - <item> - <title>Models</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</guid> - <description>Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \[ \mathbf{x}_{t&amp;#43;1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} &amp;#43; \mathbf{B} \mathbf{v}_{t} &amp;#43; \mathbf{m}_{t} &amp;#43; \mathbf{w}_{t} \] \[ \mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right) \] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.</description> - </item> - - <item> - <title>PLDS State Estimation</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</guid> - <description>PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated. -The full code for this can be found here.</description> - </item> - - <item> - <title>PLDS Switched Control</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</guid> - <description>PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear. -In the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system.</description> - </item> - - <item> - <title>src</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</guid> - <description>src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time</description> - </item> - - <item> - <title>src/lds.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</guid> - <description>misc lds namespace functions</description> - </item> - - <item> - <title>src/lds_gaussian_sys.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</guid> - <description>GLDS base type.</description> - </item> - - <item> - <title>src/lds_poisson_sys.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</guid> - <description>PLDS base type.</description> - </item> - - <item> - <title>src/lds_sys.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</guid> - <description>LDS base type.</description> - </item> - - <item> - <title>src/lds_uniform_vecs.cpp</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</link> - <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> - - <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</guid> - <description>Uniformly sized vectors.</description> - </item> - - </channel> -</rss> diff --git a/docs/issues-contributing/index.html b/docs/issues-contributing/index.html deleted file mode 100644 index 76734546..00000000 --- a/docs/issues-contributing/index.html +++ /dev/null @@ -1,250 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <meta charset="UTF-8"> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> -<meta name="description" content="Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes. -Contributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository."> -<meta name="theme-color" content="#FFFFFF"><meta property="og:title" content="" /> -<meta property="og:description" content="Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes. -Contributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository." /> -<meta property="og:type" content="article" /> -<meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/" /><meta property="article:section" content="" /> - - - -<title>Issues Contributing | LDS C&amp;E</title> -<link rel="manifest" href="/lds-ctrl-est/manifest.json"> -<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> -<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> - <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> - <script defer src="/lds-ctrl-est/en.search.min.93b2d0e7fa872893809358a01a9e6e0c0ce7a384100570924726e75a52c21d85.js" integrity="sha256-k7LQ5/qHKJOAk1igGp5uDAzno4QQBXCSRybnWlLCHYU=" crossorigin="anonymous"></script> -<!-- -Made with Book Theme -https://github.com/alex-shpak/hugo-book ---> - -</head> -<body dir="ltr"> - <input type="checkbox" class="hidden toggle" id="menu-control" /> - <input type="checkbox" class="hidden toggle" id="toc-control" /> - <main class="container flex"> - <aside class="book-menu"> - <div class="book-menu-content"> - - <nav> -<h2 class="book-brand"> - <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> - </a> -</h2> - - -<div class="book-search"> - <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> - <div class="book-search-spinner hidden"></div> - <ul id="book-search-results"></ul> -</div> - - - - - - - - - - - - <ul> -<li><strong>Library Terminology</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> -<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> -<ul> -<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><strong>Tutorials</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><strong>API Reference</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> -<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> -<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> -<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> -<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/issues-contributing/"class=active><strong>Reporting Issues &amp; Contributing</strong></a></li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> -</ul> -<p><br /></p> - - - - - - - -<ul> - - <li> - <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> - Github - </a> - </li> - -</ul> - - - - - - -</nav> - - - - - <script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> - - - - </div> - </aside> - - <div class="book-page"> - <header class="book-header"> - - <div class="flex align-center justify-between"> - <label for="menu-control"> - <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> - </label> - - <strong>Issues Contributing</strong> - - <label for="toc-control"> - - <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> - - </label> -</div> - - - - <aside class="hidden clearfix"> - - -<nav id="TableOfContents"> - <ul> - <li><a href="#reporting-issues">Reporting Issues</a></li> - <li><a href="#contributing">Contributing</a></li> - </ul> -</nav> - - - - </aside> - - - </header> - - - - <article class="markdown"><h1 id="reporting-issues"> - Reporting Issues - <a class="anchor" href="#reporting-issues">#</a> -</h1> -<p>If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please <a href="https://github.com/stanley-rozell/lds-ctrl-est/issues">open an issue on GitHub</a> and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.</p> -<h1 id="contributing"> - Contributing - <a class="anchor" href="#contributing">#</a> -</h1> -<p>We welcome any community contributions to this project. Please fork the repository and if possible use <code>clang-format</code> and <code>clang-tidy</code> to conform to the coding format/style of this repository.</p> -</article> - - - - <footer class="book-footer"> - - <div class="flex flex-wrap justify-between"> - - - - - -</div> - - - - <script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script> - - - - - </footer> - - - - - - <label for="menu-control" class="hidden book-menu-overlay"></label> - </div> - - - <aside class="book-toc"> - <div class="book-toc-content"> - - -<nav id="TableOfContents"> - <ul> - <li><a href="#reporting-issues">Reporting Issues</a></li> - <li><a href="#contributing">Contributing</a></li> - </ul> -</nav> - - - - </div> - </aside> - - </main> - - -</body> -</html> - - - - - - - - - - - - diff --git a/docs/katex/auto-render.min.js b/docs/katex/auto-render.min.js deleted file mode 100644 index 3a6d6639..00000000 --- a/docs/katex/auto-render.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=1)}([function(t,r){t.exports=e},function(e,t,r){"use strict";r.r(t);var n=r(0),o=r.n(n),a=function(e,t,r){for(var n=r,o=0,a=e.length;n<t.length;){var i=t[n];if(o<=0&&t.slice(n,n+a)===e)return n;"\\"===i?n++:"{"===i?o++:"}"===i&&o--,n++}return-1},i=function(e,t,r,n){for(var o=[],i=0;i<e.length;i++)if("text"===e[i].type){var l=e[i].data,d=!0,s=0,f=void 0;for(-1!==(f=l.indexOf(t))&&(s=f,o.push({type:"text",data:l.slice(0,s)}),d=!1);;){if(d){if(-1===(f=l.indexOf(t,s)))break;o.push({type:"text",data:l.slice(s,f)}),s=f}else{if(-1===(f=a(r,l,s+t.length)))break;o.push({type:"math",data:l.slice(s+t.length,f),rawData:l.slice(s,f+r.length),display:n}),s=f+r.length}d=!d}o.push({type:"text",data:l.slice(s)})}else o.push(e[i]);return o},l=function(e,t){for(var r=function(e,t){for(var r=[{type:"text",data:e}],n=0;n<t.length;n++){var o=t[n];r=i(r,o.left,o.right,o.display||!1)}return r}(e,t.delimiters),n=document.createDocumentFragment(),a=0;a<r.length;a++)if("text"===r[a].type)n.appendChild(document.createTextNode(r[a].data));else{var l=document.createElement("span"),d=r[a].data;t.displayMode=r[a].display;try{t.preProcess&&(d=t.preProcess(d)),o.a.render(d,l,t)}catch(e){if(!(e instanceof o.a.ParseError))throw e;t.errorCallback("KaTeX auto-render: Failed to parse `"+r[a].data+"` with ",e),n.appendChild(document.createTextNode(r[a].rawData));continue}n.appendChild(l)}return n};t.default=function(e,t){if(!e)throw new Error("No element provided to render");var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);r.delimiters=r.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},function e(t,r){for(var n=0;n<t.childNodes.length;n++){var o=t.childNodes[n];if(3===o.nodeType){var a=l(o.textContent,r);n+=a.childNodes.length-1,t.replaceChild(a,o)}else 1===o.nodeType&&function(){var t=" "+o.className+" ";-1===r.ignoredTags.indexOf(o.nodeName.toLowerCase())&&r.ignoredClasses.every(function(e){return-1===t.indexOf(" "+e+" ")})&&e(o,r)}()}}(e,r)}}]).default}); \ No newline at end of file diff --git a/docs/katex/fonts/KaTeX_AMS-Regular.ttf b/docs/katex/fonts/KaTeX_AMS-Regular.ttf deleted file mode 100644 index afcd2eb4d1488b6eb04b00302eaa6e223812b012..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70972 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 LMxXrG{U84UTIJEb diff --git a/docs/katex/fonts/KaTeX_AMS-Regular.woff b/docs/katex/fonts/KaTeX_AMS-Regular.woff deleted file mode 100644 index 4f575152f2d92dfe48ed316b668e5558c6102c93..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38868 zcmV)!K#;$8Pew)n0RR910GHGN4gdfE0Tny|0RR91000000000000000000000000_ zQ!g?A0EqYi002?|003Y{ep(7+ZDDW#0EtWh00LS700e5-(mj!5c61;B0Fkr+001BW z001rr#Rs2eaA$1*0E?&q00oNx01KDc6x;A;VRLW*0GG@F000O8000O8000nYY<Xq? z00Be*0E2n}0PJuEI{6Q1Wnp9h0EX}Y001@s001@+gLLy~Xk}pl0EpNC001BW001Ne zM+g&WZFG150EZ|500FK500bQU93<3iZ)0Hq0EFNG00IyI00IzcPoyesVR&!=0EEB* z001BW001BYOALT+VQpmq0Fl%H00O!I00z&?&Di#EZ*z120F;mb00TS#00}==i_r9N za%FG;0Fg8R004ae004@QiQ&h1+T6PbeA`x%2mEGULm>cy06|hzBPo%TWJ$ItlCClx z$4zptQREU^PLC5OPMqpgJDc0ycXPSk`{ld6?Y&+v+k5Z*dN=*yfu!tc?e%`WnEL5Q z;LU(C=0E>8^B<5PnIMGxIVK4ul4x%h3<xeAskjxlRB;OvuRlI9fywjVpMd{`Fydag zfbYe3lRDW<c9TQomm2YHpt0-s@1q2&fP_xZJYU{<U89$11c(Ml31f*FU2<x0LU2w1 zqK#X#GtZAiR!n2}bWDHJ>ZcmGxOH~6kp*&aZuibrD<{T>hYI=buB0agsYBfq{>b1& ztvXpM6{JY|sa{O_QWWx~Vs)}snW)ukUZOUU3cN%rs7wTxEY^KlDHRc`dF2-_m&ULp zg{(}-VyPOwk*P`%Ugj+6*1v?N%agX?$8TcNly;wHop?~Q8NBH6SgKbVg1zxs)XZCA zFBayx2O;mg%B8a~TGRdE%&}wt$*ARm%%rj4ncds6iP6Dfn{rK)k};u4Ght;R4>gTJ z^V7}GCYPF06AZbbnK{qa;xGhhn4>b+eEZ(mb8zPwcjX<|zfI<P`bwaqAVd%|jMyFc z`(z1OPBxNRau<0NetG73BfR?_0ZOKqqfTuwWF638(_vuDa4CqEK%>h+*E*|Qt(LCj zcqO0N+1b{|jS!tms>_oxki<Y~9MuG)l#)3@h&5w!M1(m4;KJ7SpRtT+RHvGLlIY6w z&IM1w@QRM5|Lvbu{@?dmKZ}p^LZ8|2U!!-$ibo$geb26)TQ;xQxMJhFwbjY-vEg!m zUspPBiR6ekS+4}C@TD**Wu@~@$U+FudaY6m!5c3Xi=_~+sd^z5#6zo6$xYQ1-iw92 z6e5}QiiJ`tP_7g8&_S(ulf`gZ`kjjx{7Sf1nk?mug+ekssZzl--}`~|oxP^+Z8YFi zN@Dl#W#933lj+&+1P}jt*&fNhByZ=8@!KV<Z&^L%CIa82Z%^o<lQpOhOeQU1t=zIA z0U&J=^ht*C^1f&0XxS<v<F=VTgU?D6nZL8IFYDKbMCnLkAZFj&%hA*XlgNz(_9hGo zlBF#@asKZUnq}d{dAWMi&E2{s^EW*NCPS-hV#&;8yr&TKddbzz-79Bou0GQppV~R< zfH%DLY@9yxlqsbkgp$&QAL1Y2d&pX{mmDVVY&ZkJaX)Bu-yjmLLtbSah$b{5PKGWT zq1rs*9OoDyCT1iE5k=F34viHT>_Zd6Df0P_;|r`COMa>$fH=^x7qBA)N(RW>?ADo8 zD<;Ns{oP$@-_XfgSZhSo76nj9kyk({hOnsBCu$WH!6@97iBu{nMXA_&6<VP1JIhcj zh=>-IgR)Rp7_5qdC=|kT`18`V)3d|ZJlZ&LGR}GM+yk_1E}y9P#H54N(F9{wOi(R8 zv3Yn%Us4<%O%6R3ln(E5o444sx@;fset5u?VqJn#4}Oy|!*VPiUt<W}?q6jGLGQUE z!M(wKuXd0o1I9g%%a2OQg`G)n$oa+M8|M#fe4R|pxbUTfH@^S%BfZbA@ugh*itMA= zGh#I|J79#7c*t+hhx}F|E67Y3)8`ucwQE*anMR=<%{<X4QVmb)mr{US$V$5v$17Mm zi!85d6eW4XTeWh>_H~VwGplA&lbPa@WrpY)@#^6|6J}(p6S_j7)Jo_nyqM6FsmVei zjEmb&>O>f>ymZ5$ETnOm$SSGB_bPH@SWuUgzxm9@zFW?D==8tw4(ktpTXL?a97m^= zvUre@{EOA)oKk*5GNdq@PqA2Ux$m9?nwi^zKiln$JYFacu4bkLF1O)tGpYqo9(8Nm zm)>w+44D*MN}xjOxKuX;N*?P%aAM8xHotE>mIYmqv}i1Q{_jPcnZtiqj883n$%t%z z=3Te;?R(RPx#Cb4GAhMk!bvQQ+2di%_K{=cR&t7*0k6?B0Yc6n-AOfZI~dws0LE=V z;r<&Ci3lSZ3*)nzXu=Sh5vC;&pyZ?j1jC;@i4h(eaMUt&DukRRQd%={JH{Czn<b3d ztV8qv`>)}K*ZAM$?ksXwxEQ+kULg0Lx%ZJ9j~_aCVE?9&{A<;vVd;|3WiwvFj!8lG z!9G`Ics>esi<Ub%9VCU2GS`Mpv<<XCQ?KWORMHPin0CU4o(f~hXtPk(E!5HWZcz-8 zRp(U&v=ohM8uc+=c%6Jy;J}+*yXR0E5#!4?EPJ?@y3)jM!Op)=;VLu4ITz(@_+H|e zM!BVFj23)1KUrHT4T|pMN;hs=k}>(w+gSMTHbl;0W7Ui~57{x>mU=?}9nOGe`kcNG zht=6i`SdfIZaKpT(w4+O5Ud3`YHK)QSXA<uWK`Qboi^j8d{T2w;KtxB5Apu~p)QY7 zgZ;v@T)+o)`~08VnvfPw#QH~3a;N#Rowg-4&Me;iJal#Oz|oPg2!)$N6mBPn$UM1& z{7oZ3All)B2;|lqQ<PCS4oF#>Mn(w*1d1n3T~b1{*%)Y=HAC8IAD=M<GR9{K=QfWR z<w{P@6`lUK*t=ZeDMiQ)M-J@Uxr1yc+f#u*m@JNsS)zLcf)@KL3@Q503#neodJ7Jf ziFN_d0hzCODLi=vaHZO?nWz?vRZjtD0XN|rTthXf#lm=(-P&x<;yNNaOY2TNVQ@rY zxcQ!Ie59%305eU|e10*8J|1DDT9JqxrfOyeAJGjb5l>S5eXjjJGo*0+v{TZu$aSGT z`Vg}ITz@WQXs>&{(EX+P9{I#G(ltPu_T_+6Xu*Ox+dd7+5Es(N=Et_{hGFUsB`m57 zei+sTJIGOTJ2^w%+X&)7$vo(A%tfI;aB6OtG2X$#5CNhK(C3LT4M{l@Y9ps)4uO!w z3}H;0Bbw%E9UPR@0tG_nFXwrQd1J{>x70Ltpl#1IRU-H&?zrR3i8Hs{R37wG<tabk zh>4yNxLPHk;vKEmtCOuDhXkS^AzG^uST0r;sf*P@DzvXwFDVtgLR3vACu+6uY6@Pk z@2A%a`^&j{ygchm=`6Q}X$n{^xv3QrCTcfszP1v7=vM+&a`dzciR>f3-VpGXP7N{o zVj0sIWOKqnZc2RT<tvEoAA8;Qbpi9m^g6E)lSTv#&Bz5?*Q1^DDB7<+9aSK!+V#a7 z*6Gxm{b1q``&!jS^R2qwWp14@VkjHSdlH(eAnlMf{wrjS>&Z>zKJvkafk1Q(OoDA} z3=@+uFxk<N{0)s0qR%EQ)OBmdbtI=80B0}lg0Jl1T*l*PHg0vtc|uOzbK;KqV}}lg ze{tBRZ=Kn2ZDVan6v?EYOeZ~sl@<{QDcpD%$%q&f3A8AnT8+5j%G97>qug7YO^a>{ zZA8h1-lQ0ZN#(n6j6XKH$d)CdEO$w6Ha{RuQ%?_b{;!-5WprJa;i*M50aMa2WVV;b z5dRf`=^%Q$LO=Ntb_ZiD{Dts(&`zk%<OB=3LmHAvYKi98o8RCzm0X>$O-b$8qvy^& zsoC@jgu!e>QpS4jdhD?vKczMU1)wE*i$^qx8=GI;_<D1N*EvB5692*lxI4tgC|N-s zd&@8);hiG1OE4sa&uYNAH6tmiIFSVuEV6JGSzK|k73BmWs)z_bceETN(}TLmEQ;zS zVO?k--F678iry~`)J-ZfBZ4vJbw#0hyJSqyba_k&F_H}r<R`=1%Q-v2@rCzTY8+cP znQM|u<eC8)=+Z4JS-7TdVC?*dV$ls$*(5H!3BDBGv2oHMyUF!$sl^dw=bm*FLL?BW zn2-ZMA{ZjlSwg9$?#Y#xo$#(SVjI?b%LBiZ$!p>NraoEgY<Vr(Q~&!*d9Qu1P{BB? zp+Mab*Oj70p<bL$7Tdhpi&b?>lyh-sC~4a?T`?sxuh64F3o{XD%Yq~NNB6`w9)g4F zW=V+%n^Q=C6xeP|B$%l)1Fhb^VVNrQq&<p#CX7KvcV4iG#bV7LH@|1ByN0jAS7}<R z`S;BaSuqGDHAsVV{!LcGl2*p0ItMe3E+LEr7cNi|-qR!DPIj6+Lw>Ch-??Qe<>G-; zYZGw}yu(!emZ?j|xd>q;gf$Zq3PTP+<|q(ilNK@;+c}r9yNZcR6iPEspS|<MEjJ%K zdg$QZ-P>pUAY`H5WZnsjl|f&2bh%nBL}f|JWDs1Lb*e9*on)%j7ZuxfmCiO6MvFw+ z9$W(NT=+ixY*h%+(qy!&Eo*IH;VLD@)91Kw%owM+1IOYPVvO~5*od)<Y{XcHjRrK1 zc>7JZ?Yq8Yk3AMMwUJ%H&qG(d%a+FZf4WFb^ZriB(W2>fFOJ8$;l>*?k6O&pC6iJ! zfXm@qPg(Ze$GSLe(UQuc4lT74DB>keOy2bL&4)cVmFT9_5t3=TG}n8t25deb?~36q zm+(~Fgn&6FCYvXI4IC@Q0q=dJU<-#4y3mPDk_}`RdG5^(z=giK(5XcYDJTG$jrwL? z<Mahh)yNf0oyDeCw`#RWe!aSDXV}7TsBcIGndFe~nqdNaoq%`tDFugxmMH08c5_Nq zFLpaL9@@iXh!b^*@SH25ssfk}T!tvk<y*#U+l{Et$#Su{&-vmlQ4|pwrGRiyP>lrx zmTHt$Oi&oI`Tgei%(%crYDYP3N%<xU+E4B+J$#`>fdINB#?JqdC^4WCgQV8d*y%5| z^Y?M`BJwK@|IEo-Z`wXpP6=)hfa!BUU`s1VeKC_!0Gy+MXw6u<<ctt8YiKBhEusJ| zHghgx_R}p~p#l@!fA3v)sPw&Vf3NS2_^YZZ*A_h?w^nK)p)RDRV%lCz`R(en(oR%K zqm4-IMGFOn@>`8XDX=AEnYdu1GC!0@pcIDC^FqV$eJG5$Dt3OQQc~IOg&zR<WcW!{ zHyE{KJnnT15bX3R4qJ|9fgL@{`F*FQu2W~!Vu&;qV?N86&A(`$699c@ZU8Mt@zFDg zEC|=Rsq+2LC4)`J59suf2TU`N20U1)NXc+&N|;EIYxMj!t}{0wyG&e)l$pq-U<`iy z>+A6w-{q(>Tg&Lp|7re@G*TQGc!p$gU4{<8HeS2G09%`%|1vT}7>3~sq<Nub*W!N- zUiAZ$HNV>YPSCR~Jo*Hlf1eX$3{A)B`A0D97(M^Hj6zg*`~cq*B4!iWO<vzf!~t;U z<`rEgColnGLDuZkIgLWhBCJSI4JOevdxi+%wXm|-%(;wNqmKwpoVtRUt0}D5Gt>T1 zQ1nL%*@O{cBq};o*%1LHgh>k}sp{myMqKIiq*AzUga=Bce<>OUiwX`TRiCV;?S$|# zpy;H#)8<|GAPns-{tD7`Um$f|zE?74Xybx18RPUzpQe<i`ckUd_;bmz`WtV5AP(?; zz_=+9K~I|?!h<xAa~q*{V%bN3>krJNi=xHDI%flB^KY6Tlbj=DA^7I^v~*|nZ6EtL zAJ2anY-2?2zxlA2{8l(4%a9t`N$w^$zGZ#|QtZ%bk|2^f67`9cE>e=c@H|q2^Or6) z(ihJH&;#u?swCpKEa@4}F_9jDsJ>BqsuZn3Rx1}n-6y=eveUh_n?m)PR3<#s7KOC2 zWuO*I#mIB8r5YrYsRg-GCfg|;g$%Y0B#v#n=0Myu(`%0Y%C8=s_9;znnf^|ndCf@8 zqtvUVa!!Ar+aJ_C#=Kg#`Bi5H9o1OO<tu&d*DOTr?KUaAk#z|R0s5Q_YJI%`Xf4a` z?{g;c?X28?Vsr7u#lx$PAD_NIv8<9fy=9xnp<FxLxBi;mr)$2=nB!GnR6aR;uA2Fq zle;ql4Tr5dQZ9XBr=&x>HuP@Z@*#An1b~sHo1fentlKvP05aQk@RhUc``5NL<b7fO z)W`;MUC6qxdh5Y$8>qfeyyu8c0Ckwx1ajUHkQd5Ln?~WgyrHwm@M<<LjnZfL&QdCw zDf$_u9QSmT<5nc10JjyJMK#hQA-R50M=rW<s#q+vkdW-eu^w)Wt&6HtQDBquw_mcQ zw_MyE?_OD0<xi~ypa$^1v~HR*<juvZ@~7kpr7GXf*+=BaWKs(xEsx5oC0Ej%BT_va z^r1aPHxiV!<kI7#J+<D1$sbDQRwo&>DxJ%z%Y)W}x~$odhVH#<$C-$;7rz+oYqCq7 z+t6l-k<B-aNR}Ahxn{^Agon8|6Xo70*+S;S-Sf5N^9}nxVAKK5UI79W7xsja8X&-h zfLn+lDDVbCX(uO@ye6R|;za|5jx7t%1LH>*J4Nvy4lT<aOF}3kbp8?#L~(!!fd`fA z&TL~ynQ{;(uWCE1YN98fc-c#yc<qy~U9)QGcyD*o%V{Fn%K1*p3mH+R3S%m0Q@}WO zCOhHD77HvQfz~uZzK{f}OFP2P;20_vxX1xw!K+9M;mSoFNx76@Te|P)wr6sR6MFZQ zt#oGFa6032D^^>*=Apg$G)qjcXc5KCw})Fik*L3rC%QjN+bppnsr_0U6i3iFoTHA# zZ2;)EBbq>ID3*v<KEIMtEKco?b!D`eJ#cNpRJ?J`Yl<%(JiNMZT;sB@XHZb8b-VuF z{E|3Jt(bnmJsvX2j-)Ivf2!|n@44kI9;v;kyu3bqu9kLKi$_)*EtfvN%V4O8<dwOT zlVC{H=;-*Sry?R@D6-kPkVkTB&;Jr48=tZO5bb&{L3qR~?+<xp1=&dUliSDxz-FEw z3vm)BAjtf68>t~2K!Z+aUwFz4PtCT#H%N?_iAW7Rs!J52%|4dKDWx#WU}1oDC1d9@ z#$!L5u^SC7iD{r=;>pk+u4W#Mw@=@9&)p~Q+Ouox%r)y)ud3I^$I7KbzB`*v<?Tzx z+if;mm_`>u02Qv(_br-rD>@6O)(X>YI;+)77YQ=yx9E&2#>>k>cv0nJzW)R1M@B7f z6>d%3wRgaay;aU1D=zeSixnsK1$kGicVl1KNed;<pz)EV%W}TYEIT|OXTZ8|c%lk| zx^}wvf!;$_wKdjGZ|zCsCb~BTT>FK)b#CjLeD)2Go#^KDa)J{}TkU==BDynY0&dB{ zJ6|NMRevi@XCggR?jB4s4o2^hqs?ERMmqNPF=rKFr2E3ZU>x5QqU#trM_vchjl>$@ z?B3(YwoP$ebAUvQXGM=hNXg~|Fcy-WrtvwVlqjO>t<mO8Tfgo!{<*8VxQks{%D7H| z=rDg-*F_GIG9KCxsWCa<@$8s8i_Ir~R&!TL5(7VJNy9}-jx2bnN>u^Yd-W?1ADY>+ zYDKMD86Pirc_S>S2HUf=LW@gFoe^48WL;7Rg+FSkGkb68?TcqGEjRT;CksL$QsIBF zzM#ankSp7JaOb4}to$Q1tDdKb@i*RC=<e&zr{lUNIb+f?^Ri^uM=Ci#u1jnC^o<9z zBlln-HeOB*Dl0CTf-84!SZ2%dbtt#*-Etrs>_*NE!?Y4iHs8ZAv7|h-XLf&F)0oH( zJ6>05-&FY;PxK(8fD|ArbPL`09=@F!rikfG-hB@<C=H5LbI2G-=sl@ykQ`XmzvRf; z)yp=!NNt^22AA)bx^4jV$M@=3ALB;<id^5?iBWi@2a!7ou4m(kq3Mb8(7@oeW646^ zv$LhOBYlOSR7eNq(L}PZDUh?3AZ+TZ`Z^b~>cbJMt|eQ@4)UuFpA!^hdd&*KK;ZO< zkD}8b=Q)4~5AkF~T5S<STU@(c!O~e|c~zr!^|p1!Px|8n{`g=bs@`hRa8}`{jR`e< zjbA!(>u9pBVy}q6BGA?Ag;1yhx6m%)ylC#0qZ(eTQD!h<#%0kNt(ua0J=uuDo)@Mm zyL)`j%!#E=Q97n3B-?4NaCGRKQ<^RN!b;26@mDlTwYNwX)4H<Gu$E!i{CxBChQTqS zDBG0C4I6SWh%BQGJDF}h#^vkuieol^48zS&HNTP4v;?LrdfX(G#KXSl!(m)Dl0)Q1 z^3<De1Wp#DTs?YjjSxC(NTf8R0?rrpK8tOf7uYm1gcIO!WU&>AG$S<z-Lg6S?2X+c zevnyFP?`}0Z5^;vGq=)e^0FeLzfc!lEODZE)p=A#ciN|*(oW6rba<3Z;oD=THt6dN zkhzoVA2J`F8j5jbvXIs|m#{*brpS)6a9zf$@nU5#zC=$*06Ea5nX%?S@>@Bgnd|P7 zxzH<XB0S^_>!2b2z7{tt>DtTR=IyAh7VCyrC%*o9JH~|MLaGP}7UQda_syGn*ZVvn zm-yw1{VTs58?VHqrY+IIF+ck0*v5fwgPT$xxHkTy@0wW~9V!SVB-9N4E{xV1vXfj- z?roTR_iWv=ydvmA%mPie?ljDC0EpPz>!i!#Rj0Gs2nZ$g)S}fWNcQ#DZCLM52Ib;_ zr>pQxcKZH_pfh@u)W%lric#fRj09b^8Vw)=FK9moGdMX}o2=Cr#G%l7Fo=}kdLbXO zpXX4+5I670axk^Pk3Z3~sUIYlls)}&GssF)vS;5Yc&69Z?FWq{rxTR~Z@w8hyfhY^ zeo8mp^po8JZqcY|>LQ)S?L8SL42Mw-sf_t{^UpT^_IBVLp$zqS(X<6)LMDVAa&Yrs z{R!>tqjSksnl3?Nyxjb+=2ydJt_s=XLEXvq^kglQFv2hVkbW(iCf-IK4z<2lHQYg< z<m@B&9h=*+eYpm-Bi3gOT{ER%l2S6u5fzPHOrOgdI5QV>2PmBSNrQ;3x(Rvg(YsC@ zKBOjuTc<Z}Xsn%BI$ZASO~zD@5v4$-Q&^#rWyY`&iKyy5jdJ1A7)D!~S4${CzMVn2 zPIQw&sux?O8J}zgZ=${94Ti`0a9zc-RxOrrFjV`Z7FQ|td1)B$6v7K^?K7O&Ozhtc z*=6Zt9AnzIV@Z$8CvNQLU`WT-^T*)HA>n7GVM4EI3g0*7e(4%WGuNI!#qZPf<2OSo zyEhq!7inu7i1uB#U_S2N2h%}D0teKYjnBjck1<WnnA2V7f0Hw&f9<od@~#zMqsZ`~ zG)G4h3JpxrrJ201`DK71WXEG=Q_~f`thjU<KX4~9TR$?pKEwTZy!nmhw`P%T{>L4) z9>=1{g68L%|BOfI$WmCdhuS<NGZ`&U2wUhK=E<257q29L&~PRI@TF&;x@&j0Ake`@ z-i`?tjeX~MB<nerp-WDPHWxQh(`>B`kj0kH<t(pi)Kd3)33<iKAAjuB-8bJz=E?k_ zgWI+iMh5&be=uEe!woN0oUFA<q*C$HERML=j|!-WV#6g>J4J)JmNX%fYBi+c<Rv4a zHj-L}efwE$NvnmZc1kE`^<c`R>QxmDC|pJJ9Vn{DwPj_pJ7nmt<$vJN$rl&Bq%;J7 z!yAM`MnM|BaV_7@49|&k_Ai{d$=uRfb|?RX-o7r|HGbhaF6?Aw&k!K1uSGo_*9<20 z#_II4?-WL$u@<Gp%U>K^4XdpwhTH<bc@7w4(^H_uW7YR2#PmnYLBtNWA(@0=Fn;%q zwwv_26DBf-X2J~C<!+<;`SNI&r%a@waou>?Dp<SGumV3D)0oEaaUFp5Ueo-oH8V+p z2&j9q23ywL5SXTPVlhaZ|5KWvRys`yp%H%G7vg6Nxq;kFUfnQnKDei^+Yn@7zSCo9 zf-_3ltSON(YerX`0JE1q61mvOS!DFn)?H{npfrE%x&vEh$QH6?!?goRg_D@*Y7eMe zx}pNaLM7BfPBmnig&nw_Ya^tsHnjHV_U@scMe>!BoD@-wRIP?93I%@19r$L`k9mG+ z+jfe)?y7lD^BwAUqAG#jkXn`l)3Q}z1E%0X7e#Z^4vOc1()gf5mE`rK(|V98dB-T@ z_i%CG0F!VozRX04zb|zcY2dAR@XYB4X`0)Xs<e3;Z#wq-!`H1q*D#~%TZ)7fJo)5H zKTY+xtM_YCci5O|L2+zT?)h78H!aJ(^VGAyWLi7{uC!GC_q40r3Rz2L$X;^xEp-QB zVRIfP5E=nHNf-@(?vw%v!C9ghz|=KLDW5eUdP*$&Q;eO(#<PvCty>A%x_9fI=}qe! zwJNEQWy$5slf{_GjHIG(M|ka2&6ny^Sgo}ut~Ql^yLyT;R7fduG^}5RXe|s*DnFuz zH(ofnX+1kuGS)u!-m#l2hh4@KrjCMAhX4KFu?=_Jd|2OkYkB1kn>*6%L5a=p{cfTL z2mpE<es$?2V)HekQhDd!GVXivL?0N`yyI`7?hnj(y1maENdq!8B;NbSzx%9>mcM^x z*5uk>Q;GPf8K(|1X}ieT`9IbXmNx&sw-^0{X=3|(E<v;}pC(U{SCMx&><u8bSG@Q| zyS8v39r7&^!_tv}LFP@6GJ1+DWZ;NH1pv<Six2u-!Q8o=`OjwAdiW!E_UzeLoqg3K z51l-5_~7hydv;$NYA&@&rMdXtkQ-OCfv9+rLQ!jrS)(*l!7E1HNVH|QiB&CDTl5|8 z?BA70XKc;cqugts4!El%z=?>`<I={Y3UxJVa=cn8A1;O~ex;U}ES2(7w&(09gVNEQ zrHPlth78yEf||v-RO0}Yo}g54Dttw=AC=6MzCcum=FWPTMuC=%#HdHXlZN?zv-!*A zK|ln?)bAxD5OO$jYWjeH+1kMako3)&US}NM>9PY^ONcky)%F+;002>HFF{J#%GI(E ziyCT!Nwc7#tqsI9gA0I9eY#-8hXaRc-O<KA2RRDJG-jXwn#D8~2?U^y1xFDm7-;&< z$JyO?xe4pwl7ue^L1Geus?Iw{UP1o5k$m}Wy_E43P^JS?I(^+9)Hb)iB~x`w6scsq z^}wGCECCc5ows8KRY0(GR2cIBk<11@&;|A7f1k6`N`5v!XOW)*-`LMRO~}))__<e* zbL8CFCm%U|;`Uo^JbL8d^#}It-Zi~>?V8oAc9n_)!+UdXE*%yIIkk-!s_h5MhUplq z79#?whA@oemWV#ok=pV@%OJ#Fy^<_M(seKPVWnP+3IesZuq1pn{1QTj2g!<hFMXR0 zW1-L%s4Gbtf;OpCmm1RenTO+MZk0wq5OZx^QWZbO>7!@tN&9R*hU#G344j~voh|tK z*nyST>PDiZQJ;HhSy<Z`8)w~~t|jL8$E33(OAozK-MaYn{%q~cqm)OJbSib*jeP*A z?TiOo$3|nO85^N=3`{n&H3A=S3$ZVlO(9qBfc`1fLIcDxFeoc%y+h^-*uFJJ?KqBw z3w)40i-?v1u;lu_@C}dXFx(TE@`Kj{cwWNWqV|ve3%lrR@WZ5+tP3&s8}M8s`P_E2 z#c?q0XSN~NU$FrV#->}SyN2i{H+6oJ*u>Or^C+PNC>*&stF%A}IY*?7&%}UpHb)q< zm9%Hf5yS8d1;MWW-B)rKT}gqtv2Z0By4X@2z2YUWcpb&MQw!H21SP@wD_!pjS81%i z`UNy%P|cYCi3_pT7ruIa<(Hm)&C6f%^ivN%aL?U$-f`XjeY>}8x_158*l5nVbat!Q z(U+n#zHQBKYpF01+N#i{@?-Q0ZIme#Ra&*SUpJcBsH021Y9hmT7X^;i{@nSlKuZ-V zgvrs$6mL=;=kta3tT6f@cv)t6nST1s%X$qiy8h)i|KPEc8NvO{6EFR9ebn8%?z+=) zj_yZZ63gderw;XWPdmunbvHHt%Y2Wi*`9C>!C5ROJo~Aa2cz4454DwpI+uOvm~F=V zehxCNWqc>gJbENS0nJfStpYRM(B+B35>|<&r52;Gyy0hD58o@mIeT>VRJJ=g*8A)y zPTlyr-mzr1`_y$)djgRC!@qbWo%IU#i$CZhTGQ#=+$p=@Wz#qAdh+}Ol$HU60k4pZ z0hE~->dE%UMPEgU`pNO?`gmg05M0lUs9hqqUOQ|K+X!>l8&S7^|FV(}0Mx*P*8>-> zrx<?t!uR1k{*W9dcay(ur1s1R%7q4uZ(xKHwtgjszo%Qe`Y4fvP)X(qBN5Mr{+f=_ z*UZEm5h9^P_C@m82oWTr=PNp2;?h`pb!!0qK-));zHJuVdB^ejx!GMirZ+XFme&Rg zsbtPj5<pA;jxe+^Vu>&s_5SUWFI=cAERLbfE5M1iX0ED=RIMIW>(NY6$>Vk95}`gx zl~W|rwQ3NBwJ+(GAH#A{w`x7Z7N<S0O$;pS8{O%K6uNS-I6kyF5Y+VhMKTWH55@Pg zZXr4K0*Q^uM14{-OaNx@-;)je@}NXLP5JdZjII?K*My*ZO=*qP@PyC@i|MScFBx8U zjlX)m|Cupvy8D+Fmo6!dY!8z8j6oC2Rt`$f1gqPq0r1sR$YX`#P<T!;{3K%0wBqS3 zP+Z$w{>Gf3J>J;H!P|1>@j{Uh2vPCRIDu~^w?ndF_W@E3Ncb`qp2?{a-PBB7JIMhA z5DDZsk;K%cIZvY52_l^%lv*uHib03jhOg@EEI2!hokt>PM%g`AzQPrp8xvRa(?V0t zII`f6(NNI8?UuROo!d8W7%B&oxnQ7Fu+(Egp3(q1D5_T{+ZAU-IjU5RdWTf2E>-oV zuTHly*nS*ERi_<DwgFk4?8v@0wikvUF|tJ-V<KK$lF$Q3H<51JXeT_neVcCU`tuIc zxmlSSU1!RPl%4cEM-RN1S(=WSy;Xy`m2!8#H)@K0&DFsWZs4Z7;6E5J-PWW*k+L`< zlVXT5Doxy`aiQC$Db_oML})ZP(3|oqFKc*q+8YTpqw9u%_Gd!ItGu|fEUu5G<l{Z- z2dPctNdg!C376@^bQ|d>@rH#!HW5N@?d<{Kk3j1`n;$F=sL2wOOv&1<J2p5_nc#X} zQzi8Icb#p1^wof`Z2r{~=ih}wP&=jZERLb$B5uLcF?0v;P;yy1HIY1o12J1L0Cvmq z+XxWEd-V)`imV{N+A0vbIKzeSr69rqq9h`MfIHgX)$SvNF>(_J7Md}7{4!>xt6b=+ z*HA!m2q7!Tis52^?}lYU1Pi-ow0rdrPGJZKRsTsL*#b?eYO<f(SjdO9WnJy712nGX z*Vm>F@4exkUHvXF_==H=futJdQg3U1_Y(sfH)J=>?>>BLcfT7x#Mj?h&3o^Jjr0ki zT5oZ<kK=2oV2rQfBBj-5GL-7QgBMRvaov;4X9<CS`y?(S*5zaBT`i{k|6oYf=YJFL zz3@N(Kac5u{`3E>F(vg2zaLIf4v}FZ-qh>D(l9$G0`*GyQmCq}^$oj3LebVlSa?-} zsPrmG>BEt9uFJA9hI;IdySr+--d)?f52*p&qszAx-n6#<h7Syu!3N60A9mt%oN55O zVLNgFL@p73={Y0>$l!(l!Y{#}kbU7_F-)SrA|gl!6#-Tvnrv~T=#Xu_9w`DXvR#X+ z%q*$NbY!58TCA&V!UAxhUc)!JT@Kr{K~K5D;E-MKGQUSp%q=ZBI@S8E?ML3M^>@Y7 z>9w15Q@48Qz?8+49^;_72P*@P&a6xf8c@*t;ZJNkX7RBRKffYg@X=(ZbPD4+Cop3Z ztNNBr-Hq|M>G<VQC!X&AUz__5z&4I6?VUGmI~E-T2$CoS!QKgql33IwQdIBNvMt$? z<rdqKtKyc6o$eCHC5i1zZ@v__l*`5G)fYSMnlJ57F1<OqToOY6%q##x#W^{DjbdW4 zJJ@~io0&In-hA_R7XhqON=1?WbU2(0b@iv9<8k6h&26nydmj3<h!&i5<%!ERgf48$ zfM`Qur9&thQEGy(_k>AWU3y8<<Cwe;LDqyGXfey<oEVg#zvCv&d8~fqzCvKSEhjK7 z4T-py518~_YV@h>DI*r=B}u{?sFGV)WaO_NvyLLN7#m@vBRtr}39tafnP*mpF2`}8 zxih>z;+W*pOVW(K$k7X<zYj%B3@&p6)bXOaLFxvukwJnCm&b#a%W-QKT?UxGm_bZn z*mdG3@VD?@ZI{5X+Y%V&2P#|EeK#<sg1Rz_n-g=ET)lT$b1*CDsC7L7Ojev1SJWxL zjrxnXDDuu7p;S91uy+sOTsj6=w>a1ly#Utel!F;dr7vC_?d*t79BoQ5Myw^H78LY| z4cyW(#L^*$!=AkaSg|pfxoBtax^1^DO%aOmyL1cZMiD}Y6!u&;5lR(;%^h9KER5|X z-5t%rTp}@k`JMuiB}Q}Da;G<PN+CUa!>*a0oytm_K))7kDI{=U2=)2qEyy#%9nJ)E zY6%p5!DYJbl=nLhy7f22aoDfmU9LNKuH)$*%5cjugkSKJ6F-GV;7{6sxKpF->AVNa zwpYrid0<<v3Yfr0Ho0c-efD^8&=1KPh+!P+nL=L$gBag_HfY%iU|hN|y7i5x#aEk= zrB=9SG76M1Oe1|$U;HBV1GA1;>d$v^W;g*#K%TIJ&fE#dpwXU5CEYjB+u+YyNl2HJ zdWI_m#i}jIvS>Uajw`jRnmo@{R2U3aroE%K9-utN{@Iv_Cp-!?l7v7w;4ue5SMQIc z2EpE`FQ2#1YqSac!VSe4Ky;`j5&_`N#p~aEcqY@F8A#yv-ii+WljuFn%mo(7RylIw z8}KqA<*<-4J1?aGNqp2M%svrKHPMQ+;@|K*z9ZwD*(T+>{jdLO6@oNGQzl1%xEJJs zkf~!`zyi;HC1^Tv1n@f^HyL5nXf|R^VeTP^#OdmNhu1C{TNt*8u_pcd-vPl+d?Xoc zag|N~F-*ZHwdF#}(RL|iM99<#F&2x&1k=cmwAUC$%3joZMSVxg$-bp_`wzzc(|$8X z%s|cSH?^-l5~ehA>+WddhE*QnPGn+Pw5huzKY39TAsu%X#s?Y$g*aQ{a_31KWoh6Y zfnZSQ7>J3ThMRUa31e{RHixts4iE4=Beqf8`1-`qVB+!BVHY8UoF)q~$Cj8+YLn>g z=uxd#Xp;wp`N-h>5W~4LkP#(rP5KJzYSQ=>ZUo&pvuM0sh_G*6LJqBob^8GG_Bge0 zV;bwUbF=GiMi03Rm}MA?`V9&|jgbG*zsN8whJZNd_38<|S4cG`q<W|#l~><hl_<Uu zt}{PbC5jZh%Fg;SY7~Nq=LQ0sJ1HkZiZe-=Y6zkaBP^e6#Ejna90KSJ5;hNI4Nsyq zsx89r7ji8Uat#?mE-k_{YFL<G4ig>*%eB?;5%N`&Lx`p^<PSL!@nsOd-!1POv6}*j zQr+q8Eq~XE4G<&I5Qx$25?vo0i3J)G1{sVx54;H<!j$V)Pxog(jWpZc80wB9Y#V&# zN-w!JXktjW>D^t_rkSzsW?}}nBs>880HRiC>2grLt0Mc9kiDd<akDg7D%A<3n8HH> z#X6(|AQT%>FrTDoPB<g>v(JtURm58P+Ldl>02_&v6VJ7@xNdtq7k5$#0aC=qwETrS zML+vl`;WQ}v+J-g9e53!gC=S2EH*`%%hK^eR~oPx*cf*it<w~AzYaFxze5jc1JxEQ zWUGwXd{7>%qfAR&YQR}0qZ+NEu=>qcu#H9!w#VXLs*_)D%%pn4sWt-@P$xl*AyWQE z`JaupRJbQKV_eTWQ(ipQ{>Mc}5afpYbiQx{&kc7PqG(8zH9CiLe8WPn_YLO&9g!o7 zknWLs=~@}DjEbck7su)-)1o3SrIAcVwbwyu<NTH@mhA8&hx751#S?6VZ}{S&PE<Hq zym<bP6BnF?co$UABV5Q=lq10@=w)i?MQfO4PR9(MsY+FqW3K0ga4MdT&^jSOQ=J6$ z2xV#r4cBnV)Nmn9Rb1wJP89*3c_A!{r=bAa=!tLPPs4<ep(HZv4Rodga556r_*jHV ziGnQK7Mw?-b*PfHBdUU8HDJ)UQmyCi-nMjAZtWmX@^X6x6(}!WJ~_$>YTdUz8m(cJ zd(H6lh8@%EZH~$(To<7VM&DR5F*cDM?~gU6q^vn1>mx$ePHmx(b+9XogskBq-*Ju3 zqZ5m>Iv|76LJo@&yp;7BbaEv}E!{l0dA%h>_2CN6_V&&O6{u2PCqM@$H?GP&JUP}e z(6yr#1ws=#y8GSBX1mwreDMZ$G&b%S@HG%(dnb^SU-9JR)Pga~>_5~w>L3k?DdQ$O zDC&5JsN>#hE30<U5w*Kax#iLUQciii(gE5;jSDS&t1iDQhf~<4tol$VxIYxdrIB55 zsMteNqx`zIHlmxjZ=@knT2Lgq0c&Rx4I_Qn)Ja?0b$m2Mdb-X%*scmMTTW;;*P35T zOoQAFZ@`90*5+GtSxS~KlSSik+V#ir3(ckDda(^CMg1cU$<kOm!WgYxn`{{AN0dTa zQ6FDwUc6x8MY143F4x*Riz!3*-isK;v#qVU9Dpo??0t2kheBnLNN;oK?5*#M8D`K= z51p-9KHKpJd15d*3v+!|1Czo+Lmz`@v~2>5<-Xrh>wWQsebPS;dTOU(FqreLk<xW! zYHyopuIYmLDumP`oBvUNP=8#1Vkbt30822Kvx*JD9-1BtwGikZJA@!N+HQ%f2obSa z_*dArvG_vluE7FwmiGmsSlM<BeN@O?5Hd&9Y+g22`LANMa#fMNlB$tP-$dPo3kdD# zX!K}Lu%T$pDH#YNiDR!a3u!M7&s;Epacn48a8O~e%a%J+l?Wb+5-Ak2+DCJMG^F+* zKZd@dtq?M}wHZs*Ay(Y>s^2LD^XI}Nb?%(+x+%H*UN<~4EQ_FWEIimeGd-AW?_;jR z`SEi{#xL5ta9T%zy>!m$VuWJL+SO|i#A#`JkOJY>(!mhHR_<NsOTdCd9i5}EUl!!9 zOPa6!R3SEAyk;du$V~<HD4~f4;G|={2gaTPqmW8aAdQ_KCk>&-3vQSFqetlWP4s1L zrO+*1ubZ!&T3S1K8mgvNN)=TwKA%xZma4gk=q9siG`4g}&-%C<WA4CGi&IR49n%e5 z2x0W@iUDEB>1}F_K;TW6G_;)i*8VQeT*e2prS0fTh+6s7=HBG$uwi$k3D86eC=0FV zGyps6J1l5OP?8RaiRzIzN63Ze9N6P<@xQ>4^Ol!3s-A!1eeew2uC142p7I-qYKtv# z#`*lLpnivFow`*XNQIJ_i}*hg`u2kOKf0{ZYH`i}HVZns+Z<zba6bgr6?;}~80ec_ zv=3SPS2{d^Av~O&+_>pw*S^Oq98PgeQx?bAqA?sEA}%x+36OA`<@CmM66-&7OU%7u zQz4NLG>jd*HO;sGO`ZdFDJ52O$A*JfZQ1dzYr6+79SMM8^z<e%LKtytOE*CkjlU)| z-XJudQXYIsO?PEYMRLAvlg3K*o5FCFLRizWu8mgbAGM@MCDpaw-N{%-kLk8pjZJRE zZnklZS{QRHMKMnuE_nAG<;iQ`W4LPvB7g(jS-Q_5B>cn;jqywGa}f%x8yq#9PPpEo ztQZoS7g5CPELj{$O_IGmh5><*OS=bO^0zj6*R}cl*tsc-Pyqn&mY1!Kt%_ao77}{P z6?}|ev8g{s^n|SGkDVAtFNMdnn9w_{^bYz4JsR-`w2F!NScG>>M`H*wV_gQ&Bn1TM z!}fH+X7RWgYd6X_m2V`rO~gIhPmF6nsN9qJSPs^Vqu*qPWhedukElBtU1J$Q&4q>V zZo52Fo{_gf$I%T&d(4c-^@g@H$|<cxFjxteMSZIl^#1;&8@BL>xTi(RH<oX#-EE<b zK8!wmx}3Fp`j&fm$nT?-d)Djk-*dV=Q>Q<=0Ug&s+Fbm1aKDz;#)S7%$}|87yHMxo zw5TSLsq=%-h#X3)vjEkB)FdD-eYbhvx_oW<T4iK!MYmR+p+7&-GaC|@8#+vM3%V6t zlZay180~3S_;Kj@s&A<bc-0>&-;>%g0J)(a3z2}$!0cI&L5J&PZ}eqv7P8lmb4ofT zT+OpoiVX?*E1C`0RFj(398D>ktW+V}brl~I2c=$2hqRb1TvNUVEEj}kx0oFbLbD=z zzUp<V7E<vv%fDQ5KDc30>NJ5m0fMZap&VT3$Iss<WM8R`in<)gsIjgFWD`VVb?DNJ zGSNBdohfFq`V?xam??f(f+!?1`WgXaFdO>}^m6oaV!Gn&wJl97fqKF#IxTZoI~c_+ zrq#J3+yime)Pf#0Et7-UKSd0RXl$zA1a3MUwe*`qcwlpqvC;EMc;^sf$;|_J$n0+s zl5aXO0Y8VwRIDpe@hsH%sKUE-9g*e40ahJI`cbu0)h@N^$RZ$J>(g#}eK!^G(WZqR zrfy)HBXejxN*Sl)L#AnBLpR$Owh#nuZ5L3wrqlB}*O-Tj(oHEEe}3L}Wqld_NmJ^d z>^J`WpRSE<qpit{pMrFQF69-mN41C|efqc*Cayji&#9J?X{~D)rMlKq3ShGi&)vmS z@RTh+U=oSGs0<~Is0>**#6%C;#yqTJ=bj8J;*MVtF^Z@`R7GeC>v4tkkdQr7iPOa5 zLEl6x_aZ3HjKu5AiWFKUvYfXFKeSBU5|C2K#TAeuZd-=gv9O7vBa3MFd<~G6h3zK1 z%R$bce=qP<%}L-Xd$2tByirWc#(v1GuY;yGLj!^GZv@JqMYS%`7n|aqnnj)76-5Rq zE})Hyll3-6ApR*Fk6Y1p106@lRp*;2PuqL;VDu<@)ZUYmS7Sv(T1H}{KL~U)qZP{@ z;X^+ae+JJr0p&~bq+@G!G9|QLAya$Ok}@ID(|txzJbIFGL&K-p3)x8ExA8be25z0T zg$12f7VTx|xX~W9=9UTBKCEPw_PQ9eMA6`~PSC)DC|%pdBg&8&U89-u1J$vklt*Cp z%_e*a{|&z%V%i#QyO40}Og|G6wnlhHjmI*AQHiODZ`H|&s(aB26t1-ZlYrTUXhwbt zwx6FVCMGwMh(o1pri9<;l;7B#a!u}}o1eD3TTE_Z<nVKzYGBBWeQG~<BFIb*+V|a) zY0{a_oD6exL|pf_SD}`c_q@j`f8ZT&4X<r>16}P7a+>d9&g?|eF;}m22N#7Y!(ctR zh%=q@MNc|}(s2K{3o~nUZgDo_WrE1onG0xo8l1O-*W>9t-egQm&VJ^^zu->`9CnEu z6jy8J<5ymC!JZu*Sk#u#P%16AO85-0>NY`^m1iXnTPW$nHLUvTu!4+>>%N;)rS8hH z($8QoXN3bXu6|cRN8p%|5iGxe_zCRn-n+}{XaNdM*v;NN9m3R{xhd=7CUL4Uh@#%{ zn`05z;*N~XK3Og5<ZhY{V{S~}lyg+kmJaL2n+FKBz4Ko4+E{GZAT-+^yXvY0x_2rZ z`<X!^o;cs01#&lbhE`aVFw(gC>L%(?(scFeB;}M?r4+<sI?PTqIuu2U!Ns;GiW17P zEGqB?QM3sdq1K8a2ctI=Vt)pL;T)q7%7tL2GZ<|27WK<7_3du0wp+BjP3s1^Xm=rD z7w20Oug1#;sxxMPzFZ$l@<Eq%AQyB5dsWs)N#IJ`GO7j2g5oZtHoBlW%P6B>y7{YC zUy~UE<dFkUg>7WUK6x(j5QNOch*kc_Y|2zEkGj14XUDnyRW!+i272#%;C`q4yG--j z-{us0(vH`Iwz&4}EE6!HwJY6`1z~D9PcEXmYJC<MG~B(!on2+B#uvy2k>ff*R|ML_ z2n=I-7L0TvVDN;7w3!pH7k1)Y6?KkTe#9(OVGSxP(AMU6I_31Zm`5|R`(;k|fHE-= zI(6rsMTkN?6v<}EwohA>eP%1kW+S0E!T<=mUF6L@p)Xu(pv%x@#Ii{FwdL0u>lWy+ z${VpbfKCd<L9m{?nqzY8-z^v{7Jy-l5Vvg8TsOvnIW`5pc*Zp42q)yfU&ucp<j*Di z$a6+YU1#446(>hZ$$aB0t8p9;&%1c4T3bbJ!4IS~<$o>zD|Q@=VklNsD2FQw;ap9j z@=wY?QRVU|FSRParu-WFkESWbQ}*vKD5S1SjSyG!I6u0pYGT2u(cP>JPN_zj<u2qZ zjZY1PLM!$80%6`uF2!3OF?6<hD-?8@@CbAVaPOdDw0G|;t#3DU=;=BwzzJ*KgfXmL zi}E~bf~^Cio!yk?I@Wcq%~6Vq#ahG@%XJ>|eYK)e(k6rj8y_1P$OC@y>peu&ow%OK zS3Y0F`p+g8^#uaue?)gy4$gK(Xnm5453c?ST{c&A;aWiH(h_t{CUqG(MYh%#xoWie zQ~|*{>vg1`*U9+Rvs_YA^W>kRkKtKuMDkR^vTwu`)v{CxYN$0&DQVF2>713N(5SD= za5dm@n?BWE7&kFCQ_(Q8E_mPq6F^@v8Vb8@`mGTpxR54B4AL`gljf0lG7)A|<9aH+ zY&jpEoEYI~1P$}fg(gMOeSG=SrWBu?0uwTE;Rj&RgLk4|<K5bnu*h2l;_GHbjweI5 z9)As~W|h^j9uEc}S8<|8<nUKerTn4L-;*l82&0VTE)~V9h=%1MgUDdXy7lED+tLx{ zPQV)-=8W)LUmgq-YJ{6hBaXFbQHFcQmTe~EA5Qb2aYA#@9X;Jh5Ar5-|Hh4I@(OsX zVZe-t<RY8XmCJ$gxpO&DD^8);BJ6k;yehk@6=7STYuuJy-HM1;eg-uG%C1Sj8i@?U zD1&kN$Hdou4*n6QwZ+;-?Lwjbft}M6O&V5go(OmGU?f%-zo~?+rSfth&ka><TWtk6 zE<Z<jHAWZpw^eE~JTEe*h&A`D7LC+xd54ec4rk%^1|SsrNN{5L9(%^#J>>S~TpqEz zIx%68u@DL_-hHegwPlfr<uU|+j0}fD%Px#rIKbir&^2yz`i*&D7;yP-I#OMc9xt@< zn1x~5@;D8)CJ-~onk~u1&Z!kagu+8sN3#{-c56{y7#bDN+&Q_MnPYab8})H;_AL<$ zG#Rge)?zmt{^iUV;e?>_2SHDTB*Wv_#(=b$2L;FC^XLiH!rNukG@h+x#Hxh(D{Z1w zdq;|2qJGt%-PaQF+#WX_AdBO4%T`K6Puj7aVicY!idmQ)=v&a1x*$zy_==fEq=zHY zfJjU%f&wh$GH@FjqI^Y@(PHXmmNp-9iXEZl2SP+c!lL{TJ%n713mF-pc}r5Y2!8L@ zKwEI)Z{ZmzYlhY=Jd>2iK>6&#UsG90H1WG(C3#T&-d{OFo6=&}b~xHp{-KLHkZCv^ zb%@)3Q?3$y6TT8jHx--G<&h-6{{bgZ{*ri9`Ae>gh9!Qx1mEC&=>1wy$Yz0>ABy`^ zQgORm^KFG)4reHa8i2c;iQ(=)fTR1k^VMfDXWV)E8O-IHm(~c+pl^#2V2O}?L0=px zU*6wp&?XSBk36bTKrPZJ>QpT{r{3FCpF1TUgJ%-`I~$WWj)znG`__$%&Hk~*gUbeY zHK}urJNpyq)%}C#txlJh%^tEtYkFCF&7r}zal0?loo&vW!-;J7U~^Yr-$38UirSe- z2<X7b>b2*|9In&(=B~v$I<jW<NV^T1AB%_k#LTlnprXrGHK+tfT5Yny5mnQTl^&QV zj+$(&TNtglZL0hmWFpIvL}L~}<7hRp3`WuVH?Jvw2^p5deGl|!;{IP0oX~cGR$H!7 zhw3x5s<C65KQ$i|HJwxIZ2|x{K*+y8#-s-?9?D^qG2jlS<&P3%&IZvx1p?oq0go7o z#m(0=-FCYaz=XG8kk2RO;}kjjuXjb|M?A9W{8jna=8@^k@)aRVo0ix5ge+affNXPA zRz9j2mVC|r*21M~;vgyfSrQ@AlE8ho?#w+C0F1##+dkb)xozBn5JtJ2_=C{R#;ckc zl<z<nBVxx|bPL5fqdH~uy?X<eZm}g8bh7<p+ldYsO)(VMuqE(e_;AM(zm;k?qxY*G zM60ns+4^@mt|Cf3<T&Lmj^qZO_}K}9+Tj;kUX+biq6T8N8~2s)*Iap!<=Xt{j8Eb# zU1b>#hv8AK+g5wnE_uu*FgiwbJ2W{8-*pIo@Xh`GwvMRP8npW79uj9z8a4L!AzSCc zu_1-zkI^)GMANmfK+-cQf{LWF`ZDs7!IBiF<hr({Qx8StV+dz17tV@Z-e5WMwvTYi zxzdCI6Pk2O&%CC!*y$rbM7{WRqErbot{xIp9nDwiW`WJhE#lAkBxNEujiWnk%Sy@l z8Sf5@Gs%Hzp_nuG!xh`nbppTlE?SISYOR```v-?JNN!(IzQu9S#zkErhw;X~SvguI zPP`v(M&Hs5VKI_ow2Fm1n*)|sO06{t;|lVRuvtX>Rp(GJmrUex1G$^BLz(Ol`mOkz zY{;VflUZ>xSH5W|n;ps?%gPV=jCk!CVXaX-BM}Mu&j{C^(K`RM>QmyChoI*x2X9r6 z$y6WHP<dLH{~XoJZ-J-LQLRhH@-*okDQCN$OeLGPk95_5725n%7i@JX;d!mS#A%6j z9he*lga(UwCX*!5-K&H8;L4Q~t@@(2f^kc+cQ_r&nuuC6118Dk2h%K(nmG*i@JNPq zw0D>><@?_%?B^v~r(B(4LKdXR#6rQ!s8#bY=hrQ2`@x_vyOS_%8p-xmndX*>$y{4L z+uXIlq>M7KR}OTvH}+5DbLf)GH#Tq<w7c6U0)f#5!H^d}Zz6>WWhL4((Gcof5Dfar zv<PIL)iw%g#=`Z;NMHu47*(jPu{xldAgUzgQq&x;<EIS|RhJIu;p<q$CpN_i;3d-r zVD7A$j;3|-gEomI;~3$n6B32Sw?qlKgX-L#Vt7WB5zFaKPNbmw;1>Br0Pxz~(jhZ; zY^EsS;b2w(&3Vvu``c2HA|LnQJ=EaFdQLVmRUp73?d<}56-&>;Kcc5Ku3}{^rW$uc zl~I~T{Hm`>pQ|zMn(*L2rL~kCxWRSjZi3tPFKtM5FItgpZyM>yi&_aFr?Y=Nw{qda z6}izF1{Ng*eb{we+x_C&iw$>RVK7w8k7aPq)0g#iXZr`TYe%z`E6(Ei!Z&=Cc7dpe zC9)pE%I#AFXcN@C#=_B4qR727RqA4!94w>;)lVsWm0%@?V&ykioMsuUsOl(6tq2UY z<=R_UPDDCWD48q~%5`UW5I2@)C{CcIB`47s3oJ_CdynmwpByOs<JZLJnvNo8o>eH! z&T?vhxTE{#mpj&-uUnFhkg?T8FEO|o13I4Vn~4O|9d6P^J-z!0wM3Qe#CW2sIfist zur+`wy<iAz*WEb*wr8(eMY+Yv@+lyk8McUBc#K<c`2rgX-8P-<ZuV<%K(yHJYDH~> zsKI3eF#z9Q)OD1>(!k(=a%<`;Q+<y|st$F1`2pDhPcB{BGuyg+;gW@ION$Ah213Z) zx1w#y)L5devpp9GH5klm7XK|><TQ(dQ;Rw#MM09lfzX!z)~O}q5n0ZOG@unrw?jiN zA1lUEX;fIbC6UkSp2xz8m=${33Ke7q1W4xc(PA{!l<mkCdPHgFc_h*xZRFsIe}Zqo zQeiK)X@`W3+_QZ}PafeqE~C;xn#Ou2tG{|(VN#FCmYpk{;X<jlH0~$e5MPmMH`Ra- z%YD@I((%lW{UyG<BS{Tvk9Mcp8={4c2TE4QYL{c%v8*L9an1@oM_V`T8#6lBRL^!= zv6gUQb7`_godnyRyTl?KEk`TB<Ye=2VS;eJV3PJGyLGpDWH#(Dvot|_@@^pA)R-=e z&8^%^K_4CGJ^7F&Pqi)3i71m#lis`$Y%bsAaDx)yCg%hx|5O+D^=HDbo)Yb2EE}NU z&o6y%bWmxfz^GZ<Tp=p1*AD}gNJg%Tr=YF5kMMPiOY7Jp0h1j#*8x@vB}?fr2IteB z_i6P#HQXe>pL>9rfk&(r>CD>hAbrF|!ml1(zIuo|HpMJ7*{ByAazRc%6rl6pFtZ#! zn{lU-S>MaP)z?C;GjyD_7MUh2u4Mijap5||F%Hb#{Fh`y6m#Nmt`QMkxC@?vb2LMX zXc^HGok_Dj-y9Fw9Ec<#j8xvpK!pJQQeM3*q;?%m6<Vh9%`N3;UAR8rmY*s=b&@cK ze;Jj>TlwPV{BiMg$I?-~G+*~GX+o;NiTmM?utVcoNZ`~VrD|<SM?I5CsY3o_DjPqu zOv-gq7BwK9g#mU(iQp{*xok3(0V2!fUVa9Fel7fsOiQXkO5~onPf1jVQ&HihE1W71 zWG{dduTB5pCgIPB2hQDjMyWcoL(<F1NvZOQhBJ!wXsRV6N@*I@JdT`@NVKJbZ!wh! zsu3xIYo*0!)DWRar8!Y?Z8;W+4~|MHOm^&Qo=EVkk1<mU;r&W}`<3v>yVrGhuagZ) zH6kNDn>JKymNY>1jo!3Bo!+082q{_MYnrV!2*15wBGlWKZ45i6L<mJHj*mow`Ex|y z2|F`l=r`lNxm2Waa_7`tvrU~WIb@>pAI@Bp=k-j4oPoyP@yW}Ziz60lXR)^Lo&mV3 zl+WT_T2xDmab{RbIoMffNyaUW2<Vhopmu!uJCGD|BL4|QT80Z}mh>Njn0*`w0fIP# zk2CCDwI010jpxe(+#7_rpH$*r?~8kri@rx)$}F@B2rwu=VKHj+58YP&1)%X?oKb2l zR#7*=WAJM&C6MhABSm{_wke?D>TWT@V{zh}H96TI9#K<JRPlWZ@j_cga(Gy`dHMT) zy7U#Oh2Rh?f2aH$aAfPyMakUb0a)q<=057WF7o6d{MzL@d&NtKr(QuB4Z$mJ18Q4L zC<-t)Vi`Qi`4?0~-*irrGcIPu9YW65OjFXSdHsZ_i6^DL^VM2vErd^(ag)h()QXxA z=TLsG{2dm;1hxEO?msv>O}>9)?rpar8liC8D<C9fe8p8K_}s?RBm-?@`Frpv{7SUV z&7!AFW<oyGLK__LMN$ANOQuP$DyUYH25WQXK?SZ0>Zp#<U+;&Fh@-+*4pW#r1ec#L z|L)kG$fU%feC`wC16|YG4?uN0pP!wzoKJ|=tk`?rWjW<n^q7q<5Ug7{afK7-sg<k6 z1@vu-!j4PW^*4^Kyq+=29TcX7v+^wU1_zOCutFZ>95gsF7{i9aygoDSB5({|XvNCi zTR#K5PWz;`MQA^jH4*q;hNfynt2p?rt%bJy|C0JU<YZ-v5klua!Kp(ivXFV~j`H*5 z#p@40=H;JL;u2c^xx*>r*WI{a)%A=peQx~40U!`qbAq9F!7oJZpDz&Dy`{^N2vq0= zbu%IPPr3^cVXoEwwCWNEtFn!2(;@j!ZHOxj`3y<l_YPO$U$yr(ToBfI-=Jf)wq=C! zUQ0s@$6#IQBII6a0p{X_XY0H<(_!0wnP5J0YMDqr`1+TeOHIUg1z;@F1pgc+jXk!N zO-5|pGNX}{L9p}6$4R4?B2T>1!Nf>KqNb(WkyvZ3ScB&mdxE6AM)J5{%xf_J{OrA- z_`sdaLDtm|g)+BY-r1)5%!yvnYCbJiP*w@dmQUrZYE(&BL_d&SUBIe_1t*{irO;g* zs@}x~jJydXj;&0tr;<nv9(Abx(8I8PRwp`eqaij{x=6m3ntkLWQ;SW4b@#%KmSwI( z$o~DY9WmOmvG^t|))`T$F@?%MdT)e!v(s;WgF%qR?e?BaFB$C92_rh&x%I~DOmH~< z8lbS3ksYYJyX)_|AW{s?0(pWQDV5+>vaVjkMcRzeaOGr+2^Cu<{KZg(dCi#bKq?np zML{ci`#ZOMtw(bHJ2?uuf=f7;{&!)cL)qK@7ClI%HtUP6sDJD{gXoVx33y3=j1!^u z5wvPGr&7rSQ;n_Y!8Zm7`tlY)nCY%-jTY>GwF{sNRo9}06dd>d4Qm^cy#{gh9oz6t z``d)_?CW`7$=(}B*P^v6Z1SBVqE;Z;6g?VZhgn4R|F6MgIHPS+tK~bltceRf<7&oO z<NB&Og8W;Vi&m5m;JOabLuJkLOeLv9Why!{EP6gFWkt2dhKDvf*H4ba*s`lFLP8(? zd?MA)iR*M0uLtfBV`lyM_~h)X88ukocBeJb81yhfix;7X9qXU}Inzv-?T(HPTWEbB zls|Xm@O9L?_dZBxMl8Y^b+?{_N>f)c;&6TceljNXHnPP#?rttj8k{nFDGOPS&JlY3 z>pW_5m|4w5QFJ>3j{b1{8(^ppVXY32wTiW}W&MH%fyYQ~HMXQMQ9<;06%kzJPpg&N zGh^V-!G~Rp#0sx$HX<47A=@Cv@f*j}dw0Hr3SlT(oL#aK3rJqkJ-E-L`fIc63JAte zL%_LycsM`P-GSb{8Wr*gzMUDd8K-=F{B>)(+t*WZhkLw--v;8Ito+ePqRS<W>dU;= z5W3?vr)qCf@5Y;u*BT3>MH#$p8#)L<waZ0B{NZ1PU$I8ukZTNR@Zb0p*<qn;<uy@t zD^T?_)Om9GUv@X%>1<wXT46gm($X}NXzNG>+eZOxcf%qU8t7~psGKWdfPZ~qKjhDo zNqyZ>6EJH^rh453b7p#TEo(2aQ7YW6zE2OfXmz^AwG~3w=7a<A!hQ*UQF?AN_zSo! z?%C`31?HUY%lvuwS!&mH0>g9gE-j%}0P-nYYNztx=l$ND_Xju{Bhm~jwUyRHjb=jS zywC@pvvoXo6*qMkZXvpD&AsDIXu(a$nwzzB47YR3;&WGF-7;avo64WN37L;_=a)oO z24%u`wb;Ked`TZ-C3%v?82RP=IU(aFA>(60#-uhZ*=#9Q10gwuq!n36&X=`Rl8%U< zE``QpM0p+Ad{vdw$R^v6q5CZ29EtaA{R<7*7_pmfe(CGWf55OZ%JJN<Zn*^!Y=xL$ zfqWNn3&Vg8@M_P7mtHGjM%P>eL5HDBN~M9tm*DcPVT<DDMn^B^RtT=S265e{=oc7i zO7cTO@?~PiFfJq??eJH|R5NI;CBYF*B(FRPu|nbEYR08FkJNH?se*n~1|HMh0Ev}< zeCXydwJZ`@w;I_SQw)bgY{Bje#<||s_Qs(wy*Og&U=0sHd1V7No5oi?E7I`=6OZ+T zFmX(ddBZhr!wDQ_W4p%-HXz;Uzp$g@EiuzuJu<uqV{S(5+;s!K4hNME9V&V<*qN-g ze<=@*RcRfSe@Z#!<P<7?jj#c=cO{7G@Jq2x4i90YcmO3A^@JH0kBofklCQ_WB-A4i zXg)Z3snq5D9nOs*ec6@2CJCCL#A*1`;b9957~ocFqVG~slr!o0<8<kPFvVWDdw(e( zmkKSov3+i}Ae%0`!bV6ZT(^*Ucw}|JuItl9ZJW@kuRU3}VyXTK9qLj($f}W2SE}Yk z8nee_Rp@P7ezkUK8!=7Nwsh^UwzTPB*de`l_G44oSlh7{KKJ^w?(%;ZH@(HZhY}nC zk8waeaj183z{b&aqk9SgOZOHXO_aBsg#oBTqC?xE_A92Hnk8pB!kJGus8wgJA^QIY z{tClxk+$*=uiYO2-HQ17C$q;gdWbMnqV((OWoB&dZD$PzdfGBA4s3et0?&;52pEBd z7iR-8Ov64*Xm0J<pn-&6*C+hCPm1xKeW-;<78dYY0uZ@~p%^Dh#o+;==0L^SiTX^- z%49(4%_6?{H;+Lfhi>|u0Xi`EyaTg`^J&LPPi#+afkPKyqyu;M@3E{cJ6&v;+zAwK zUm5_@SUP@F5P;h{BRKf-)U^49^5ur~Qa|$)!3J|&M+XOe>8k*a`@}~ug{D0#BCf*| z-ABg~9tmWZ-y9+q<Dsh;TCq9}<?fk0RXd?VN)08r+6ttMW-T#E4S;Zo`%y*p)t~x^ zSzM)2%<rc{x8~pN>vQ0|z_zQTmP6+@Z-AcB_*eod^&gNgi@Ga_F*780_uWe!N~keo zJeVeq&c(62cl#j9uR<F@sER)Kah-xA7cYh8c-JLem-QlXo}s}h>WtkG)S01s_pD49 zRst`3WzaNif|D!vHie}15+2*5I3wn7=ZW@x?$()P#e)^~RZC_k|M^o8H3RWE#NsFb zIVqn2CpIvs*5V7fHg*OVu7IkUTq1t@`a|}GcCYPBpC^=z4v)_?<yP&=W(g716<}$> zH6nrqbwOe{X&NoX;f8lu&bxMPVNI<kIdj`=^oEt2^Q#Vtn*_E_2F|;d2GW-|(#A`t zqq%%|!@}{1fdOXMgLK!zh2V0zJVFWl*Vs+t2qDXJ0G91O*{5^iX=;QIt_Us*PVSGh z-&DNX4=Jzq9D&jH4Lxopvg}WC6+o2<eU$>B5*=lHi0W~u%;#$^?T~sQ8TBoIyg@CI z_eSS|w_5q4Lkk({=P$KFYg-(r;j)z-i;Jx(^csQC*PgBbal~=1Z%ysnFI0H?z=09x zJ@4QI{hY*fg3-RcslYj}i(wpu^O*<K;Rfv3#H4roV1qNTv+bhdvhzaL-tmC+)aG&U zbZ_rC5XzbNtDg~SV-R1q&=AM;JKurJwt2a1aE%=QHlGNH9&@eMu2z>;42I?SmzO=H zP?rr+Sc8)~PBm-ipbY-0jmS0jNNqtbCzr4NK!}*uK5)Z$YV~CeuG{!GThP#^!h?=; z-^13LE5w<GE7n*K-#7R74zdbHB8Y>G+kmW)p1bCl<&?i)Tw-v?O6M-Qgp+lygZ1!v z%fuL5oIPv-lKlpo+olTry=_z5O#DyG%jxj|b^|)Y$nbbGh=!6McU&jc421gg+W>q> z=v+`U`0*YOsx3n3RM!TSF|5x@B2ES8$<&cLU6r;<Sy}bR$I~k>YjB+OE4Ep0eZbfB zO7XqnN~x*ipmZ@2!&G;=LRVZSRQ%6k7s%OgSLnLyIe|mO!$#=5WyOoygpSy8m4<D- zeM&=TF2}r{R19plKO&Xu?FYvlj0ok_&XR8le<r1Q!UEcrT|7RcO8qUg;Z4MbWr4ul z*P&B}4XJ^^vWa9=+2RjaD6I0Ei2Ho8avvQ~Wz9GqWPZe4+-}l^qg>ayh}nCvaqhk7 zk4cWrxo?`1e@M~uGOoNp@EyJeD+Je}N6ara$hh+C>cxGnX%ETh_nBy>Rmz|L8jX6w zl#I;P)laQ#e5fFc$W>yL>@F4cgR@(sgalX29Es(-^Lt+rlwV38x%$0RUgL&?*T|2x zJx$55+rH2MFcx*P0_$8P4&CR*HjXW7>l}sQAPOv*zEW&fJa_fFFfrnH>{rKQb02;0 z)dx35<5QIn!EO<E=&+NvxC27sX5w(gzRuBw#m>=1nubrp;N>Fvdt|XxAHQhzu^NTq zK5KnIE$6!6$5iTjUu-@uCrU~ar+BcUkCy6){Uv{RID5QiBQ9fCVN5W@kdEyzLYN_F zOfSTgJ5duld!w;jZUW4RYhb*nu~C*q;l@fq(RcMg*r*jVUIfH9coDp{P$;y94TOT0 z*}NnsCKH_q$6UvRSj;rNU^AFbAOff}8kHq2&-2S|!VvacqgKi|xkeNZdO_b`StCB( zZ$1m%*~8h3?>UyEjM3b&HxT<pU{L}@Scnk>#I-f5_<Ya62Q)_uYEco#PiuimJCzi@ zB@}R(MkV>KO>8EsbY6;s8TGIFxzZ+xb2x5du*#o|iuIQ1@-K`22eHu_w8kK!j~}H5 zLPt?!`3Lg9@<&m+{C)YKhEJA2py@vtpZZ`V9)?bPAj;1@3e$6sp8lxXW3)OgZSjn_ zmeo3?mhFXxL@XL`PS+BNgLr626Qnd6<Syo%#knGD#xtdg0M|>~RetKeOXhw~|D*h8 ziec5wk<R7@&e1Kwi(~dJx891(TdHyi!*-#G(Nzy!PRm~hWYf8S!5CU!5$$YAavg3f zz9eS7@^28T$D_t0J0l*g0*ic=;dMGFXwv6+odQe|&Y645@9KD78gvRaXS~Y4Eio>r za&j_S*~=n6qKs$usZ}4_;^^Zp^2*E5$2{bgmj=Wac!N0nmb`wSETBP+BWh?3lI>?R zfciU!Ye#ZSsk`@6wTB!I1j=uy-F)tX>QVXgJjs2G@RWK&Sj!2Fk}>%deEL)V;`1Im z?EoDT55BkV!7n6&1}DC7;skWS<JzFMSn_&@TZ}5FaZt{UYsn(3nGPk{C|XGz%^06g zH8~`6@=MQe)V19^Z+{iBO-|G2UVm(Vjsj)nAMReQoYaCDVT@tJ2@Z7KJv<6@I3W|_ zl|T2em^U(=@$|7*Qml&-oE+S8qhl32y3Cl)!7^xcZ0J2OwP<m(0InwaLcvI0BhXv5 zd}47CVPE>_;QSJmcwTLpiuqKj7t#Xf<PUjCDpMMfn*8X%IYNBhBC(#(#`PO5=hLxW z@plMczWn;3<Y@{$+wke}&eHA3bSz71^{vY;Lzd1cY8Z;`1DA29;ZPD>(_552O|$1y z4-788523Ub(2&yZUZLF*$$LkrqE%exfma(l{N$fP!J50N*50@z%?`_qpFAp$IICr` z6cyvzkUZ2N_bC#%`x8X2MM807%iVY1E%bT6Wl0|^*D)i##FD`sqdDRfgc|mVxrPG` z=%DM~|K`^`8E`2otZ#aaT3oY?^0yu5`0;FUwQ%5<CPp33tfq*Q*EFc<utu8|ZENjx zKZ4Wrki4{0>dCJr9?d9~qA#ov%0*97>gd6Xbx-FGG?Ze84jizYpG7xB0A1jSouOw^ z4fQO^oTT2j&bdhUTpbgX91-<pc}{qJI1-ZH^*^6cOO%_E?6jJ{DQun4G#qjj$C~-! z@fzzp4qle1bo?OtY9-?BQ0USbc6brW)^*qY36d_7F>KSJ7;yrDxfAGLe~uO|d?~7b zQb21IPl`o|Cg-}sruqxEDkw^kbb7L$od1>z=Ud#c2n-pnAAm;A@a;`a0Pv^luDi4y zxzOsBe@~fX%HT4(w_(Z+l=JAN3m1;~tM*rk=Ut~X%ZpytoRZp!Ue@};+R@5-J>!2# z&$(~<EA_}-`GOk%zG~9yB$1jIgd~}?)LvT4zob2?$?_L*DDSTdGkOL@0&WG7xgXX9 zQX|A}(v03H_zVN0E{Yjd>&VZst7NIYi($C73vqa)mQu@~C5y}OKa5RdQMa^4cc;2| zbn#{rQ29gU52|g4vuG`PW8YL)E)?xpXtCjThEjuTz}~dDA>KKrqgBHu0)HbS+P8F2 zmXT$9#to!tNO*Z&=y~*r+UXFG(Jty@mAD}1M?VLfKm|I`I3cEEG+Jn8x+z6J@%N@- zHO<UCrgMulAro1`sn>zApp~+b29+`Q%fi!Ym1_zu(fNpRC_YagX4Uv$4`>lTG3uzL zTjajQSItBam!0-X!fnDB#aCR6ej6xvdLH@)JgHl}{0}et-Gd#7n~eX*ZwwEEW&G+# za~CIcBWBb|U}y!&>9x3$KzSDONk}~x&u`ot3DxbPLN1ccuiWLK=dowA!H1<h)N}~O z-v!DDH7Q(q1$@Nfs>bI;c;P~{Ny%doW}<I06EKJ0J@5M;+H=~+HCJHFHKcr;;mSUC zxmjKMHJFoEO21|~vDdFB9CZE`Y9N1q*w+OIIHN_h)!JUsuD4DP#O--MylxMr>}p&> zEH$XTjmtNas>P`ht!{^a!dv+6;WOO{U99VI*KDLD7R%<Nk%;>_m(k4hk)s#t+-Y3C z9|c^u{OkS88y&7+c=X70hH@vpd|&z3j)MaGmZ$%<_WO7y7mGwZXa-JFR>(>xW3gDK zB^+4H+?MGhM~*n0HZMO%X2LzNyjk2voSJS?Cl5HTbI$VS^8LVR$_!aeNg=2BsV8dq z4o!zE1RJtR%ZeJ#Hb?yNqh5c`<0t9eCRc~<5O;1}dF1FJlRJlx969W8<ARF(#rG|^ z0{y#_U8PRz;_nedh;#U`5NGs#V^^H?)Vyq0&8})!b+E^SC*dq7ktis4%*;R6uxig# zcRzwYQtaQn4Skze9$g-(Jd_(pgox$q(XL|ewyl2dl%ELC`7xCz%&F|y619i4ctoly zYEP@9n#gtek|LUv9M3{%3=IQvJc`Qy2JEi8Fw?n%;GQ?W@g6`bGLOpdEq{n1j0}qc zT7c<;4+66cae_bm*0(;)5w4S=UC3a`79FcR3u!e9ho5*VGEaYDGLE|0a}c0mA%|tm zKUqM{@LhMAuglD|#?-?YT8lO#tM0){BnA&YXgrYL*yeM3^Y9G%N3Bz>2@duoWc{N^ z-Fid)risCUIwr5*;8BG(mMOq90TUz34s<Mt>V3;*((Rowv70+!0kq0fy(`u<7uBh` zLmpf$LPEE=D3xiBCnLckm_rK!5pYD?ZR#x+!5Efb?Q_cC4p8pcI#&^=pTb*ViP~$D z@oTLXE{_k${&q5vsG4BRS4AvYBKwIc#4$#0>5h2ZMuy$^(#gd{t^SC}@YuFS=+SLx z{5fuR954pH(x+pv^$-r;c;k;-v1yX<Q0s4xu5_@H<|j&;ej!b$Zr7y0Ph)5tDoM8M z`AdF`KUaCJ@&)4g+r|W0AAkbGHUqMF;#i}UGakRA<RXkYntH;HNfDftM}ITe#W@86 zVByxE9KR`uLqb}t_kCr+0LN*@KW_v-()w+93}nn<y{OYg{cTLns)Vr;Y4is~0KRVZ zuohsIf7FFaAc9tolty(SSzgUm8Qc8OOHjDEZ6I-fz<J9%gNSdq*g?pgDP_`;{OxaX z0{6a&pMP(d!ht|}I$RuH(AdNud`lqk;6r@WL+vg6p$7wheCeBQ#2wQ>2qP3Zx?WEu z^@(#D1B@cI(GQs~+Y{d)9nTfLt6QTO>9%EnyO0E1`*kpsw<ffGRA}3+v|T(LsKr=M z4zc<iIq|PP&Z<IJilZ)oDJi0EeL%M2Jc#d`s_M}2edp;9+*{GPrKL1&0+`dKT%*$3 zxhp>%Pju(TN^Rja<$$ZSg$|`H=u)BMdlD<+dv4N&-l}IJ-EBND?uEkMctvq!yiQz6 z7FPGJa2QW=p|9>peSMEew7YX*tFRj@<w|2m+!6Z5qys-J<*h8A&2K>;f?=s<rShVx zWUWxtcm4g0KjP~4Bu4}p&4sI1ZM-FuBC2x0Y;Hb0c6?=)p*zKOYE5?bEn~ptapIx` zMN#+Z=KhzvfZFmM1B(XR+u2vX5O9{G$nxGywrx#o5B<{TUH5&r_c*zp1x5XR?{nSv zyt2#c?zXyK`JQ^6irR9uj_fENjGTf+rO<4C3yHsVHyW>PFcEpxvb9b4wd)Z-o0PZ; z>nua5)O!7j*_UUNvSFFx6u5u0MXAK^6A1I=yO(oN`+h4r!PY=csm}hz1BD*`<u5qS z(_b~0uJttv1m5>Py?t~+JAdSTPM}U9q>U<lzN0M`wjt=-2H~v;aVrZP6)8n88W@PF z?K1UMQE2n?+2&Nflqj1KLV?}6;$o~*goTi{4hRuKVe#Q~(xR#&+)LM3;=mf}+7<<5 z!~%~$VU}`ZIzs0hZ~~u1h<0XDZIO?C&~-oX+CH~?oR&kbD+m_BrWNMnk2`9$;#;C8 zrL~0CAxGtg;F&nOHHvPl@`ma&Hf&|E6u3iZ`S*%@YvU^S7Ja6vI~lVg(uMM3fG9(u zd1ftfC9#$=z8bnS@pgm8!}(k^z@c6y#jtNnTNO{Co;o|voDEl&(Cgf=jY0&NqBTY= zLAGMV{*IDO*|=`RoyLIhu9{<39~jK7F<z_UUB^lJ56MmgxRthIt)V!LYZ?|c{Bbe| zql!zjUU-x%Mmy7<oU;+%(o3$4nzfZ;gGv^p#s9iXaNO3ZVTh!sWxyCio)U~*hUZQI zMriq8d$H(Qgx>QSBDF=GfDm<chRZ)H{}06es!PhxuAtH={Pm3zm9dT%PtG6Xs+Ld> z@mDklNR4oWgmYK-)gMEVVnem6;oZNeYI^?Cv~Dv5(aXMcWMd;@u1-)AHDgMNn05Yy zpynXPh@+fq+iv-vPiQ(<#qTZu>>y@n#5AahPy!`^5Ipx;J;VUQ?=1g#Gq9zW#}G;_ zx@ff1Lriz+-S;9IqHy1xa4?9`?e}p#rt>%7Y8hOX+jz&YbpLF6=NkyMDJy?HVo*R0 zJMZiQs^<3P8{lDlm7Gtj6@a0Rd~-nU^{sHj6vroDb6Y4;Y@nJ`#pMJtM*Q|nL>sA5 z#U8eFWA2+kF#&FaA#~7f1Ih*(zc)#oxeo@QSGZVDyT~GpKpQnVo_hwuV4(lXmjCco zWT21h_BVidj9~hC1`anFeDc~c6WCWRL-|~fxdfwUFtbdGzY%ijQ_lJ85v|iIA!ZHZ zLaY8<OG@pB2>bP?L=;E9=u&cC(}-3SRYbUG9YIcja8S1#jDF+GXA2}d2<IY-k*X>K z3v@>+c{9>3qXvYCP>GueIVh?FdY7$R3_c;HWTtMTuVYE)`^F;`bsn;Hsp8;)PM~f` zKxbME9XgDW^ddA(_)O1f5f!6tk9kJLi>N0q`Gm7m31;y#^3XO_C|0^1)-yvtx+6|7 zjGf9ZL%I{hr;*Um#|+D-SmDUT@a!IoUU3@5%sPdHrq$^XQaW@;Z8XoZSKeL4t><S? zI{Q>OgstJje`;`=CRTiXbMex%W}CqmqN&v*hfTu{3kKZyzO&((Nqb-6ne0(KlXLd2 zGW}h|g99~Q33p*|pi(Whmo-J=r?G^x>`XLa<=WMDQs-I)_l8Ge8=drPZV|8ckYmwI zBo{`|W9|+645E%VCb@1G`fv5B1S0p`_^biZuHeRco{6GYY}{BJ3G7{vMtub1Y#42D zoh1M4XNhDUKia#cFV02|`Mjdb#w^ccU?*1HB$-7ld}x8w(sxPmll43k#jp7MCoIG{ zGF+V-h-w<D@uznR|6*ews&L80l;gB|EY0(ZDz7oEFsfCyzu2Jk<dtX7Rn-18ur}G( zuMc-c!RhGmiAFXD2;Q`0w!w10GweCsG_bpOXXgOlyvY#YZ3%ZoLy>i>Bwgvi!O-q? z&wkCe=Ml06_2$#vhX*Lp!(%suII~FT=H)q$6X?CWy^%X^L=cW#9e?G^LVx!&eFp~- z7>?_>@V&=D^k2P>fulUf^1bjh{Ijq``$c<M*A<@kS)!+JEW&CC@kw^|JMd*oQS(}L zqAt&KTcnmpQB4H0>cUnI6O=m?%D*lD{@5MJ#?*IoY!U!{_tcU#oKrw_V1!_zyP2UF zi%gY<+FNyOJ75eqwS|I{Yht5?7}xF5QkPR~FD{jt*wIsR%MsUc#WTXX+H+*i?QLeE z_2*47hJo%o0Lao|>YRe(4Ye01vn?#Nxj(DZsURF!k#*dI7eLlXbhpQIxjKXz6f0?c z_g~(7H1xj-qV1eWw~JEccG(0Y8(GKhEdLg$^}mJ@JS(>)VGj!@&1Ia>8w6B0P~!c0 z`CkiC9ZGH5B?6_r3mdJ9{TB+K6jtE#^Z7!Rs`I}Ivkh+gsoQq!b>z|<cf8^6CM@SW zBc8)89Xoq>-1b!3{a?fmJ<i;YroF2gFm-ri`(1sELhoI&-#L+0dz(7UxkLXOFf3>n z3k>&+rKIgf6)IOPlvi()B$R$M0sZfR5I*cQe)<*L_c}zErn~g-3c`TH9n74)+i&|+ zqceBM|00HuF-Kw;aikT$yN46#yQ@3F9f}(yhR)p9zZyea;gvq6byTweF6in^GT~X# zY-_~>843*-YTLc)_6zxOQ_f@}8}1U}df}lg5%H%Hc*rO})mRv6Fg<d6@%<Jd<|7|m zFltcS?(HvhZAHu_w0Zi#7?0XEMSvQANXox^tF@#3p5<Gv>hTyFh2SQgFskdDHs5^Q zpeD199EDJ<;Ld%6lR@-cq5DR!FV$$nt>_{+j@Hvp)@w5))>x;7HmjE{9LcM!bOWN^ z2WqvfQnb~j*sl;>WQ)l8he7o!@2psp6)VU?aesH;K>i@w`Hm&9bU7tDbC!m}F#{bv zUrzEK`yd(XhzfoCV@cH3wpr(<`^alB!cgqpJT~@?M-0|p1mr-fW48I;dnq?q;I592 zp%@zOH5p|L?c7L=W9PPc#^Jt+aJbEZPWRg7KmAoG)^X#F=+I^-wrLL>*KoyS$tu>_ z9--szO{-QejN{6fEIf@KT&=3QBz&S5RD#?+ppxp<#$I3X(vW2MwW_LwXe?H#N=1s@ zXsupV8jZZ-kWR=ye8Qj|O{P%a&K1_=Tbavr_m#P&1$5~1pLMLSJ>3{}%>ZpkOipnE z3%869TWJUX;j^Ut%}g;qqi4FWH7vO24aA%p(78~2_x6<elHDd3HGJNMDB}f14ck+p z77s!BN5uC{7B+bL)N%kye~zB(NWZpuT@Jw&=sI{U@*-vdd)L-*$!N<3XLzT;WyjX( z)!kNwJD~Ik`E&J(Nv&?}g6NsSP!q=q$g18S7GXO@g-|}ukZ2n`HnP#RU)_BTNk{fF zq6Y!S7WEGAu?eYc@j7@qAOgs~UVivu$6()ntrG!s@nx74il4ZHPr<k0sJk>a2F@a+ z=_Ekk`fw!GWe}IBJzf$FI28?DKzZZM!$UUu&=TGF)h~ud5IK4j?Oy$N=%8A|x(1#> z*Jv5xwV5hcQpF(F2Q5w}`mZ&dVEfqC3yjdn;Ol02h*74uwC}q*pIv@st9gxO@TOHg zD-9IvzsIE^zz`g3-)965tPZK?y;eQ1MdmH9#LMe)ag2k4x$b?DCC9LgRQABF=Wm*5 zqr}!jZs*I_Hs$h78}G;ybkML+s5^ULVfU1&8xUfN75f5|dKa&fYsdPD4fqE5jF!@R zwPV`PCL6~Qfl0*3)B>PvCo=T?h#PdxUc}LA{|(+Fnox&P{ZdUgm~LDw?t(CqufNuI znMl(@(;8!Bmqv&u-|EhHX`F|7^)1{(r~F#}0*D6ZR&I0V-%bv^;Li+=V}qaeb2cMh zvzr3#=#fL`Z{NCl<+3HE!LE*Mv*&6lNLli|(zT_(dNlp2MMG*TlJjHj{)%*~jK$Yp z<}N;?T#k+Ne7{(2#m&Fn@K7}f@4tPcGCM1vcX}JA&VNHoq9u3Zmf6i)X13m#Yx&^l znnmcjOP;)lMaZka>B2AH_WEx$+jAdXw*VZnqML;b6A3w)>$1ef#i4$CZ1HGA<BsK9 z`t(p(UO@z0hizNgHk67v3rl*}V)#rXbLV->S8rXn<z?BXrtHhMtlRqBt(}QTdF7=a zxd2&TdVOQy-9P@&@xBMgHtDF1Mr{+Z_X?F?-cuM%P}gqEb@hlbkR`&1ZCMrr#Ms30 zq4s3A%^i!l>tt(11L$L7JuD$t(dXYNsPbsk0*wVv0I>{z=z}t6`a9l^2ose^5uzAA z?61(j<CZqL(%pfQ9Tc>>`$nq04$07JWn+?>6nWz2=$)4F_IF^%5%<<X-jSh!09vc= zNq}~gf7n4;W$h4s3|*{6tM>~FL+(qeM63uoWAnGaLrVF<4`G7+8(thJf2fkr7<>uq z@2DiSYH!u?<ziJy2hm-Ir7K-Q7xxsB3jOy5m@gU-bz^Zi?ZjfdTj(2<eCqjiDNSh` zVMyaFOx+Ot+e(Kr_k8g(y`xU>Q!eEp_|r<uaSxZjRNiBIb$1F^^I8V|zyZ=?ez;N- zQ!1D0!G3fJ>O9|N)FI`MmoHczg1@D%*I&M={H4RLV-Ylq-nlZQp*qH*U=eEQRHov| z<it}5iQ=axF&_0tCEF2BXFHyW`FSRCqs%HQa!6he6N-Dz6;=YOP{}vPA^HB}a|1U{ zO>#@;=%$+k5yPUW;r3VB6qSEm{ywz~4eIV8c}a-~*We8IX&ZtzuHMLh(jD{9iPsOq zcfWV~o!bHZoAKM<fNH}vMv{<|Xy|k{(y7FoQ#nUU*PjbHCl&T)jb|T?0H}7mIRIAR z=9>`LEe@HOAp|Oazx-oFZ4K&v8xuN40w%zzzYh-y?<^z?TK$cl|L*thTO|}c>3)y~ zRQ}+7zx_>}W~#NifX>M1Is?J!R05aUfp;C8E4V66T2ZbPWI}2syOQQDUSX{>>2LWr z4~2#x2g2UQ2hcd@x_(1?Sr$>sZoCc?m*DGeWP~`s{nL-pk6&^Xg3oaFxvq<bz#!D% zU;Y#Tz^A^<xlD|Vt}pKsu0#3Var)uMgiie;oBDClZx+<@5Q?@iq^OeNxRfXAOA}RQ zzCN$Tu$s-(#*RUB$v#8~B5W_Z?^rtmAU7O~CVNdx=+)O;b2TN{?Crh!dIA7}&~fxi zgmQ?00E;3b{sAGox1pu$U(0oX9pZlgI)Ck*X!|ZgK;9UscJUni9G+4<1XtO|+B6HF zoO8BfF9+p}IMgb>!oLPFOKg~V-xyd<N5H0d<qAw=jg8FBb|Qo@dI<_P$8~thBFu^` z-u{o}=YzHz(eZWH=$P1nNNj8d3rNw}*OR`r?mO`l9D*;a_px^as%2Ahu|(RR2gXH5 zZ>vte)%to9t|V9*$D+V+#d-3l4YB!GEB4?|G4u#S1ax@Pz%4@$Znq3#QN;UiJ<t@x z#AtmT7#OqihFYP_z}91(0&r$qCl`XJ2xA0`1A>k|9W=7{M+{tEZ%|}yJl7$?k=$#x zAPN+pTr9MAt;^8g*%7}`{xzFrAq~~FU%@9p=r6b3ztm#?a#K~SG;e`U+^sm#eZn(O z`?*2unO)J^k&5UmT9@)rL%v@EZK{FPbG&Lt5$65?C?h6e6n@|`omZG>I8|YKJy!Xg z+h{Tq{R>ksx-nU~Rea@W(x;?p8r1lRDXm}T*Gm8Q`4Qxb1Bo}0`SX7{3Bo6JV5mhd zKZ;MNU4`pjIO9RN2hb~S28;-%q;koumo~1o{r4FT&_KNIj5hMZX_sJK2&(5C0{uE% zaHh0N<(ZJ}pRgGq)6a`8lz9gJs5J-%_byS>Ysb5bfqCAWT7`+9ythAGWvciAmAXBt ze$ahrAXryI9G!L@?g!#9-4Q7O5a`qj)@1;g*teo<aPg8w7i=boS-J>;bPU4v3x_&Z zc?N2nzX-N_@+J5$WCaZT9QqevPDtbqK;j7${bKH_m3tB`J^4lbVQz+XXZh{~TAOdr zc;R&U57kTQOx@{vT$0)%ZMVQ>cA}@<#n?|k_5$e;(El<pJ9+fvqzOT*mNWsb{Hu}q z7B|B4ITcW-mQ!KwjV~S>&^A`~X{H1=f?tveVcF-#;mj<6(g>FO6qTlmC12U6sC-V9 z>JD8ub^j_<my%1-0@&Oq1isX8P<pW2q{PO^4D#mRI8OP8Lp>oI<+8tX(65_UXI-0- z<2No?d7M^w0zwE<PU}v8A2A%cdo$y>PJh3pW8lqsr(TB!^<KBWX1ks>5Iv1H5d3dc z*yvQUYDuZ7&;&V(L-1Qn{-XSFkOf4nnlZ|JMgcwkvayxN37KnsVevs*P`)0%3jZZ$ zLT?k&Ke)THrjb9xnug>&RJc8=?bU*)bZD?XTW}pqV^HN6XacdaS-+nz;ztE)*>BZs zE1Kmz5<B^dx?7EmSfNN((ymb~s2_hg_Bv!@>esRT2X7*p$xo=1y|g#DB-1?roG?sK zDbr>56xB7Gh--FJv6}{6(mrKGOcV+gfO6f*=@A1(db0V<VjU;Y?v>pGRsc1oS4I2t zEv@R@aJt{KGO^4|(F+=33yGh(pt0Y|g%p0tC4-ADqjDkTw@eH;b~u!pY+@`v*&A(a zKo~FrbSp-&BufMvCz=_LP4))75CTAU7j;-`?+&0A*SCgxV~85zU}{n&*<Er@17m2( zQqp6_f~l$IxHz}EH{}4=dsD@(NOuEJOXpf$eKu%s7xnqj9;><nQyCS=$ty7bUsULJ z0|+c-lu|Jw{+lTma7?5hdtLeWnEv|zQI-CN|CV77v#YOB>pqu>n!SXRz@K;gU$0am z7!dvN82+i2Qn^1@$^FKo-5rH&Q&1zd#2?~cj8tMdh3WvSRZ=dp$|s-achJg!BmPwg z6!5TNS!mT~{=^ag6Bv$8<~~DBox|AN6D}+dsBDb>C(!}JLS}M7OD_LA4XK~u@^AkO z4E$4vTMVUE?ZxNp0cPnIdRF=~6n}m0rAf|{{G;mc1Av~v#4#8;dKf}BmA^peieH$v zR;RPv7ul<=kvcE#>uzmHIvSa$ekeWbI4k`0tetf%kc#_Jneh!s637L*V-VnT{{@z$ zfJ|U+vboPDq?)eqf}Zm{ScW*pQg@iuZG(uS9IOy6|9ko05H}GDp(Ym~Efi(s6aOGo zRb&umI0QfQ$Qg2cJ~Ur41c#3zYTESRVT$U#x}eGz+!E4jK4_i$CMG2V@fUDPl?PgL z;287+!y(}1AN=`@E-74R85WU_T+rcwOaxDfsyMSNS}&icbu0O-datu4O5AshWAT&Z zRz_#I&iAa!VpZK@7on=?rZe#gWXe3C6$g~-N*ex7<qzu=h*fbwDR0_&!wG7eb-6FQ zv_nF^O+#^jTEbpsd7_aDLq!NyrE9lV__~U$i~ibdnDFWVWp4DVGd896%>J=)i`y<I z-t4jR4@Mnl)SWaw{>8}L7hZg7%zrW+H}J0IF~G5wk)>M#+!gnwu??jSrgVJ~puo`s zb9?{FJfStLGHzuBl~u{w3ZjakA0AR>TS2M9#&j_D*JJglGwMz7kAEpbxZ`kZd~E-W zLx?l;)u_v8fP`j_p~g{X?oBTmEc9jOfC3~&)x85-mX5T<FvOO>E8se2Y}3$2!zr(Q zabU^a_2qwtuZVHATVxTuS$Of6?^>}q@8Wsh);JrCt43B`x39G_^-uVw^@;3jgQ^5q zm95Xh6g{cv3zbzxH###vIsDQn0t7-}>~bUT^(@@dV7WU=U7@_e9T!;ir5FBm#(``1 ztjMn!U!M+TvVl$hsJUds%@v%s>6OJ`HsiJR5NtZTm$y!jPiJMRVh;w3`o)S?X2WE! zEVe-xrO@xV!#ese2a|BXh;wI2cOqL1F$$q#Q)BlMhdUO;F7NN)R{8p6`<mPPEkYXC zG&3F<-4-C21bTY|gg7I)boBR2_cyoqI+QxY`6k99;;jOdV5h$;Oh{@b9jmW_Ho^A1 zUev%vTP90s6Ia@-!d<o3Ix3QEu%cEfl(g{wkqUrE-RYlRcy(AuuETh1sB3In!V;OV zdc0O1agZLq`pA!`-NgT1=yjWS3|tmRmH>IYzp<$p;sk>2*+l<%ilX>s10BpQU;Mua zy7DF;QQl;?njKuVDDT3HXz{o@DXm|9ulkC0&Z6`c74_l_USwN#eqYwm8EekpFjap^ z^obuGJ+)7icBnql9aMb+Q@>9%>|B+LcaFDG$h0C%c%V5`tBwTPvtpm&1VWj}cz*@_ z1lqNH$$=u^Of;k$R<^n5*QlzhWg}juN6<ON52kwRYFc%b&c0K+N+F`UN^`P%slzP; z$1d*^U1jc%OZPFaBSxo|A!KvBY>If5`a&F*?Q0+erE^nGcaYNLj5@jWoF#EG)s?b! zd!Z`bINc=bd%-q`;+PJybdN`=Y6W$5+NQll)aj)oX%o)A5261@RqFS$V-XIjmj%03 zFWWXY-^-5vXxg2-<^M``s_xf!c^nLjI}`oMX4U<|ZME*#-^tv$pZ=e%NDYf<db`TN z-lL5uX7u6(!~JdC&%j<9gORh`e&wAqNjTfhR;SDf(9>tN!K%EXZW5qa>lH1!wP9_s z&|=Z(P+tJ>i``chIz@}K-9&{>Q3L+%Y&TBLHP>sRJgVdHC=O@^ZI8?_-5ok5>sVdV zu^LgWnsQ9}QWbtxq?R_J=1KV-W~5Y?r|s->kiFYMmkf?l&xY>emW`J4`OfWcR~avc z^9@|LP*=ysEu2^f&zl~7`#U)Kfy_o$P1I#0^(taZ<RaTI6LmrPhs;|vNo^ey*4qn~ z=L{VLi!*P$v%%x2G+5!LY+4C~)}zv@@=>qo^3;0XS>+|eFB+7y&Q<ne0D<Q#`Pteo zQt8<wATo7XpG?kX!JmKiVh~a9iTknkEA)P?S**TzwOk-Iin+>Uq7s=@IgIkHdBrA& zac6kcBi!joixaIjFUj}lT~TbY9BPb}%8v7FSCeg1-sb3I>N)>`?m>@eqvAQmx;N{I zRjbN=(Rdx#H0b9~Ve0w4FwEyUl%uxpa5%IRbW^7s;tiY$K+<`|Nt${Y*t$!gA<3By zB%4PMxs)F~I62teWmB^;6L15J3j8AFryT3fy9=|sb_f|~XFb<rgpgRo=Ow%co`yYI zhmf@?s8$9CV)9=+;wLWjque3I;}U&nV9?(Zmg78v_9Ze&#|0Y<`=ZN|BZkp9vUv+& z6E${^uFidEqIAzwtwrP_Ob{mI=syc-5ZZ`lfg%JTG=ewZ4^oQO6Mux~&<E9uV24_t ztdaiZP5?1^tTIDuF)@{ZY(8y{dN;sO^^HURzJd9qJ96_4d?=U<kd@1gc#xSCrIrRs zByEIt^|b{IthYF``ySO>l3pS)vC^;%rwQlB9U2W2iuB;_!F<3VPJ@jmQO;;VVuPLS z(4Njnc7d0RfJH1G$n|CdG0PrU*xWyU8F-!*h!nd6UZQ0e0jyI>MUnn=IGhZ1^{1fY zapL&B^aqu9W=zSQtsVc(J2RxU9nKWwUqu-4wTvpu>;F|d3^OnMiVS3<Q27^*gF=K* zK!_V01stdRYyYp~S><26;9D`~ZguSk9M|dXp#*WGv&+*x`F)pV?5Wo#sorin-uah# z=x49a3%~N>tn_)|w_N=B$`@8h>9Y$S!}n+})ov2{+;r`x3{)9?<1k!(8-<#j%jm4k zbF0jzi={hNS=3bySv5s!-F_7pYAGh_$&WRoDjp%*Gg1y&RNUtrW)S_&Z@>#-^LdY% z#NnvxP-p+v0W>nkiQI28&;qT!vj(Nc%dU(hd*AaeoAST=0EY;Tpt}Vx@>wvWMq|KY z?ovK3sgWy}r;_LF7Y7#}?9k&TS~T-Ffbilz1EYBSy*A@SpI!<015oVWqKijFdc%qK zIC|)voH#VL;tl6TA_apo!ug^SESk9$Y=dzEak?Nds*DtC8f)B=aVY*wh*Zq+%q$S9 zn-*Ntuq-WnE0n>FNko=Sq58K5?C|#n?Oyz1-x|Q;)n%*z)L!E^B1ipPg-$&Vf6dzi z@H$xi@$(6H2>R+5d;1x)-L^IaB)Qr0h&er5)FZ}U`6_{rKxSv3;}UfK-YZ|CivqB3 zKR6xKBh1XT?urb>PkXCC4X15tPt4vOYZJ0%4#b2H|Ds+up`3kQ{i?ppMyG%a54B0G zTdQxKIEcjiAbQW{wG-|uZ_!bw^}!DUu{ap-dk;a)xg}I!_VT^w5eF^xUp(>7t}aAg zde(2Am|A)uwRsC+gxed}uiW}3Sr9Fw#X%v&7wg|VQR%gpXx9mBuDon}Tw+t{QY!SQ z8dE(r9ctKsXccwvv}Ip(=S7mn9hatH95%|HWyGvIZj2K`zWz1T-YO~fUX}^YxiZiz zmzPqFP+ESqz>T|CT?579+4o!YCQ^Rye<HiVv>NQJ>{E>sEwFklKx{@1op;5hI&mnR z7wc<4{Q^yz$vix}Io-UFQO4~BW0$|=tUD}h!EXUB7t!cNSLLRgnr5;-#*Yb%7is&n z%jInLoK>>@waR&bLP&nbPhohEL)}X>)uB2Xu4Tnk{8v>e()-{MpKzJuE~p&SScF2w zt?DvIkHClR@>eo>bj)$eR}~w7{3F}`&97o1M-P%9H7H6qJI?y%X1aQf<Gdr2$BC5} zONU)Jr^haFJi-l!+EzTqxHEM$wV)Bb=Joc~2a}~7tdeeg{L<yd(M$fG2G-9woOt^e z_?y%E+oB;>Fk%h@=@hPBO^M?=>(^o~Qwd*O5Y9U^=Yit@q*Bs>aelw>_!J^803c-U zm?-Woj&C>NKmVw9`F$836C>t@!t>b>^LP1WMoMbF{(=dU{mDyAf>r;<f8R-0pLOV0 z+{q!4D4~M+NY!0O>xaAd?$wv&_vcQT#%#GZjB#MyqL#_+yRwM@*WJd3Lcwtkc8wl9 zXgU9*Wy#y#YTF<DU?y+r4r)m4-X)yi;AHUr2RPx(+q8;$(bD;qyvhhk_`tkqy|=<! zw5K7!2a6F~C%pyf(ogQFxb6zv!9_O5fZ98dyq3Hm353?SxAV0tM0-1YXn|Xu&BRsU zqSkC=W2xm_6&CG9wnqVte<oB?8%%SwkcFr)74@mO7cL37P6Lz8C(T`s0)ugxy1etH z<Ds)}j6ZX%<2Sy1NwL3S;@bPre+J-Vj=l8;0lNd=a2;TIi=>)I8OH&AVIUJk+#A>y zT@l%^uzT>V+T)Bzbqi3(-7PkUeSz|B0PD#5U^eHCs&@Aw<*8jN+TFQ}RJ*G+jNy|H z#qc?ACS#}06;+Y?Jh!=8hpmU*PvKnyeUm+7wVNxKbK*Q;6|GB-a$;R|RU~NZEZ`9V z7S6RSyC7DZI7-wnn;D8pnD@LYmF7Vmeex**^pQx%&wgw<<)%o3%N>`P0_U{-c@prO z4rgBSP*NH)%Ygo3#9>C>OxQB6uw`+pp95Q=2JX7CjSAlV(}9i%0!L@n`DFiu?Y7Ea zfvdQbNFaBOwn=7`<N`UWv{U-YVWl3yTGsOy4nR}}A<j;kXTzgT`OcnBj)`FdWHV|* z?Bmfbv0_)IKIl;2Th}k_-`d+%`3=rwwrmzN?CqUmhJ6k5N`nLi)P5*R8CU2S-ucUm z!lRlGHXM;1bn%`<OqDiidqH;1gF|4cw;=$0Na(#?=)Lr0z1u2lP1*XFC5waF0NbzZ z`Dsn(^k9Uq{6kK;kZYwF2yi2nj)zU+tn44$yv=bwpUo4jQwKFQv?L?-v4vXjK_$kY z>dh|u?su$fsW&`~5wbY#>fn@9Z)PnPx(*IuYBL19CkOR01*aBqP?_*$1CZ87{10^r zj}&WMSRYD|Tp#Wq9*ho(|IYLOclPCwEGJ5daso@FA-{~g_CEo`K&p-a00031009R8 zrvL^3000000st@oKmd0D005Q?mjD0&000007629i7629i?*euMUj#V?d<KFCmk6Z^ zJ_+aw{tL(q;SHz`fe&U7tr6Z6SQI1_`xU+x%NOey%NdFqvKq1*>m8#Wt{=)E3Lvx~ z^dX)h;UZoliz6)~u_M+bB_wzx%Ox@;eI>~zCnk#~+9xU}YbUKJ11Mc6=qYX~$SMjd zcq-*9UMt!x1S~ErSS*4p;4MBan=SJ$Q7)z~@Gn6x#V~y_1TkbW$ucG~j56{wMKi=S zV>Goi$~6NuCpC^WzBU9lRyJ%lsW#C!8#hfifj7rE4mgH5(m7{2-#T|X+B+dTY&*3) z06a!KnLPA8NIjK46h4(dFF&9_6hNFoAVHQw1wvOsv_lp{fJ6mEj70rKbw&q9mPZgr zen+B5#Yjg<5J`4Pw@K+rKT35<pi0q86H9PQ-%LSFwoL0yBTZ;cu}%X{FHUeywNBJe zVNaY-+)y4+VNkbF{!vR&!csa?q*Dx2XH&6L{Zxom5mkIu<yJ>lsaHH#!B|pQ<ynVX zDO!+Q0$Yw;7hF$VhFzvz(p~yqBVI~gZ(f*QxL)920$(&=R$p{qp<l*e3t(bksbMBz zTVb$b2VziS++%8E%w#KN6J`BocV_ZuLT8d_C}_WFfNDBxBy0(6Y;4hOTy5oU8E$WG zvTrYMmT&%Ww{c5x*>a0>FLT3mYjp^9gmwaU7IrFjH~;|v000310GKy_X<rXK^#BV4 z=l}o!0NApa`2YX_0NApa`S#WRF$pRI>i_@%2mk^A000000C?JclQobOK^TQ!_u%gC z?hf5Uw1l{OhGpbMM6~2Zx)C`}x_K2LmTrXmx!>G-dp%oyt5sk9ZaXs=!~yVUP6O0o z-($WlVJ78PGLQN!dmFQo8Q4nZm?9RLZcL%RM6u*0<}+^*?G4PECt0oAg;k8Nmrjs; z$K;@^=W531L-YP(X7UE>Xz#GAuw(k_taJKmSfKY4`NDpV`J2r`%u<~7yO>K~_S2NJ zpNt)`4;k-9fgI%=PmToXCS8RmXM??Q@_nSznJXOi`D730cwwHPOt$AzJ!%}o#aKqH z5Asxye)16%$R*9uzPiIa!#&!UsHfy6@w~_7pn{ubN%q?mncJQH=55Q!Qq>YK88{N; zLE<Ckk~ww0i-8M*^G`_D;RCyr8{tKim-o38*#(kDxEOnzyr=#^diJ);dqPwn!{_Ae zE~KgvW=XBj<A4iz+mDN4d>85^xIk^+$%o;ItDh6y<LU_hy7(hZ)4LMGdm!>0-grr< z{{&t8yu^OKGndFd*}N^@seX1U+U%UxaVd_YqW35Joqsn`U3<baHM;<TJHQxG`>kjI z0C?JCU}Rume*MpoA&OJ>+tvSjIkkWyD1eb20HHJnVtCqPU}E~hIDvtcfq|)uX%7Pf zLl1;ze86DH$i#pI8W<Sf3oyKS3*!f*xu!5UFetqL&#ds@f+33eHRE{&28RAPW{@n? zY9OJ&zz6_UA`%$@0C?JD&r_HiKokYw$p*FU+>@Q;>(sVw8!xqO+qP}nw$0hOsck!| z+spI%3n8pOHo35fU~DL?p_E+MtR(~gwCF1wqP%d5+M<P+D;9~>;)TT%b~r4;YPA~c z3uiWpMIOpbIjInhrSUX_X470+K&xpTZJ`~shYr#i(q!lc-GT*n#6VocM?$248`+Q# zg;5+OQ5qFc1vO9?&Cn7Z&;|W40%I`)^ROIiunAkS6NhjF7ol+zPhGC?qcXaTE#u3? zvb~%qH^|L$m)t9lyX~HM9%t&us=n&2MyknbyE?2+-iUG|&W&WxeLkNM0Z*a-JYj!% zvj6Z*4|(R(3R+8>X*=zv{dAbF|MJ-W@Hmkbc~R(xr#vd5dce~-;AxG{=!u~igXx%s zrC5pe*oy5T&oNvE<DLtDc-qOia(%$F^B+$yHAqcTo75q7;)df!^cxAEe+__c`wsXv z`1<&I_&WG9fBW*a{?}4p!@rdIlFR$no7`*X&-{U3@O+-f(|IaS;mJIPNAqwV%7b|z z58!Uxl{<4MuEEu~JeTHDT#^fOLC(VISh0uQEE!Ckij#3%j?J++21j8l3-jK*GcU|@ z^VB>xkIXG|)7&s#W6U+PD8iz5>m7Ql-lXg4jM}MF>Lfam_K)w@m7B+J?!NZ%TK%=e zYth$&ubE!czNW75PT`Hh3qf{Z>KkT&kqwApY$hPVgsuRT1qlEaRyKAHPA+a9UOs*S zK_OugQ894|NhxU=S#*_hl(<z9iLHPxnFk`|kwtFcH%&zq#sUBpQ4+EM0C?K0RppxN zMhqQ?mf<qEAm6#!ZsV~pGuv^}%ZuM<7wx6r<@Oo;G;+%S`e;3JvP`9gXhx%vj*g@W zD?_Z7<0v6|`Ma8&f60x-k7K@R^K6oCP`(^<rJ4OPtcKO<s$Q`jhgHHV)a{<KRG0=H zTZSp!@VH`Vjrh+wuFZVho3%#6Xf;~+BzCl8=P~izT<q}E#3sHgtGh`;Mc*h}<5{Q% zJL0QyeifE*785|^8S&^`oI*o#HY%&zvbvqxX_6#1TW*qQR&()sl6YJ-1V6QA20-<2 zF6O!pxS<2UPuNO5t{Dwnv{uwt0+N${aQVb}w93_%1ARz2<?vlxtv8X@e4NhN*+LTQ z1gB3IV;tF%)F86Qb;CoU+f$W3H4SWaph2+?GOny_u(b;RTyJ?iWJo}cg{!~U)C!3Q zewrp?lD12b!=^Vhrot#_Ia_3pnBC-$_PtoHh8`KG6y-XjwE<Jg7B-n|fZRmM)uztc z{Sae^T<6QUrEu$@OXH?Ys5drRjbg{vPSSEbo-jqF67gEr_IT33kch{_7jhj79V9#< zy9Mkfu=98dZl+602n?(uE>DIj<tgzbNb`8wyj+Tl+FCog%oFSSM~_dKm-F$<#lF&Z zpg&dWPntqah0Ad<HHG?R0Z+Noq-fos7?+O;e6V#^6Y`jg3n35L26;|p*p{PlZF6n+ z&v6C+r6xfpUEuFRySr3}6tGb0lm=TNtH(XdvdUVWGOAFOXes6?9Z<w$I^a<RP6J92 z{+H=#OHHaE$kXCv!{zT>`x0n6joePT9-lFVCGsqI7J1GTRgvdSQ4@K=6m^joP0<i( zn_@`hJEj;G`K~EOM1E+<XX`VMTjk;_kKJ<dwa4!n#S`wo!1z8ez6aj~j3OOi6nP04 zMQUIac^Mc*Hi1#(3^0oP02oEi0;5P{=waDvo<Z2@ltO_JN;x3eNWb}pJ(oR9(iKD0 zr9&#Av%6ZJu_G<eN*-U`Dvx!Rue6G~bta0hCS`)xN@fl{zHaDNiT4KVtZ07_Oc;V6 ztXF4$R`Sz+tnU`rtux5zCh`a1gYmKov%4POGXA-T9^XFB6iv7a^E)VsI@=_lx-z_= z@_C+jbr;hfuONT6FwwUy>&$7yziWUB1A@QOghxVmJ@+-y!yLZuZ6A~0_rWzCh!t@v z^Zs-`{;5J$vVW?~R4*ohyn(|Z2CTH!9ZVmpxd$zQjs9a_3DY&r)i7I|i?PXUtW$Y| z_TI@fA@q#ypoSU|I>-y6jFQqpL9|5CI7uP7j)to5^9qlWQL_Yr&$<39w;c;5zb_mR zH1(MQ2l^qXc_3=!sso&LbXS5&wH}JTOklvMT8e#os2v-cO(mRdQ{HG|_k8EKZ@*9n zb?~4vTH5&0071A}npD1?_old6%Ev~NFRXR&Fh5NE!naOi0H18B=XR=}?zTSA=9%HU z?txAN!}r`Afu&i1cE1hE;<FFaaI3?>?p6zGw7ReF4&1fzXy6V17p>U+zq(K0XLkWh z;|bYOZzmaGjD(EM{5z7rqXz`&ySr2FBIPsv;p{1y!&5%r#4LVvi2k`Lly&+P-@^KZ z;X4p7g|!I0QYQzBLD*MD+L2#k`P%gG&S3Ed3riMnP0uPdfAv_`W)1^D6oikVkBbBi z9CIu)vt?#3OVFTqR*=o}_a_!R68cL9^CywT5IGEy#}Ea~mqZamlrTgYLsT$75>*UQ z!w_{0(ZKvmG%-XAL$onO2lFk_#SlFV(Z>+3<vpi|dBE`ArG{D`QX{>dWPHjPTi#2m zCk)k7O7)CVI=8&nlr9)bmz2^KrF3n1Zz<g{ly0@4J1ywm3LkH-l8>iv(|d5e>nG=w zyi@EmD{)O@0C?JC@ZQ02A}C@bBV%9W2F9Hn3>*x}1sfUIoHj8qGH8Jqo4HvTIUp<{ zW-yE0X%{mCP?{6S=5X4@-~eGWGq`M2VeANu*x=9+v5`5&WuqF1w~M2Jfsw_bBRM1j zq$CmuHuJJF@@VZ~{NK8PrFSC}P#<fG3jk?DB&PrX0C?JCzywM_sLG(qwEF*lrqxWM z3=B;B{=bE=0AX1NZg|>E%%KeeF&Ibj_kC@ToM>}JAkKkBkuU)poW;Q?_)`o3!7~hV zAXtGw{-W-eCmyh}f&l;%$VSg3I}46-V9iO6R4#HNbCtWMXWMa?3!}Z{q50SUKVY#3 z9`=@vrO&cc{3Ztu{*)sd|Hz3kRJm(<_H)?Eg?*akq50R}|JxdMEjJFsz<t(N;O&}B z+q$0Kt4n(FmSJSXVxl%8-TwX%rnl?Ewgc`iKybMzCza_Zj^4)aC~7pm*P=$VK`U%c zKd1fJa2|6=n|Z8)r{w0REAltmPIOL-vF>p;+13Yqa=O^w-{bxu)zw;&yS1X~GexRF zQ*?J<yCFOa9C2k$kyAr8)TdYK3tudd-p-6hBb8~FT<EcqtaI;vtLPiyrG)f#<XRjC zBjXa<W6RZ$dvx^}>gbH`pP_=_ATjp(YQH#lBV92`AT=@PRz!om#}&DRX@9dt-IDWb za_gE9((Qwrs;R{6UQ2qYO{&jQCSQl9wZ&c1ZYoHeE2rvp8&{@uR#}r?6<qJGwO+r$ zG76NKpn^dk2^<J-5#ybl5gIbSqvlal7GVVp>$y1y`-laz;24ZhBAXs71iy4{;?te; zH;HWvM|;;r#DaQYWTM{|d?5G)r*hMloO@t#93X`j_cauva{jC&&fZ`7YNup9*Vi69 z{08F)EW3sAxk$W~U!VT4f8p<U(EH3_<cC5A4JZB=a*m;H-a3bO|FP4LGhW`X^h3_0 z%pn3{8ebRrj#=NQAz1iy_cMeJw5a*T-%#QbNGuU2`$)Af=v>gq|ADA)7^$4G@0q>H zG!WeLxye_<UcHHyoL!Wj`_Q@d$RPwh`{5>(U%s*z^Aa?MvW&zv&XD@peLu9%xQJbk z9i|XS+{q{f*JW75{?AG{!Ah2R-F3K*uIO8Tqxk_@>rkHn0C?JL!P7wn002PIdmpoH zJllTRTB^SWXto+5ZxFvbhX8-RZmIAOqksem5+hE6Bq`El$dV&Zfg&Z!RH#y;PJ<>b z+H~mBqtA^2?z!XHAP+nmGHk@C0}eXmup^E-=C~71I_0!8&N}D33og3kvMa8-=DHhh zy5+Vp<0ed+GHu4JIrA1QTC!}#sx|92Y}&HThaJ22?7QQxd+vMSp+_Ej;;Cnzd%-tB z@PMrV0002&?R?+1ZQIPYUDs?I7%^(hxCxV{Oq(%l&b$SSmMmMbYR$S0o3?D*v1`x1 z1BZ?rJ8|mFxeJ%BT)T1W&b<eZo;-W;>dm_kpT2zi@$1jOfS{1Dh^Uyjgrt<TjI5lz zf})bLimIBrhNhObj;@~mkRW(4KmZ5;0NCw6NH#y)wy{ZfqGI9_l2Xz#vU2hYijknC ztfHzWsB36yY3u0f=^Gdt8Jn1znOj&|S=-p!**iEoIlH*JxqEnedHeYK{ac(19LaSc z0K*S=@GyqNAU0~nuqSBlmYS5|(+{NnfWJb?H!tUdQc3-{UoNIOvC7;%ACBwI>T+62 zrB<=4AC0o(tX5q#UbF@sdJN*A?-tW^c{^^l&-ZgQ9d`R?X~u*Zo0t?Dw3xAAMfS2n zjSf8~%-FDt#h}Fj9eNBHF=57n6&rT3Cd^pHrqIk9rE$33oUYFwPn%~7Rn!Bz$Ln;t zUZ&^6<-9olzc}xX8)+Gh1}zRaqPu(EZ>G)mGO=mLo2SF+a9m8}RU9-LbeOPV7e|c- zEjsjAMCZ_Bz=#DKcF`-;XwafVj|D3>>|#);F<``m8QF^p6K1T~u!~8d#()tM7OdE? zi`k&X0Y?m&Fk`t{U!G5k=e^{`W5k3R3s!8{#i~%FL5l-A^cXN=!i){O*c57X=rLf# zgc%FgS!)d1*`b8nKV50{{rm)$Kp6rxd@FYty+(7lJsgjl_44xm>$fq=+neX<u-Ls^ zWguI0QeU6Rt60>mQ{$}HRy1lha``ki(N`o7BRcdLFk-@tMT{dlF^}lbW59?>EF(J1 sSbl6uIrJDXV!?_HyQmdvbm%c)#Dsaa_lgbs7ut1b;s5~v0ssF14_VS-dH?_b diff --git a/docs/katex/fonts/KaTeX_AMS-Regular.woff2 b/docs/katex/fonts/KaTeX_AMS-Regular.woff2 deleted file mode 100644 index b982d6eaf85fcd6eaa94a0302bdf1db9c08e8231..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32944 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 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf b/docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf deleted file mode 100644 index f84148db5806b752524c18c6173fa19e8675c976..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19316 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 BW7Plv diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff b/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff deleted file mode 100644 index ab56ab7fa707dda6bf71209cf0275ef6be2bab03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11696 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 H0YLpfq!Ad2 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 b/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 deleted file mode 100644 index 710c26179c5f1c9998065035a82a578cf45d60e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10448 zcmV;>C@<G{Pew8T0RR9104UG^4gdfE085wv04R3=0RR9100000000000000000000 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 G0002%wf^z| diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Regular.ttf b/docs/katex/fonts/KaTeX_Caligraphic-Regular.ttf deleted file mode 100644 index 97814db7e2c7bb3039692551a4fbbb7f33baa46a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18684 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 OTGv@~T#9_E>wg3J&>da? diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff b/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff deleted file mode 100644 index aec8a33389cb27a7e2e603ece720eb000fb9a0a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11460 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 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 b/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 deleted file mode 100644 index ee5193d7c888fe2e82fb54342f2bb5d4c34b83a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10240 zcmV+bDF4@YPew8T0RR9104M+e4gdfE07)1C04Jva0RR9100000000000000000000 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 G0000_1BZzK diff --git a/docs/katex/fonts/KaTeX_Fraktur-Bold.ttf b/docs/katex/fonts/KaTeX_Fraktur-Bold.ttf deleted file mode 100644 index 483a7cdd4eb2e0aedd07727ead59f50818497a0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35660 zcmcG%378yNbtW43#=b{nMrP!`?^#((E|s-cR;f0XN-A}0EiJ8<)aq6*QVX@ZX`0@F zHekSjG1!1HUcoj?Gi)=qKQA-b&ttzg#`bub&wdy%Hul&a+waZHW1gYw{Wr2oO=GZ! zc{8MFRb@s*M%=Ug=RfBjafIVIi+h&ixx2TIjkVW)?&81UxLcpYyLVr{cJao)e)SJ& z9QSC6<H&n2KXxle57{=yJ^vMaK6CGl`>y?=_eX!nagY5n$La66@8Tmj@V>)w-@Agp zmHQrg&AsLWA3KMi|C-}orCq&p@t(-qtuJ%j%`f7;$*Xunp~5=8?}PZgboJV;$G`Gv z>o+*=kr=-3jSoG1`Qj6Qc0s`L_?`Q;YZo8CK|W->57+t{e$QRMc<stF4<=vBaj(m9 zob;6&4?l9N{f?C<IquCkhb-K<dF94C-|}@_@A=a>{>>ca+W7m|X#`IzbIPYBfpB!Z z*>O8=wc{4s@4lzqrjc*_RhxVQ$3D3GpSi!MpWs4VntSLoj!X$hHV?h1A3Z(5BXa&B zju3i|qtv5Y$wM!id+$fS<^B4%J$_(v_-;b)W>@02c6N3?n@aeR0xw0H;qGLo?MEY? zY*9HP%U#N|ccPwDEL2_-%N12hh#w`YR<!DgWUZD=Rq3@#CL#J+Rs6UAS`-M8$oszW z`>}ksT`X~U7LB~feU*Nev$-sH<1;*^JY~<K;@+Yh&(pJbjNpN|vse?(C+vyXlivO` zc&wK{effh|p0=|yunCt;gk4$UY+{R&*9^D)h$PF!YNglhPj)8#Zt!@LO$)sW-i$c5 zW}1fU$dYz%&DUh28-^h(F8!=C{hAeW{CYaBh&1WTay)e3B|;`<UkeG2?EW3UPX8r$ zj(d=Mn)|DPzeFT?n{eWqL5X^kC?rpKMcjMLJ|{{XCtl@LEWAo?aFT|<CGIjO%feYs z5b%JC!k@)zc!31jlDL6ni&%$$_H-=6w>^E({*GU)a6DD`%R@!Duzw%E3~d-BMed1L z-?)D9!kIgdY)$uTm0ZTPxpU$<9ScgTg-WpyVHzSuqJFf~?oamn-DvQJ3@xcvWQqOF zRu@a&pX?2nzRk|SOZIV=?@#u+)k?J>7kWLv9bBc;k;+|MTZ(u%fSpJ@_CX)V;Z<ZO zOZ-wUw<0>?wxE>rN#D(91;fzI$n?(k8dXMmy@@k#2#MqIjbwIyaN%joZ6|vrZ*8(w z=En=E-n2wS`*`Ha8$wZ$f9Ya9UaG__KB+Cd^o`fv8J&+7PtR3MLM18ZnB)SXq$Xd1 zRw|CAdmAd@d7`9C#S<MWc&2r7C6wG4kJlZt-Z?)qaXud}6y0Pvea}KCN5_|1g(TtQ zF<g!G|1QxSueK*rye*gSc<DpiPlTo7nNxj_=Y>!@35UTQC7<Fx5LihVCQb-<mRTp+ z@(Wao4$U&++0-i*v2I7>p&|)6s%9AU-Huz*q=zmM>Fx({ofmf3$XWU&F3(Ay3=uNk zB>i>-CV*G4u2DbYF(FkeY*m?rCdsx=s?h~c?OK^^oTplEB9${De8oRZzrd@i7hC(8 zL+AhIS~ihVWSKX|(#w-~z0v*`53ihLcYgowDp{j{!WHn1VfKyTZYRoKOwjv6@Aa7~ zK&DLUmFoR)KRhX=j@4tyaHW1iC;YTu58L6$XnKVHo<@k`_1}5#3qO4I&Q{x|ib5k^ z`Pzjac+1&`r;8!>tu^v1Btk#MO>@;jsXsY3(kSNAt_cG+nXhCkxPuk6$rij<gfdr9 z#k1lb@b#1ZKGQ+mOs^A>Bp*uF_I<WS?4EsJlCe5iu3~Sp2OaI10&j`au4Nw7WW`J+ zJU#;xrw2k*R-HAQ$~>QJMr2D}la-7>ghS+SBNX<PF%_k%NwOAoEveYidF2!tHC0Wi z=tLwm6g4DEnh^BqvRs-mCfRdM?S713r6;)<H!`S0dAJQzrv!0|C)^2m9!hw684pHw za4<RSRf?V<#fDPO!>tuvnb}ma(8ELKUGj@@Laqsd^0AM{;^ecR6-0%cr`z%eKA>u0 z^3uQ4)F1ybIP)+W#pNE`UE;n0kCx+{ftk=Lhp*-CFA&6q{xB}E<%xI_(<fMXu%1k! zm0snsTv!*ia8{y3oM;QgAXZMKDs4;ip_CxT>lwr3by}L42@z|?m2BVS=ZJxCo#g(S zUm{^n;W7he3WPfqJRbrL9uPk&oD#$)$S$8_-h5Iv$dlwrUX=gZOt8DhRqosUd+u+z zdvS2<(2IF^O`TojEH39Fps=X9e__1Gk?oyNg+t7;@L(*u6)N{tW-5)z%1EqG7%vuZ zOnrBgR_JeY24@cpnQ$9yF=}ivNT>s!<@*FHJxQFe@;q;dk(YY$_*G(&e*?E6eEoSs zev?JN#_oTn5%OQSI5*05m_=?JdXa@?o!t+!agPgb50Q_Y+Ix?1+dH4GltwB0E@)=3 z=uA6b;a7cVrUawLr=9kmX;nzsBaNlzYOHdyUeD#X9yACw56;hNLL^@wFXp?gwxRRJ z{p4>HBU)cQ-X1?Pm9+H>8?v9#&u`9^c{Ox=Vss}S&lNM}kUf2GAR&&H$X{|TZhX)h zAw07${D<&YS-_43A?wT#jPP7K7V#X5Yw@jN{GIHynYs*}Pp5;(;pcnZZm%CWeRkiy zE^`SL#OMq1SKCAqB`V3n(W8XC6e6#4Vv595H550dMxYYpONvlf+j#qf0-OY;;jl01 z0#9YpD!lG^vRu|gS!OVia3i>*4*4tKt|J4zq*Ka`!geTPatrIvP##bfy@BVz69AQ* zW8s&FFd|GrZZhHB{HFJJK67PZrV!W~i&9v600_7gpb9t`7TbWB;6FO;5@3cWNivIp z#U49?bXt}2Az2dzRegM2wr1C@Xr13UL`7W`5+oCXCpXCltDi}noI9B~OSa1s1zrH8 zFr`(6bh?RDqt35wP!ZsQiezL$A|%>5n1KEeF1x!(b@EkimAh}C7htudPXv)YX9mxi zksu;b;6%iF5@0Pntsn$oBgCFI`SJk*#|!+0+sBcuok5szE6cOf6JuUTk+@Z|D#MfZ z+kqcviy*^M;sN^%RSmtpBr|k?W$>fHJAu*$SO7kkp@l%)#bWQB$8Y7LW1i6%6>K3- z*N<8S<#5(4xa$cZL|&0sFF49uF2%FQ4%IYWUpn}}Sh7@~zsvaF7Z#i8D6gh?lTVPg zLX1f-Jy8j_WJ(3B+RRuiasD-yQ1*DDX<~M*GS`{4BNYhL+?}I6^0!=t>vI!>(GvVC z+0+q>HZoD>WdTQbm?9ry&I2Bncx0<PQ7nW<1o#TrBAzMY@$i7M*9JT;!u%po1!kDg zg<dZYdCR?Sury5TBb^=s>;o4e5h4?ERZ~w0ijuKr2vwoqzwoh-`vT#K+k18PPodm@ z;z$xn_g?z1Zb^lHXERp#0gLA~F``x@8~yYbzxeMD#&{ZD_=%<<OgYz>{KLD;fEZum zW|;h=gyY+kiX4-Fg2|ubAz+aQ*rYV@hmgN02Cy?EF!{fHfXE4=aN+iGbSVGHZn>CB zSO(-jLuLZ`7ns;xX4nWgkzqiHbOKWd;zO_;qe8J-0Kf>u&ZPbq{k|9ye)o5%X@BTL z1dlCQ{kf;+$G`U>qk82ID)IueZ$)ty=XM@i`{f!z+OtUM$OxeVuL-5q2G57)JBJ>4 z!ln(_K0BJ~rjon1vx+!DxpDZ~BK>7<0*ZQaV5>0js0X82hEL)5uy_&2!NG`^;n9bA z)II{2q#*)_Q4I5s?{Fy4_A3r$$=53m4bqk1s^5BO>RS&D5}0p$D2owA+@rvp(D06- zj^FlB=FW28a;OBU2BthbgN4A#-XQ`UpFp4vdsmfsUOKh+ND{w|bP)d=L<l!nnCy;? zR!ZrlZE_Q2LSgWq`8uZVa^SL=2ME-B=m+;az#jN#exW_dR|0Rj=l_R+iu{?}*N7+^ zni+A@$>@A%+aTu3s+L`DZ7z=7nxDR$%ADAK{LNG=>Bklu1C8hlrRL}|MEc7z<(DCO zQ5HqXY9D^Ltcyxjr<vY~gNq{*OZ~;87e8^}UX{GWPpzf_L6o;mW#;QrC<<Gd+?}P{ z<WIN+cl<N35oZ0vOw?h&uyE|KXch``&|&tx_kIxN*!v8fQ}W3E@iAD*&Zqn9Kqx+g z>HR$X0bX(uE}_c;M0m1Vlq=IIO1>tK9D3hVmLa!CD6PKrw|V-DpH<1@^nw&qyZ8OU ze@POmX}w<{Wrat=$aAgTf1nxq8;E4fz<!9mTQhkB!4BaU(t}>`^d`)RC+CKqYURLz z6Dud$V{tz(GU&h}(@-SCtZNUA!&MDaGeF@W^0F+zM_R|cRv;%IqC;4&R`{W(uT)>g z8l<6+{ynjrojYY(v(apesN*NT??!hlnS02gu-Qv7X{2`r>PMB(ST)nNioPku*83Zx zad5^Xxmsqtn4-T<v#~-gon7uJO2LV3Rt#xw{Mhu!YJFsUTqdyE)fLBWj&?L+WGCXa zTty2t(>cX;=D+bHi*3uT&E+&FI?){83Rzf<^zJ+@BMLUSdG6Z4nj``buq7x6<;?V% z<>f`he}VD>V!pruy4-*egJ_66o&(nc;b#8vG2n9q?rwG{bBNAJv)gXYkIdDp@n`|~ zt67FU6tRT8PLN(qPWJly5#jdg4$UK49wxU4gKU9=REU=U)4z)l<?OZksDVIGC>&Pg zu&<1bj@m-Oi<fVh>eFw3=?lI7LUgR!EXicCk{AE@SAQ3Lg{Udx-RT38BZ`Iz^`d&M zy6DE1Ceu#5BfsO$!}DtkQ8!hWlCSFLSd^#SG<c<7CI6PwLHwK_;$)zmArOV;0|f;n zMBoCM`iEH@gq6Tsy!;WvPW$g7V4m9hj56QxnR2NVfTVy(V!i^-Ahb^^;g{(`B0Jj3 zqzYojpY)W`T#UReUdSh6+1yL-A1@blH<4m@I<>pZ{Vw?p&fz-T#wYWHt{-}lks&T) zt{w^Q85t4hFpKnq_wXx&`0QO4=NQHxAQy~;5x5-U2rLC0BDH-3=uC#&lPpgiW~q`~ zWmsk?m!XODx|5HmMkZvZ<?wlvY7Bpc5>+G8mMp5OO_e8nRn>S!aLI2JV<neX6t(Dz z^raspBt147jWv3T#Y^&ZR<%`XkL$K%XpV&Fk7tZ>AEskK{RXmw!XS%#W?9~4P&1T2 zwMUKdbh}sV4RI%MM!vtm2U*bVA8`eReB<wtPQobyW60n7>q$Px+kzrU^i8*aVk=zR z{lCah(SOB_b93A$25KRzO2W$yG0LEQfEN&>zDf9d)ZFb)5$s;^DU;E6K6fI}DT*iL zFWkNaILPE64x41GB*Q7+s#HT_Z+5!Z0S`4!#)HHnFeTrUWn`a>+5_I)BO4^}NBB|R z<DZ!A0euX4%%MX5R%JXUm&;zLymj@BNm_7H6X&CGAzzrWmGV?7rMKqa-^ypI&0MBI z-&#sWlM3-7N-{Z~UAmxzW*RZCcqrPAgT9ISRzK;P$%Yp(+ncS0Z~R1}Qi!K&B}g;9 z`z3ysevEsLTN*6f1Xy|U*8K}Tktdh#)_BNwGipjal5F5l0NxucyXU#<Fs@f&O(S&c z+}W+oN-5+r)h$DHSzsBXUHRqMTdf4V3R7A5ntkqUU!!qcaY(@hu4d2czYNuF<XQW4 zT))>{ph2<%-XO{-kO<H#(5)Y&mwb<ig-0b`FO_dSqSsr+l<XDPbc5XYRQ4t{j&&Wg z)#ueLIF3T?fTgNL8Z~)d%@s{+XX|39roS<V_o}8}yb;qvq0zR!#iBSVkGLbMCP{iM zsZf$i^Fs4th{ld!X%8JDM-J=FbX-t#icFWc<LBNQRl7%2S}SQvG(F}^S}tGB%d$*_ zL{?N4$t_eOF?V66r;_O9MADKEY$w9W^I=MqP(#Z#qRP&rH><TsQcD78Q<{ub{$fxy ztGwOrgs}e8kl3%%k8roR7q}ne?ipNs!|NXAiS(F9W%4ef$bh1}unCwfiGl<kmxvO% z&Jh{l{yHZsqO4r!5WE$hNfD;O^Ws^c?Z8MZy7m6|zV*$|KK;-GE6a^qAy>(%EU6gE zW0)ItnC3z#9mc0Z+K>=)R_uk~4F3diL!F1Oz*Bu2vE!5qB#EQ;d>OnL`-PzaKX_l3 z5!ac<u&d&0{3wBju=tPP@Q3Ac!3{BTcar|TG3OYT<!fO#squvTHd6E_j>JRav6D`< zR!MZWj`WPe(G3q$($t#sM6T|t0@X*YcEq;x<E~LEB3dQSj8SlQfb!)`y=tk3X^uU; z*g2CD!IcS*-w??E6SrhD7t3TD8N_bV_>fY!qg8`C;`rU#r59E!8PirJO;MxtBZ&~W zJ5Ay>U_#J|_US=kJtB7dye!CxxJ(<mWdZm5>YAU88J2ugeR{#G%3?8N#q44+Y{;fn z?<c{JP>GLa!cM)G35A`sV~zECqhR+Wdg9M7o+fUhww&{lRib8I`l<JU^P!Q7Ra_xT zBPt6(IjRp!-`M>k{eAiq+(mA3&;jjBM{N@pPB%{+Ts<JrjR2}azj)9J1jvnmC)zrH z?${muUai8+8?=%)%$J!2f;iL<j26Bv$R_vkGZ1w@NI7q-Mz6a_C;R;<L<<^oFB3<i z-C^1Tq|U@l{$oXTMcz-pIi{(xTn!18WAMBi+qz@(X1<m+$&*yj^pVgpZ9Ml!ch8u# zAzG)4)2b7>`EV4Ti;_^<jQHRcz*)o=Qfk~)3-qE^H><Or<#iLq32?(oJX?;&?4{AD zB~hdHx;Gs=9Fc@6B7j>>=^d&(b?}nopS@F4CPG=oF;)>Ngq4IwMM)FnkSw|CIMye- z`=|V~^k>25%@1ZyZ68{m(G{8J?gFNxo9%H**E#%zbaCi1=>>*I7}Xcyw(hyq?~ODT z3L-PDAyFeo3e_RgJq!~s2MVmt%ht8G?pVNAVgR<R%pdMiShvp(tb`rR8~^?nK6-3X zB!;0{>*JR{vg-Wx$rjaGf_vjuM50dd)bs6ZdOQ>MZABeFZ<*&RDPC<RZ7|2Ri8*jT z4NDcYTwPZMxl|ps!gJ5unRDfPN#w`he|UPw5G5m9nz}17MZD)%F8HC7+lqa5*`R!? zsY)>?Vr%O15yNxyvJz`0y{N$}=|Urmj5_a|8gF=|(OAfqoLD9oQ+T5or3I#ko4bEX z{}A!%Q7{LwW7~%jcOSqS+ZM(8ZmxIBC8+K&;3xJpSbzotJ420!D-JbI4!+ZeU|x3y z`hEAFI@xL0YJo%;Vd)Qj3zl`rBQx)|7n_)8>oY~3Y)9B(ffMRLkENni9i>GX5D1Zp zUBbtK_%Gf7aabMYhxMUAq5&(%Vj^-FvfSP5*zpHmNU9raM(1kd@DJXd_GU~{6hv!g ze#C6($KQtffa%(%Xhuwg;%sh_f^e#5u@-=KQpllnc23H*WP8>|)g=bjPLsltRVqzS z>QvSAR@}+Rv~S+nIDN=Z-b=Kqlqjy~hyCRG-AZ9+OXIal(U(I~rzsI<B<9If&~;4> zQ&DNxSS3!e?D+ar*r-*tc3M)4swO(T0%BQ@#ZjA;B{2;3sXT&^=0bAr?sfVHh*K|c zrw1oC5R~pZamV4wI-=5pSI+RDdk~|LLJ8sx4r#8)K!r>>7NZCv86p-!m&9_K0Ph28 zy#D#uy!z^WE6Z~;qa%%m!ie!|1&Dx&Vwgn+1`z#b<JglKWH*c=k;*<F$^<tIXhC5` zVt}POyZ~y3xMm+8%PgSb)Z3nqi53PKz&0j5c2y6+NKp8-Ma%T#j!vv_t3Eqc<;|_8 z)6}S6y<&x4-z_G=^+}2;#M+a0wU(+U786)<D^|AQig%{W(zx4Cc|=~ey#Cxs9yRJ} z1-?q2uJ$Z*a$@6Py0Ez+QK}WgIpaMj%EG(QvOx{U2#2TE>my^;{F0+qPqrg7Y1xVw zx?NeB^|I?KmRL29PfSP6Vp5>8k=8ZsT!ZkI;H11LV#wICZnwtsEr~A{jB-?`jg?Fy zI<=K+ThhX+Uv}`6ITJ!+*2r(sj{^@r&b@ySIsktC&Z8SVqjIM;mFM;>5thp%f*|pL zC5lvJUQiSR_ZMVvfEnPg81?(l9my)pgWR{d9QZ<kfG`e<&;Z}&s~^2_{qo&FEES2l zRcM4CWvajs=^j{M*gFs)@B?$IffSjb19?vZMZ#E_4~<5d{F%ke(Y=cBFx&-T1s03H zndEP~TO`YS+Ca@KxUcYNy^#`5+~rG`rrTjfRJ!@p#NpAzAokeV<zq!TdicELOUBfK zjj#p&N1ZhcQhzKYZTs3RY^~Fn@l-Wu3$~fp4H`-|$_G=it^2jqoma+3&K6?y(qXNb z^*ny|V2JR=Y%dxM`Sl|SKVHl8%0le}I}dervoS#xs#Fr7;*olZVrJ3WoFl2DCh<$& zpBovg5{tT69$n|tX+CkQVM=PsB;n-7^s6uALW>67IlM7G0yd75cAuwzhj{n>+}XkD zaU`*zQN%aD;i>b2Af1N(199R#qzN}T8OT#+$P*A$xDGch3c#8?|0+0SQD!VZr4HTt zzPCO9?8DdYxpeBpfu)f~D#@t3@*WzjcJ~t_nD?-{#H_kMIT?vY10e=MvC81T4?B%| z?ErK3eMp_XAZk|X7+NcQEza&hx`A&9QmvusvhIMrU@1e|Lmx3p1LquIULwb-GCeC2 z&6^tA;8lshXN@*&TM_aD8StD4ZxFq-A$C{I5-+DjAM)gT7oxSMc|MA+jT{B?r$@JA zn%%FZ(*(d<Ok1*ENH~&^n~%jhn^8eb>wHu^etbNy2}Z~RlK`4Iv0AnrMJyE#R#Ku- zK7D4ISE(1N_eYypQLs-n6ArS0Y66OP!h<N*N;RbvlWi^AR~vAfe6wQ^qNknt_RP(@ z<WPtT?T*IR3PoaPM84?H=8img-cuSTQPg(HFe`Cs`$$7WDLY04S>%(;A>`@>h&8wS zRr)afX|4?Z@zTIr9VtfPe|8u~V<f%`OHSXG{~jA@veY7=b}h!?{}Z3P0%EXs>O`+I z##ljcl!5g!(J-ka;fS)rH}lYYA_7<frl~MF^_d7WG*Ax~PC;!A-(|9A+5r{W*O;m- zeQ$dvrBqh8d|oLpX&~qY;#}A;EqCisX?0xnsiLalR6|tU&Z?^kZraxUa5a<QH5tD5 zz>aQf+N{YNx~^9%_uW4`ucuP<**a0;NrAKw4P{~+EFyM>y?LV`Dynwv8y^%jXQFRO zaxP;lN~N&ko?NMyBvoIZl3|}C4b_fsERC6!d|^C!uqL}MxR2fcw)+UZN<Yk1xre!} z!9jQmZY&q(MY2sKKp$poFhcNzBK}+t>SxT_B>cI~1>E}GFgTOjx^VvF@xzA>ET!V5 zybRyCzq-NNdg!2HOFPQ%t*u<B1gV&ab!W-g{xa^F2^Mu9j|L~mmHusJ19D>5WS)?{ z80FEQ{H1y$T1usk7L&{9ypS)S4K<9Bc*zyQ`D7v~64!8qG4i|<<T!e`>{y|YtUG>d zaLCMb6OFJWs;W`&VwOspn{(riLWD-CnoB8)8$ywU&rQ*H_#r#gi^1{e{Ad!eC#st~ z2}R11Nxg)$@+it@L<pC&+5Fh_Al)w3Ywi4)LG@5CoNZg4Cb_`>t(dtwC6+pwR3}j> z0c9BZ5lJd6V5Pm?Kj$mZ|HrvEk%tD!7(#Zxfo$Z3*FC);$iiV%N!3>oh1>6@TH!R5 zDK)UTvH-lSBK)bGDsne;!YL|;41BN96&MNlT~$@iqCv`Hbx-LmDjlx0U*h>6y_S3X zT2^qaD2(*~|FzhC4c3r0YDkiAaGF3hXx%^mlzR{O@=f7FtcH^&+>=i{cI(=MXHMUF zbaQ>OJ3d;>dm-*|^0<M3uvf-~I^B*4L16b$-3b&C4q<5L0U{4zU=$gq3}ynzJ({J= zJw!Xu%OF46*WDm`%d!XZ2rS%woTxCj68Ij1>oV8?1AvQxGXG-4CrY9wl4N1=!ZV=@ z6VV)XiORrL^3kJ<XI(Qwwaa&<Bh`xGdLc6h4&ukXnwE&hiD;ogAYVe(lD%WR5Y8CL zQ^zwcQAb%s_DbnmE}V#llD^p}n6g0A<fP}X9Wa5_sMMZ&SU0rsLK59rgG<wBPpP*i z+grM?XARJG#?+u*Ok^h)74`0<QmRaNL^XK8#J;3aY3nG_O~=<Y%byM951db!Xkr2s zP#x+uyi&AaYqIKFjl3={k%zp?VOvZ|aW`7kn;CeQ%<fm{*Aep{Ao9Sy?*ig!9#~hN z?jx^d1q;>yInE&pN;309Q0aRC=~2G`Pl!;z$0|wEkhN#M3vR&Lvufmbcb*lT7yGWy zW8a4kLkaDyk_=-c_=C*iJKQN^=H5**%?w?+eAoHocXlQkwNxS=D=4g7RbhH@8}>nW zpgpX+Wf-Y?-Jq%BWgp4<5TRYN1if0M3&M~u8M;RJMyAT(&{&k}K%)!z6L?LUpljoo zqf`<7jKY)N<lF6u@mT)(0`Dmr&E>ia{iW%9&!>Uz@)1305l3xJs#YXX2qR3Wjj#rP zHq*TH?%8rp^psRs;>i#4UV>L>rrtV&MwOH^K2uFfs8h~NlcoR~^cFZln<R_!=~Ug+ zi)axGYsC;TLZ`+Rpcc)FLRk_^sc1Q+3fb-AmK&YddV0=PsM*YTQg{R!lidBH@Ofy= z=gG<-^MSX$fZX&WkKDL6A&S~B-idsY-~9QFm08b_2udZ)A#KAQN+3mV7=V~Oq88{9 zMwlU<X;c$|CaJDb{W_<E`eEUXXaXdts-P=@<<Fuk6hCQNn3Y7)2!XVMmC=rV$IEJ< zaagpZl1Sl&b!m8UH0ZF4qvQD>U3_@mf8tL6J1$Act6q7}%=rzngnRMhKm5LTz4I+^ zdj8q-XOA9PoNqPLsgQ&9`@G2*H~2QDA&ewr5taEQP@?crL*4PC6YR(RM3gx!26mW3 z3l0VZhUzhtG&}*mY%e+oUNeBs`=^W{kA7w34gm0;TlM$<W*~Z~?7qixe)bw-@HQ*^ zzyYJT4^-A<KOpLHP24wo@t@3ZM2Rr19zA4h5zn>qaTEIMj%M9xSmtACl%DuzH<>e~ zY)V37vNNk~zEf>hb2%PGAF$(!GZl}9ooKP8TdBoEjZwo*#cjtl(^=DPHs>q>yz3%W zD`iuWBmM1LHRW<s(vPi++To_GY#y!_!?x}?IZx4zBuY_kX~_qs)_by*;bnXY(pmBe zQmYX$RzXjqrm0%gLZ9`;k(600ee!1L$cC+iz)tGC#fREyMI*8=l9+V*qt8tysg@L- zO3W?4^_JJUsx^}VOb42F=iykTmM*6b{q&D7lvJwPPAVMAWW2aht^kzrswkC_w|OJA zn4=q3Wo(5wCv{~`A@i!d5Hm#~lIyAhx_hAssyph);k%ltA4vu8u=c@+#wPCjP(_MW zJanfJ9^`pJPcZ&0vHNBI|DYe@-oyR=z&}U?>D|vh_`q}tEX+HJ%<-G|5*en*0Q>~1 z(C`yT+|Y_80IPtH5f~psDNu?u>^#9wf&gQaWlLtp#=024%M)FkIOyE|G66*WkOPv2 z91xuKKl)Bt##yi!_w8?c@`*D$8|w>m-F7t}a6kd(yln$RT@0*%xnT(j!ME!59{tRW zAyDysjYgr4Df4abj--Av;(L+(+@tC;GwM}}fj;9uL*EV`k1Gs~lsRx_Zhimn3)As} zZ(UwkpRpv@$WDSRNYxq=i3nn!3h8mUTzhr-E;Lac?S#XP($2{_S2A?PvP8p6h~8WQ z^t|l5uB3{&Bq7f72}5@)wqCBH)z>jXh7+|+JrV0PSUJ_<^{RO9y~s;a8!^?*RK#c3 z{qTYLKfXFB;JjEk*H0LR6-_461dJ13g1VX6@seWbYPa7}iM0@GHZtoeBN8*C$VyN< zE}*}*d1@wqM=|M2x`r&DSG2~6>?Fs+q-+2-pvA>-vc-%ng)4n6(zUzu5AGNn>!Og0 zX>PuT3O8p!17Agd9|c1&<=2;}z@5AU0Ai%h`m52TDX=6Gejb@MoUb4REiTZrpey#F z`|mne8bh0=kG38Nj^Vo70t%LW=z{jMlA%bdRaS2J7I)Uq(3hJ$_q`r-W<z)O^5NBD zwThN7^8GwGEBsHgjYH?=FI)^MvfWmUht6%zW||{QXUY*TUlufNWW?DD$yTUYuw=D6 zg@$HDnpCLjcB>-M{eDO*K6_WHQ`U@TX?#B2sZEt$JE17pOY?h3ODF{+4;CC!KYqGV zuN97MncB&6R#77<*~s#m-tP;cf@OzN<z6EhmSK%eUuE^Yd=aFqS3WXlQOONgyAI(? z2?>;7Z}au9TTGcc+@@vI=~-kj`Q1GKG5R9+6!#IbI>=6zDX)C^2j2BILE?Y<xyR7U z^U(d5?i7&AY@Q{;o=2ELr!tRPA*dW)<RwPb%4lf54zDMmgf+BMb-xB5loH_@1OfT{ zUVGaA=oJ`aaOg!=iU01dfIa}8zkLsb>3{A41c8;Y%|o{zVt4_5n3_EEG~s^Y$DaPk zGaq^M=DqjavEA#onuQ$q6nTp6G6@eGWtgjBR$tgtI7a9*Ke6vzzUjw6(LgTlmjRfs z3IaBCI#6Kth6g1@^hWgT_4Zs$<(orz;C!kTGGrWCWM$ts+3&JiX_akcsdPuhedpxj z8-6oAwm5yQBLE-4@aG2+72Brf4mBFnt+f<7a+X(CyUB3GFe;)GG9?jg&{V#l3YBbB zRhMJ+Vk|y5ZjfAc-86-$J?>Y=5;>@f($AzbiI@@v%S1${H$wh4nnbmzts0WLP>N)* z;fL7|>v*N1a;Ihb#>fK)=(V~ZpX)_h8wG4*q4jcfDPmiitN7`oaVJtZ0Q!F-lT!5F ziVb!~l?5T;N5hI%>^4Ut@o40ZP|j59#{7mR@Zo43{F-t4tzjuth~&U@>vv?5mP%=N z@Y>{svC21oHxU)J)Wo9gnX2I~pnpwOR1=Ptw?zvz5?P{t%`CYuz0;|BEMgkFALZW- zfAuErj=_-^c35TNeq?#|Dro*Tcr_wyjuKYh6B)4yyh_k5!IBEJw*Xj*`~E9nreFWu zW4GpJSbJ&<0pOqXVHWm}41H05h^!dotG>dqF-7bBFLQ0O&e!ryCzq(yWK!Bn`SR(3 zrY%k_o<HoA{6@i7(E{TC&-Zo;S{wvEuc5FdN@}=*YGAG|3yE}$S7VmWd*pwL2h8q% zh5sFRz#rs3!Tq;E_RXy{i1wfP;JcPo)F+M|qP+AJBq?mZ>fxij2oK1z+iB#B3Xzo? z92!3qh*v<lB<QLD5M!jaNO_h`h>Y8Y14Mb3^%{kP`UcByzN-raowm0x0N3{|Uk+=a zEKLCk%_u%#wcr+zo$cSk@CqntqB-{c?|sATU;XF<_n$knd9Yb8=HtG~2o<=Az(3u_ z`a=ix3b4N)6wyo0LZ)nC!vfyR(!GoURTU4r4WXXwD)2i9y8(ETk&_L7mDtBaFPPzd zA72t=Da`c^AsUOj!Nmir1$_X>rCED6HtG!l)=>%w60(xKQpj5}-#vCwRP=`r#4ORi z>y&LTMpQKC>HzZwkt}<)Dch#0LQIm9&sjtitcb6TB<P8grHTzKZ`L=a1YS?X4b+oG zVKOEtQMBkN%GDO?`fgZ@P~D2p-8D8L6qB|f`9h*(V2g({N>ABw$Lhl<z@{6g9!aSQ zT~(C-O0nrlG<8s<idV>`EqAnYhfWT}Qo40E%-2J;3By8gK^Jh!2O<v=3`%uFo;z4b zCyQal<Tb6`9&{r0u3gFNMS(Y_`_l&fVIZw>Dy9{5PhQng_f^6K%|w%*`_NjEkK5R= zWhKQ##W(EEnC_F)VdTK({4&T-A=!lyxps4ud9pjvDfA}#QM9XF8$2*SOJzY<Sl+t{ zU@wB;M?7aW8mJO<nq|X72u4(RU8KRBpmK=yoFXv7a(@RXV@N>95bolIQzs4`Tv?v% zO^g+Cv8d;Am&xT||0QEFm?%59dlCY!dDz(q4G4;F!@xCE5vB}ajv@TujNpjso+9mO z3}AksM0@X&XS(wn&9H|ji6qR9R5^ZpIe++M$lGi@{o(4!<L|R+W#*Z!ONJnCUFpV0 zLT(Z53yN9m9x6m)@iS*5(QqzXn9`MG`BP6Ed?D$LmYdV{3-^#@yIwnFf>{=&jOU`u zCTwo5LDe_oTXN)Gk9yIg>krSBJia&zMqe(5WlgJ=M#HMn8dD_H5n6@P*5bLFoqDEN zn6sR)2T5dsfeZ9wsEJ+~T(STu?*?N`>Xjr<#o3^G23~CgJQXW?LoBRX$1w4p!Skr{ zUq_p8!1c2oxdCkqatr;z*G?Q;Ug~$pTZOElaW~0LX63-jNYwXQv#NU*H>3sv6K3M* z_oG90|C>2cnL!VH)VU3ZnShu<GGx7HxgJ<9lnM5cN03F)_ijvKSGBY-Z5GCwGm%-> z=`MuA({^tpBU&M;KBJ-0Oq{vws){^%a<w43QKLMqgK|4&pOO8r-k6Tb;~T&x_aDvl zl6FFiVfV=$l%}KzaXxldGE0x%qR+JiN04XxXsHs?Q(oNf%p@E?F=0}W2*5H(Y$aXl z9WaPcNMZGqrlPC-(T1Y<G0V4Wy)pCnf+{$6(hjE*x+7r=6FERqaxUvghaZtiD8K-r z-T%Pf7og?(pmOhl?nFi8H&{m~8rlg(Z&a}PinF+_#~<6=sAbsBn=*VKbB0|gtSkjO z&VCIRPR8avm_&!MHN)x%^TB)TKU~#d=d2Gq2xNofn8O5?1D?b$Lv96`XmIhNB!<VL z!z9YzC;bxdV!v&)I?u~>Q*bJqYMx(d>O#6A(a`;5VH2CntkFKv4xJZe-QXkGC77I6 zr)Mnv<Xt)6$#^J+$w=sAb@4dZ7pNfoFHuBKwM9`C3Sd37DpZ#q6ds`I%sjfSa&2DF zsgU4NPbP9s0{8fUHP)0$5Wt<eh<Uk(O`Qt0b9qfHx36qxq6>>)Fybl2j#`%f-p0L8 z4`~-F#Kw^Y^29_)7Q^A}a{qz%rj13zGDJbZfCb9M(RuJ|@MZUN@8v!?aB-jXZt%|U zc;nV8XuEwjx(ky5gU+a4NsyUmL#QM$9T5^^lt$pi0E1ZzIU+5a?Ep67yFbmvOoD;` z-~+FJ{<Tj$@ZJaCd*$-EGrdj@a5yl(KB|sP3z(q14Bg8%_jH0;5;o=qVr8Ob8u88M zwa4LAuy+-&R}|BR)P;}ths?!r@53lR%sKi+l&<$RPWD-C!{+xkIr~xm?LA_p(j`{( z6nT53urZiU_nQk(kIo)sg|kV2GNXaC7;)DN?WADakx7{^)DHtDV`tbr^2J&ewNWW` z<OR2)+$son9VsM64wg0P=TrC|g-2*R;A)XlJT~H6ayV1UD1_9+nJFTtmMb$yig|if zB0%5aWTl`K!y!-2*xR|a(ZYB$WGDN5*rF~;RksqMq2l-#mQsxoO?Lc9xNFN=E>mm* zv%^`I(~=l!)Ij{Hwq3Z-B;EtY9W}S%3G^GE&*a?W;ijGP3RSeIIoNw;#RckT%X$=^ z<H}*JIYNZE9m}g=zZg3v{2}=2XSh#szel7&;WIz`<Y}bh+Rt6RmzRxq5M5q90J1{Z z{2uHUK8Uslbht3G40A2;Ad~?^JQ0Kc=<x(=tr^%Rd7U$4)nJ`Wx+*j7+EjJ(I)~;8 zlc^+VN`~Tim9<3#urZ*s5mwo*H&=v0{x7*aYHz{icLu57{Ph<<_CxP^{<+5=J8^7l zb7^sAx;;@x`MGW}qJ=fB1&SFc8A3%A9)GCJ`<e{RE*3_GeQg6l9W*I2HI^BB!a@ix zfn@X*kz+60#Ua6m(EDMsJVc-PCNVICeIgs*ve&x9oZ=9%?q}EizQpP!><+{#I}oOZ z+Zc9q4815aC3X(|$xJ9#m*fXAKA_lJ&xcq!By=&vdm#z?B0gk<C$*62YB70jv>le? zy_jn{F=uRktP&YH0F5k;4Z`lh*6e|KKeOc$+fEit)6&-)s%h%^L^INyo|$iVAD_3t zi$oU5G0T*-TFIhXUQuIt(QNE2Yq>D0Y!QQz%<gyxHpmMquPI_@=A|DsfSN;OTRm_< zQi{nlc}caac`2+erXBi2p+tFBl+HLpvW!+C)Lp4_Y)Uht$2MBYLfYW3%SfZa%uyes z7LqnIZ#f$^+fYP4T_3T;iNPC>zbdSZV=to@PURvY+mD6%Q8a>!aX*$Uhx=Y>b}H=L z>4s&ena4)vl4<nX=kAD@$s*e6b1^FWreD5$Hf<&oycn7-A~+xt5I+GLNJ`~s{>Lur zl`_}`Td(jW3~!)re)OKHpo25F`+xEWP&<D;I!6GnMCrae4}$K>QmXQ4Tt(Qte3(iA zKcaLK)U$Y#L!Jp*o>78GIAKMgpk(DDdODfOTtt!uLkT?hv(G&7np-z6-Ca6xwb80; zQUXDk<rL6^J&3tawFHXticTWv6DANqxH7mtv<-Dwp@O+)Xm%waT$mX|QTGT;VS@b} zWx+XuK3k@B6&xA?DDAWnl(EZ}RYn3>#pgk%<<`7x1zWxcD|uvb%GtGYOx2ypc%fFO zV!5u^mYT|!L(Va`lM$o2tXwV~;IqZ)d@_<p%cY=(=8lBK3%#->8&l!dQivDanBj!u zHaXqY8z{uuC#8eQLoShRHTyln&QU9+WhRUr0}(mgLChw~=0R6d?x;moUJb{qH8G2B zTs4`Qn^{C0CnT9dx_V_~%v3XBK~h7r^A=Hh<-TI~tYRDwRf_9yR%jv@L#4Rj-t{mD zX-iIq?|R0dm{uXO9HX$?;I9KI{Q#nJ>IV)TgkMttB{-h(CG@#x(8?)nUM1q5-)tkB zV9k;!E<ng^Ydih}F<(Yx-Y*yj@<qCI4n>C$Yd84sI|=UX<&&@p?j3J`;|ou{_WapK ztvoWSO7SK}wG3JMz^4o$@V-AAIua&-_@;gDweND6R_uc`2pvZRDghlBLSPn+1N8uE z#*4*!_F{OORqmjRYy&!45t#Z+f)>VkOwnZWb?lU}8~upI_tvtuS{Ox3K&MhJ2x7H% zcq|>6Tr*VFJuqwg$7*5KA=Z3rtm1_eOQ?V?&BNYBX+o8ZeAc#kYxT&D_IAWEL>fK3 z+ErnLVs?wtkYG>B;V0iZHac-OUU0BR-tx=?D-VQ2>q<ecS7q`yq^dl9Ojj``Mn>}= zZxj>_)!<Gg;j3CEtWKj4=B~7Kq2^WQ@2qI&dsQ#&Me@sLM2jb8(8+{umsHA)7PHvd zqG}UU3)Ng4MY-vELd0+uA=Pw3uRl4Lf8mzvJy|i*@j)IVA6kv2Yb165#-<0aHljeA zcE7&+5WPWvlDoid4%QREY`}TkVA$^@V$*~u0K#GL0>UV&Qz9o{9(Fq29tdEPtuv?Z z*xuY&oX5uCN=0GaWPQk$#iJk^;AiA*w~MHOl$dy#`3}oQLtTJ@L+;i6_G0#7Ain*O zCXdq3H$f(<io3BwwY8p|_L6!^%o0Nqlh@Ve>WPOIr@KaLJ9pnb$1WmFq;t`NDtd)* zStPb6&DIU0XIib9vC&>WQ#x5L*<dXQwr%K|Ft!+#Dm6hcg$U*cII561LA9B|R=0(| z8%hj&{Mol2U3RgbD4ILi$T~i2`^YOa=B*xB9_ihfJQ?y4Y6PCyXleIp?#uKmT$sbW z7B*0VY+8hFfVN!+vPL?Cbsi2h!6sdf94!|+Y^QLjAY=c0)bB*F5JeZ85Id1C(>L?t zGw3bcIVVZs3v2X$Grs;85((@4y`Va9wzc~t_j&qd%!cU=+I|SVfMgS2w1F=Sz6{$U z@pUKIH=e~tLW~iioNFs07o)LY`y*_nh;6Fq;hF4o+~5}^{vG!{^jTi$pU#3wA=H0$ zSESE@i~D(n#8@o)`g?)wQK9nL^Ahqox<S8yX<pJNWuDC!DhLs@wQ(JRMF)`QW%AP3 zNP<)d$?1kq{+@pDj|kWt^J_>cO!~W6m7}{~qMPIoF;8TJ&3p-I*xa!(Se!+gorp!% z;n<mtHNq%0Y=2=cBkChqux7;$)2A#NSD8XV)xeS=i-W;Sm1>x2Rq%0ec)#Cc7^sR+ zbTn)qJ!>?UL%A&5T#5c{o^<?}aB!{W+T`3hDxQ;n)J1f55Mfg>End(;=o7j1+^xq1 zsWc@!CU0Vyd2Rl|t*)_%4B4PllU>)AFyltT9w~a>CS5VoxQG&BI2BS1SJWEDx$rTb z^>&}!{UZ4q{TJLUcPD1QZLd^$%zg{Qp>_OWD=iEl5`@G4B;1=noUaw|ez1i-Ux^$S z3&8$hw-)Lbcz6~aL1?p6aIx1Nn~m~eYG@$!v4QHKQ&Tk>iVRAu?c<&ki@rT*Qv+EX zREJ<@5oYypQbH+8s2xC$oYjV631ND?mo{|7Ro+WBud5{`HKrkAVV^_7mZFD`uXjbE z(}<z1b%b90%8~JhGDOD~H%0Ztmws%ngk(jhC>~3jXTk-eZSVzi5qfiK_s_|P$$#KR zxhRH;0P9zZDP0(fdW;R#2?vlMm?%+&4}Uo>;Nh@NEak)d`$6H?E;{vm)s0lK_qkq& zn+Dqk!8ZAJZ$jwZ%xJv>h<u_nLVkRbSIrQKCdP=Gx6z3?Hqweyp-d|tYK&`+Qauo! zo5^Pm@oXf_?(Xha$S!@HTj3G|AM3NRvfL^c6A^iMtAhomm5?0d0Jjw#CW0&w;qI_E zvyY?_|7TPi1I(V!4)dcB4pYRv2SbpHEu9v{*Z(92fRVD~#-{Ia#52p-%g;s{91Q7N zUJetRr%tX^S9qx@x^F)nvvPF=3p$<45}JR=<kha2Q=hmQF~X%qiFT%i?B@6hmm19p z1MTS8PBpt-X@wjw?qV<A9dBE%h~|wD+L2@h8hOX=pOKG{Kj0eN5?B6gDTaO^aW4V; z^j_-D{${!Or*fIw4;|zxYy%E-4lX;uZ>(#a1!A@UEa&L^f&4rur-U&neXK{&sdDSS z19ugy-igf=vEql(8=TT38G>D*cU%>PMn8-F?*@I3ix>+-qp&teR>_;TNuHW29FA(V zG3a@^g?_1Uo9!3Fh$$jX46@C%9v*GnvxF@lD*F9xd37YL#1nE96?K{Utm1Bg+``U^ z0r%9vW;)-70@BTeIY8q>FIJIq<`L?on`mJRHs=N0;3e#wVeR!7!Da-5a%$gv49fM7 zpwUSXF|%p^a8cMn;Yv6h49}RIZZ+$bRKl@!6;XB&49}>rp<;nTu$f4}wQPmh3$ubi z>SCFoCDm@R$Oh^#Z3+5(IvA$b?t^lai9J`5)U<Dqn~LTh;T^;@q1{F!v?<FWfwU*w zTr?_qywRD}(c6}YB#=VP&}m3r^-}Fdo)XQ7jJ)UVBMOx-tm{HC9A0{4=DBk<Vr7W8 z+Oe&<F};i_H0dd}F~r>+N6X|Natqkm_y~(S4`QQHy&zGphwOrG<}w&i#wetlJS(@e zjg6R*arx4vM=n3o9#7%hy`~RG#T@ZoI?A*GkuO*UJQlN*y|xfAa2U%NC{nc=AiHk6 zAI8qE>TRzc^%1Z7$bq}v$!?Ej72*9_ZLa`jW0!r=T!ZTtP+ySdM|)s_Wm9NHlCn^5 zVy_s9!>sHRC$aqzw9I06Ah?)X;nD5)FuDv6!5ab)L^y&^Z5^9XWd%bl!n};`%wHn8 zhOywA+sH3<X(4fNu|*|xT4RGh^-x^{)>?VWjLu#;;26&76}Z%h3*W06bejAY+z}=2 z(pIlxSRvct)0*6yJt67}8cM;XzLXCo9k;V0k>_w&L`p%gc6KMoKR|DH5Nn%iFj)^F z0NxO*h9_lv67h-*e@D>3;|~J|dwUN^@5c@(6P9!@IO0<XrNYoS6b=FOf<sMRJ6&^i zq``>Oizu$}>C=9sv20>7BiMDbbTJe{2{>Dj8mQ!j+qo>f0_Z_MzR*LWNxh6pO~8jA zAX=p&O-3+@56j8(`P1Q7m8iAgXpuP7)l90H>0i01%HDxv8W?IJnpCGh**|p1q^d7l zwtlsrchp?6jJ?7#%@nly&wc;(Q=%<E$@tx`l65fsCMFgrpPq;zmt|3O606~ZHfJ6v z42^<QW($MN0ME!M4R~HWJy`XC7R2Y62|6^ygZ4aRozk}npHF>|WOI;x<#-I+iQ_u< zd<zNe|01pvu8o=La2{oO@L*Sr3B&WyC2}wsc}Yh9FA89>`A{gUW8PGB^}{vAk`;8X z>1MpBduBAhboe?NJb@PJ?%(Y8xc@|d1`{88c!Gk3ZN_+l{!o}fe3Nl|!Jdl%0|Y4! za^_*VZzxMVid4N>my3Eb9506pt~Pi<5Y(!OCVl~XWK!cKbAk#vs@)MYu*;qIV&O2Q zFO;N=77a%VP9m)R$9qtcg?~f_v3A!FKWrsiB((t+PHHz}>A_FFT1_Vfbi-oJ3t00P zamOb>@XigUPaQpslGVn1Z-NKFMRO4+T!o`}c{bri1dYAfgqQAKIWSl#74um(uz|1+ z#$i!JmLx9%3_6J?9p(kH-+^sHfzkLF1wfr_-);=UFa2Jz3MmIHLIpqc+dhl{A7BJt z5M<b#tu{J<5qyI@grFwKj9F9FQq*NuZN)Ev9T~Uj0TkKI*gGWBYSL(8f~xxXeWgrp z9NrVtK5I%|$)PHZUPbiCS|%IfSih(c)8XN(UDcFT#HfUfZa(@!RP<mgY-eVCMDiV? zIx{ex`IPI?^%yY{R`fZMN3)0Sh2+eP7fTe4{Jq->f>vBQ{-g7(JOUh2VY6;BuTzj? zgbURg=Eq9xE8-}L>uLgC;GW&TBvascn(zX(FuVYJOei)+!temXY&n8rFH|1Ln8)uC zNrG|TqoHPm{tysyy6#R-Mj1>iV(tMR$1<&?{RU#Y{UdLC;a|pcuv;JzQZ5>$l#Wdx ze0-u^{gFqnn3gSwTFAH?8$MVhbx`$uJ^5qq`#98Y-ts+fc>byLJEu=9&34O4Tc$3z zar4Htt#(Tqf~8`P;RX_gXTrd=Kw`MNq11!B1v-KTunh!(Lxa_#zZGr?qJ>|=4}r^K zP$RH!yb*lBJ_4B#7%^}&Gi3~9!4)N$*$}w>p;xOSn98W8;d*^U4iwg8T$wz6sm5~4 zs$*9Hr=oLxJUzHWK82vp^9e9Flq44?;^4B2*qI~<_5fuT!lxu#;2(o7WWcV0ixf*` zxVU7J$d;nim6V*pshC+tLZL_$`r}M5CMV!M>GrHf{$Lc9UrTf(F|Gx08fY5oW3L`Z zPoY>q|F$6r2JcjhA*oo_k*I7x0cuGwO#0NhvJwX8bRrzBS?!%;ErV({`VGy>kqOBX z1ff{qMO_lJVniRw6LaKrcc6qsk(z7{hpE>`lW9@c;;k`Md$6|?I}h##62BArbfn2L zIG7LH<prBUr#zM%OSrev><6qWq6+4KVEOPnbD~`Uy(*SAc!wdrwvSz8kv-x1nE4zL zuz`+GVGVn$8LX!}YVk0uEav@97?^99$V^E^jO@U;8ssRv$~(NFD1!m|g<LWiM5vcO zt&#WK`RtM}qCvsX?fevy(OBEIK&fNftSJeucyweXmq*FDB~`KeHovrWD*?xgZCnZr z5$z)*EhizzVtLrTW|f!+#$@d;ApiR^m%;=<<<qSk>S|a4s2>_IWIQ}xc*-#vP~nIj z0hWC^t%R}$+68b%W+U3-jzdgzVi_0ikA)77AZyZu*kSblq4G&-0wV$D`>M$Ealx1; zzh_G2xDffodQBFUxt6B2imINt)jx&Cg+>-9h~xK6mTk4=^7^?4#|t7+S<Kkooxo<5 z7PkcqhM9#2*AJLFKn<YQ=J7knMq4%CQ^*DcOKt+&au{PpxDaqV+2R@cDpUif2(nfN zQwIIV6976@nIwZ$1uO)fni)~lkFZdR@e*v`MBtf=!}X|ES*I=<QHuqeBJ0^>YoG?N z!<<4<9j%#|gfeen#tW6>mZ;|@Y#>1@phKe&etWb73&6Zvkn%GJ9JSF>F-xca85cbj zX)?dXe4Yy4rt({&CH)RuZ&DQv+Q<H$(9Ux>64a{Q9J4T*OAOl*ezqES>j@8QjHV)O zQh(Kno|Kt1rKCi)kAicc2Swd@<PoJHRyXEqElt8aP>Kw+1<C&zW<Lh#p;n4P@**HN z9udUI5F9{-u>wB)MTGT?0VqgvwaO0>C|e;cATqo@vk~%udHy^hcOzJ6CLNi)Xm!Vo zViA*4s2V;L*3|obo?>`LFBB`}(2{^5!i~>edm)R#*7s3$b7^pl#?n1x&R9{2wmxw4 z>iY+>1fVL?HA!y1KSh-(OR^mLQ)`$~!p6#De&wH&Uj-h=yuME^2@DZRxMSQfq{YhE z_z^GMVleX=BYN0_A(^fO5@z`-9wFLU;9<{_6qs{m&)qVUmO(a{bhT6r#~QS&gfJ== z?k^j`;1@!ZU2M%uNv0nryhb!c48e-S!-pyflD?Hzic=y+RiaM-8+sF36dq6@Pm`AK zTW&`5#Fk`h3aX>hgoY#uanmol*l;3;Y(6ArPKXh4ASzZk4!DkLpB7qom3%|cSX2JQ z+RUP%MinGLnCwn=|8n;W<V)lWSOs)FZ=;zGIEFjOlEzJzv5$j?TgNb|V4M8QZ3X0h z!mY1P^+!jzQ{+?tN!k!Lqmh_2u?{TF*k3?=5-e9hTf%an`3yk@tvAfuMH!Yvm>GV6 zvspl%^x3Y?D0#Nj=lS(@rG5x>Ji6GhZvdN7&CaT?J4J;1@DC>4#VOk>*HofD!HXD% z(>xx9$AHhsMYqdFh7zV)#Zbs2KU>mJ`Wl}QC!j;Lrp0r93?bUeV$`iqg;p#vn4U3} zo+HLYi(W*bh#K?Yu`IjZ^xSMMWYiRX^I)d~CQ;Lw7B!Lkzni`hlb6Q^BZyfWhC&gs z*`T)*3}$22$3;dWVt_W`4v)6#wPeB#2B?);>c;kE;d!wS1yvsS5`^?gP{8<`c@l(W z2FHSBWln{?$i_h=R^;?)N7dKFlYmzkm>jJhITEI_9V6P+czg!8E#<PS=<r5MqNT#D zkA2?)5Ei7W3ZZw6VJM(%MI`>?1RH_9aQ*Dq1bz=ti3&dP46Fimu1{_xVHIMR$#OrE z1$bZ#dL#orbi5(EhEIouQS`(CC*bCX9aIr$fX|W$c0T!qIdP;p?^0PAUGR8u%oH0k zMj;(o0*}P&nDa)gSkGUE<Ywo=tl1%7l#>~mFEzHa^nxF$8Up6rp+%zoWM?i0Of6ZX zW0-QKRnH98u-6OaoTU4~%~&oK!M*@uJ@~G4u|YIlmQy*_Sw00x{R4)zOmm01%AmNs zG~MqsGnhsO<`;PfWX53>B?tC-Yh$faW@FBSN(@qJqz-suCMRh2Bvf@s&OmD!oq&f6 zgafZ0VJS7jHx6V$BJdGFWK6lq*U%l65}!^lJC3AiwPFYpkI({Aq}pK<DK>IigGv<# zj`(1yv_1D&V%p@LP+qIrJ<$}?JozDhTa|qIO_TbhkwQ$CwR~I?gq6vaxq?y+i&9}# z$28)QBPedowPd*!jTVy2Rz#E(>r3&lmM~VH=w_M!?(cq)AERZ|FCJs8@vVzHCs2nN zZ;e<wx3Lb}#yZ@8<9fgvvwyE5cy#vc`vH3|-~r^|9~@vg;&27_o*#@B!#|KdlYH<B z!07%6wH{2pT9p{;jFypiup<KK3F;oJ)kj4Ai;0JTI|^}Tr-V94asaK0N#u;ci6Rda zo;2wLF|oCylVit-wOI8C%>Zv(7q&bS+L;1G#EuQ*$fKG+0u1pzDRKy>64SiLp|Td| z_56)_dPG*6m`VVP634D?%t;ZSM{7i)o{TWCDTMG(6GUQqdDZ1pMZtC36LQv4(!X*R zfC8(c>RkuSsFPj0vs9N|hm{=@+fV<$YL-ky>`J+5>*IWS<!yN}n4+!hu8@BPp74Iy z)|E7D%MTcN)+o*FhB-d0iUjRpU;?Wb@qmyzT^47sl-`~aGQiFjY|kOEVjf}?mbwV7 zd-;=205jwuFexbIrs3$&UnDSi8<mriMQR)ANPHA70#DB;eVf-@IfsZe2l%X`BMAYk zP$CgHZ$frX^UV=O!<Yf5a2gF)Ee5=u(=qK#6kEpHam6Ywd>{OcB4P$Ewsrv4lT^nt zowOUm)M2Q4(T5{d9T{UmX-N|FytaNB+cYG2U`3HtWooii6fEBhc}~FwS(ub;G_)kj ztXea-TSNlg3fP;e7*?)qQ9La})+~QxuK<9t36#UQ0}W&&IoQHQykJ8OTbUxbGwAT} z1*61ysN5hLF}?@5oy6pBld74Fiw+)S6j7us`c#e}yW>44O&VG<C8R>_S?~iOhfi&x zJD^}V<E~?8P~@b7J>ya-h5@3Y;D&o*Bd->djz0UrWl?Z2!bDQ#=u{Ro0@gGFDj|i2 zUOuEIeJPQ7>Hl`e)LQ-2L<k!o(8r7@T|a;1OzTL2GHo?JUP8VpM8T}HnF|f_1T<+H zNEWkfmuANsutCI}P4vNTfI<jH+TJD+3AeUd$Z^wTdRV$bog`Q~=v6QpkM*$x(Hl#| z_|v`B43@TsH6EIM1zUHZf(*{GL^F8vhs>mb9a^aUUmcB&>Iq%0g)^(Jr}5&!E$h)S zPrdv^$S-e@b~7&WZsq`8IV6!m!LtMntimH$4+I2lVma*AQ<yTOOJ%sVm4oZ6_!kc* zCkH8+SZ}-p&2&&xEoQ)ksUbJ>(%*V?>afpCnNdX$$D^}d=tz&ekNbK0Is7X>_!k!# zc40mYU<-ac9L(ilH@^3n0l&4cG(Sa6rGW8)c@YYqwp39s>Q=NcN<Rl@F_ARwP^G5I zswQX=UTj9PvH!2NtATCny3Y5#M^Y63DUs9<Md^}Hq$Ki@q9jV9WZ9NTQL-#sKbGx~ z{z*tlv?W`XT}l4Qwys8+6sek}>#(IQI$&A1VZhKOk9N~!!3Ja<&<)rEv?<mY!@2}b zyC1AsfMNrdtPA&@M@qIFdkKm@>gk?$-+A|*bMCqS=ib}w@9UxJd|23Ke~EK5TABZw z*w~6ymLt(nKQ>SEl1oPd;h+MVk{LjC->?%u#7qf5bF?wBKa|+7YsH@6){q4z4j3oH zHdrw$b=LIa*J|n{L!zbL#iPw_Rx~v2<Fd7(t;U7De92;Wto#$?jB9KbWlD_dYi(;9 z8qGyvK$;CxyGjb8B+(jMXjGAT0nL?BO4E<>4rPi#<QCEZEs;qjgp)l|Ti4-9ufyWT z7#8+M7qC%O&6wM2XoxpQQ->V7h_2S$-Dk+@YZl_@TwtlJ&epkTfCgJER!pA2W~j4q zIP+?F$lm1ZZNQ2b3I-*!NrHWx1M|WI7I)@LU%(0%Uz;v5DqD2>S~@27!<4RV01ZB@ zr#736j^4>bUa!9n90$va@D=eTHbX5G4>+#Zmch~H@YS3>h-q}Pcfsn?2CX7;xlk6O zAk>)M>3@Jf99RN%r)DYkUN>N-fT|uklc`UDWn8<fLDJQT*zpLxB|!)eTkNQv#ePgs z8l+Z-*#u)iqoX|}{<_K9c+9CcCi^Tfpe6JB3t+OoE;Ac5+X7A$6_^UghMWf4UlGHE zy_hr6iEgZnGdP;;P9N$HgURGlUOwlmKX+O<anSO;*Ga#E<)CmAqg5GjnzU)04xn+^ zi`1?3KDG4JTGg2N)|it62JnSHHw{0~q==TDm90y`AIiM`E}hxh(AZ#d>dgH-fO)XS zW}d+i2V^bD*i5gvRggm11N5fUXkLU_hI|ONW!R4kM)|0X^G1*g%Z<9;87CTZb#75O ziblCkA2kZTh5$BsXol^j&9(o-fsq>!g~8U%htsLSUp-}^mPfj*i1ipT8DaAnGsx^u zv{28HCLXjF6SNTn%rp<JsU#&Riga*9^<h;liD7_H-VWL<2}f;d{cRfS97H=>+Yd_^ zjVT^UN#tQgLFU1ffZc1YqQ0g#W9o`y&l4<Q#kK;^>GyS9I3}jd?ngszY<%I7bq!u5 zk8^g=^0}5^{j*;G{y@Nj@su!TZ)Xk}v53My;1DGb7J}4ESnk|^=H?mM;d1JKOR%TK zrLG>aer%?f`<k6Tv$fHvzjQ&gw_xiVtZe*=@C5dmJ5D*(s^wHPkzAOJ1v52THBIbd zfVLn1`{+a+Gz7rDrvXY#ZE9^q2~89X>0<S|)+$4K<70Pa#Fr)#g@FSQI@*%D^sw6z z*@u=5EQ==uXP}nM)nFoP)z#Vt(jLsU>jI7SHeq;3rwFYm{B!}I1D#aiiAKw5iT6)o zoD8FR(Dn9^%@%~!oFy>$>5M^d@d!PuaSYIyYUJ9+TA5>W0#{peEF`tdlM=@OTCBzA z!(^E?_Egy9mcctOw@VkXy8Cy*dv+gqAKWo+LF1>QmLc>Cs|j4KipUs=6c&nIsoX18 z#pp_sIB1mVp5nApmXQ?<dQ~`0!Gn%R<#P(4Lz6MGM%AziDn$dOC>}_B_?C)>KQX&) z^)>a9TYzRXuGTqvXPqrE&#1p8Tq7pCYW0I1DVX{C>N{f=sm3!jo^$Mrq<t8QJb10m zgsE@ZRLL-Huzu+`BYm@7UW;UHvGh>cAvv#(52Ss;`YwOSDh6u<*!4&3tFhZ2t34bE zIXj2w*bH++V#rf#3$%om`Y_3?tCJc8%zk*YXo&>8U5`C^$RBKW8Kst?u*+g>@A8Fo zMn|*TYKtVe(NbrRL=v@bK8=w<Q#6GgMcYlSZ8LS5#CN`6vV2B1x7MLe(_-xv^q2I( z=p!p%ePK#!s(0vxW}~-GS7QrZJBo64%+O{vM^XRSU5T(03lV9z3}^Ye__+8wPBSvx zexwhnr?2h>g5HjnKw3qE1VpmHr|K8L&Su(}iRRZwwXbG{*Z|>cWfpueTuqPFXDY%l z5lOX{)&i!aM1xW=$(=K<cw@(-mH_szv<g;xOLqc${)zU3T}Q0kDY!cHSZf|`Y;a0m zzvr4KU>x)qeFLYB_3ojzzJ$wscKT}P)DZ`)(cIjG+1@dOC(vZEM9o(0BkoJz$Y39G zj67mcSRV+4-ENz{1!JG0!|K)<dVLLgYl9g!-)*)!B{CC5`_*U-+IjHBv(s+$q;)l8 zJ~BQx_gVyMouS@m!hf=5bpGjdLJZK*>4ZJlV%Lv*Sx}bgs^TdR!s{cv%0=}6UCSDt z;Wj#{m9QuXT=aI4{Qz;%C1NaAVb=k@VE(e^!>}`Y<@amspGCXaC$5QqEY(Z%(lgSF z(jDo4bZ2$1=oS694T_;u^KE03@x|Iy?U%8m#39rB<~J<=Vf`K3ls)8dIIVS-y6@CK z<F4`WhJR}cH@)k9qGjFpFRkBbdy*gZ-;l%YO~KLNzlZ#xrO;EM4?8~7@%_%p&cEn7 z6>bXuengC%R$7$B=y>$6VlA=Px&z&R6wk!}w5Pe}`QFpL|J`>pVZtw+cq;K>|7QOW z2Tl##8GJc8n_Nm>Pd)`sh)>*KyM2D}G5g4WF|+iq#NVA;U-|iti?J*}tmJ<TJhIBD zTpzGr0<^=gK7e}`TKpk)y*dv$eSnkV{+4|X_bLW`JEc#vB;0Fo2jHF;=UEr-Fzx}| z3hp-CR^0Wt<G9H`irdG^Xp5IwQk-BO;Ts5RmicrpHUt~3L(&gen`CE)#W$E!`aO17 zTEIfKFETDZQ+{7^GnZ7yT6J$Suk<EsmcGj@()XBObKk&sZ?I#M2kru^LpV*k?*nI^ zap?{Vh<{!FIYyS+aofZh3^|=<b8t@z-(`=8N6Tf5;>-(AGFe<;PVqd;30QP494Wtx z_W@y^ElXcz4)Ix7UKH3x@l3fa9>-wb-+@0fX30L!BH~e2E1qFV={YtC85!|BBD`LH zA97=mVL$F+ahi>x6&w^k%Yu+Y8sGV)zXxw`GAHr?6BOW*q9X0YAD8{?l<rNW(@%AG zv=qgQ-D?r{em@fb9Ht^n+=k28GZj~OoAqEk4VSTse3P;F9PFp>Rv+2ixy&--jK5cw zcsa>VK?mOAjD7J@<O<<uSU17&78Gc?gmMa51km-{@FA!LM%y>wHZuo&Oe_HFvi<BJ zmPkL!euX{Ho8HmCQ~MsK4}ill>P_#)*jdaXJkJ~75#BMr_rG}gPWkQfTjhT$|FHaz z<$ox@^!5SYBfb-k#cEvm^g)+^<18!0z$GqsG<OFXFJXp`CqoU4Q_L33BRZUQuxs@+ zU_V<gX!o(!Htr9|?ZHq-XID6)L}T6Yp5DF$`npLrG|bq(k^Kh_j*g|$<C%%cLsN&R zj~tzuotr<laC|40Uy^%bH=L7n^4urBRHks1oycZ#oKL^PZ1dBqVc}F(O?cJLT>c#2 zK9N<$VBybjejQs{k{7*xzshndOUvW8Aii`y6;%aA<@s|_RaE#gS6`b`b)i$YIt5cY zvy?e@I_sDH-t8<`=jO70bu{PYYLcL2F2_q1q{6b=39srCSL5_PPMELFWjTo0E^yT} zm(9b6(>oJE0|X7^y?JcL<yD19E+?yOE?dmyqN=3u3|Do*0tnKl=d!9^PN@bt1@d#M zkdLZ5MFw4bxujoAae7mUoc`l|W=WO8e)y;PHs40rrMNx_w&t_>Id9=uE-UBoc61?& zS6*VO8go?DD{4(Tatq3&iN=6OIVD5Ha;l(;i|15f3E`{ya8#{PI1y=1FTJ8;i$DRH zqxl@6<i|B4jmoVWGfQVuVgIhMYn8piH&sFrA|N3Rj`KXTEf**uG)XZpNmJ!s5V=F8 zDh1`jcqIz+Lq1pA@fAMaW$oUNETl7X>Xz9gWwL&+?9YY$QPrxHL@}c-7sjKiO#uU( ztCsX(;tPPB%Bfa*I)*1Jo}#K9!8kOLa7bVYWUIDxo^R*5YJ<?CszaGRo-OH?$8+tf zwJ6_+s!nBkK0Cco@$~xPU$6PsDJ5o4pU9T%cBEe+rP?E;q)^>d$wL3o`b(*T2Pq*1 z=dvZzTnH<*jpU_h!+sf`?ZDp3d-?>rPj7SJWD@jG=8=edlIkG|Si*R=3=yYQcHkB| z2AVct3p5;zoH?FV?Q)7|R5O&)goyJgo=5aAIUJ}_SSq!hFVz_$>eYxh05Q41TYV&| zx|NbZt_N}^w?QdM<TffL9l1?PNl$LGQZkV1RZ2DFwkRbdxjv;-OKz9KW2$gAs)jY- zQ&Bad0UJ@ZRbi?%@(a+|1{zxtHV2L5`avVP0nkXU3>wL82aV(gK_j^#&`53vXe752 zG?J?*d_>CvQ3axL<awIJMCn<8t16^#F-46=)F^UNH<Wl1+V;S#Bo~r06=VM|22?4k z#&?rP@TlG4l3s9UvM7cq5qj>G%tv4MD!gB#w-0zi=A$_p1;qo&BY)=krdD2$ACQx! zUcn8H65t;MKk$=06uXd&s{Kl=VI-;!d>ks2a0%#xND}4=@))0_%nMOYZEsJ?lPD~* zDArLdB1aFvPR@n+lPG08$RPOBP}JIVq_`cEIUm_ZSVMPT^H?PeRfjNv#nn9J_tE+6 zZIMgdds_@i&AAknfku?3hz~K!6Uco?&AnL(`Cf=r3VSh~UzSxVU06mjB&G{q!1)~V z?)~2sKoSaOd7_Z?%7|eCe8JITL3j^{1cXpwsY3w(+3TTDDBAC%1R)miaZn=y|54LW zDR(z6Xv8qdfFpK&s44?_1ftoed8<ZbaLy;>Ns5e8YeW+nF;JB#JD!d45!8)Dan%!n z-4(eZh^HyM+O2&n3H4Fs%FET10gX|0f0cstPSWJ5dcL2<ouoXV$UH_;n?S)ll8cpM zf(se&;I21P^RD;kuD6E0^XVR(F-1*AcHZm>G^MCRk!|P@Da$sp<wqh$vc%LF;!kVh zA_d%$E-9CcToFSisn9r%LIY*^uBPtr`|+wc`5?vkMYNCPM&-+hob>wdNpF9yN@IqE zu_M$8;@?k_ClafC?6N!wmfe-iiP|3dus)_HkQWa<#D5B9uHdRy`+;*<Q3v5nlhiVh zE1y8Mwj-w_3aO(y4Y3|oZZS3vU<QCdz^rmh(0t|qXg>3VlfkQFghRjr;Sg|~a0oa- zIAZ`#5)J{65Do!Z!XY3>IB5W<2#0{vghRj?!Xe-+;Y<K{lyC@mjBp6Z6Al3d!bt&G zBpd>k2#0`W!XcojsKdKrK1UDg0eGL+po4%`G<|~iC>~Z7b>A-b1$xl1FKQ5BuW1mW zUQ*PNUDS1Y&`>XH5TRbtAVU3=qVC^C-Jk~zbyI@~bxVT?^{R3kXAA8#o>CFjSX8C< zxf?rGPLwe;#{cFR8Y;rGD48qG?<*x{NZo1_SeSs@m~%B&Ah$t-YErLKzl+tv*O+>h zjaFcZjhEVmr{~eSd^%f_md8sWdV1A(6UUW}KD~q{7EsWL&fT`+9NO?JLis6G_Zz4! z#!LF;ajj~)|KA37D#0cGA*>c&5bl(JRPGjG#vs0c=NE;ycAmA*Jc12`RIYhA6&Q9v z7>TaHFu+BBuL4Wx1Mc+PbT9;avg)q~{&Q8>aF4%L=x2XW^|!HR^t~(LIPMAGsfCXo zCpN5M?CZkMsP_pNyI+c0PZY3-e)bDhe~3f;Ru$H<cJYHMtj8Dt&Z5=6HQeKm32`Y| z^|!Hv{_9oPaZmV83O{}M%HtcW=U29P=TaB%jmL+gz44wnA1`jMo?qt+ORL58rJ};8 z)|X<Y`;mKa?sdE;$){He>wIjfuw1<K_;lfTG5grWM&aVt)s58UwdIM+>su3-H_jJ% zZ!FG-`5uHQVSLo*WN~A2_3}E8$KtWX{jpX_R31np?;N>){d(+DVQb|=;YJMc9_ac+ zbXPyT2v!OePAVTOrt7O)EBsh-v$%1sxXg(iK2x|<+{;MJWIDdGx><R>aQWQU^}<Gx z!((lAskpw053jB-7dQCUN|7&29pSTAit82RBNYUN?=amH>xoqYtA5K1*9xm^g~hcZ z*Jvs5iLrTJ*cvviY;9c`?(W`P+E~4^wHe!7T_d8qXD5z4{0&QEcm6Bvapad(cAl-^ zR5s2!vExz~++G&PZwPA$3F^Vkv1Y4?RVswLj;94+uEJ*>ZV@AAoK0zP3}Yo97jh3v z?1r4gm(z&7poKby&<YqGDzZzUY#Q)!z*$(OOn^>|N`cC&fKs4+4R{l9*WpL*1}G~c zv|g-&BYKCmkRKL8bT^EL$8s0vBtqWMxTp9y!lyW5;N#<X-77_s(<hZ9rN#(GZs`|; zJPP1*g<XJq1J4zH4<NOEdGg*H<Hc&K6!xUay)i3@`#Q9M=;E+=CjB5eUBiDGecMSL zj+xm4IH1&dxU@-gOi1q)gt)oq>jiLo4zXNEDA<Pz?<yE;a7p{twRkDUtAI&+HUJY< zgu4LEJOXzX+D^Fl(sg7{2$VYar*scwP4~T&?Twd%s%yAcp^*jD5NlejcjZ#hVjM%A zLYQ07OA{#D!tV;;Zu~aE1?9pk@Y_UOl*`t3)3_UyPk@78<QuV4xs^YQT=Af55uTWf zK&^l??6Gf}31`<@P(RtQ0@=ZwsLkq`3$_a$*ex{T9Mxu49bE)30oKlf82Rjgo_1lS zV+5r@6jXO3XY?SxKID#mSV0alj5vbRebDCpSnD>*#*hax;As*T8B^>qbm9ms@Mgg0 z9Hev%DRvwyVot&WH4FQmQy6nS!_HzP^jqv@Or4R*l0iF&6_%`mS+LYxU0;pI$KvE> n`r<uRx3}u{Roz6@?XSAYsyn2)6BFZAH&b;dG}&OyDzpC|C`*Y5 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Bold.woff b/docs/katex/fonts/KaTeX_Fraktur-Bold.woff deleted file mode 100644 index 189fea5e4ff5d5d66f2793d6753f702590add053..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22632 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%0APg>lfK9+NEbfec22@_xp#;D&6-v*%$^l@Z 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 M000310ssF14|n8Ps{jB1 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 b/docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 deleted file mode 100644 index dc3bd4c040abd23afb59d2fe385f80e23b0cff41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20360 zcmV)0K+eB+Pew8T0RR9108fYj4gdfE0E@5y08cIe0RR9100000000000000000000 z00006U;u*}2s#Ou7ZC^wf#Pt1xIzIo0we>22n&Hm00bZfjbsOcJ`8~h8^UxIY+QwR zJ3vrW_WOvUWT|?9|34>{LrgkPS_U^rknGH$WGFi&87YjndU*~@$h9#G9VR=}8G?A> zK%6XAyissmZT|1mt>!l!i~%{id|BLC&qw=3Um#Y}rYTnJX~=ZTwA-BU)5+jNMz#Gb zBP5v}BHe0dcGJqHR7w&c5C|m+30+bu^w7JY(geYV2#Q!yu#}GV)Vq6IfAu`EoL;l^ zyYq_m4*&Vw{QtF25-eDub{k5uQZY;_2bbKE)`6#@gNgVz8QPJXwKIrl3<$OmwWE7E zEvjC5zSxi7*}Lxxp-^dNl_C8hi6y)-M23<HRXm%fEH)i)FgxEnwMKg^ZSQL!$pXoi z+bnUq%xZlMpZq!Vt9kb~U^_szOo2@=ab?R;;J9^KOPT}!|8<J@KKhazJ5gfCE)iPQ zaSG(74!eLlic{u|hly~;VQ&5l1ZcKQ&zIU(vtP!9StlL^wJM$v7^2q1HR2u`<!W4| z9o^VJNZ<plixlnFnpOX^aFK^w0(6}m00e-=Z2wwOdO|as{bF3iz9?r=fhasH>J&9y zHC!A%=-#~5zxvCib+Ld3W9bC<bVj$NcCKIn%W?kio!Z}f_HX8w3`rcYEXfKqeRXPc zfdZ`qBhB`tu>1hX{%w9OJ6^!lp(RL)%OF_~!5a}Gs_VD^Gu3QoK=BxyqzpMy1r)lA zy^c;*;ij4W8?-Y6kc(N;#?C4%7QB1Fl6;R?kdm<A1%|UhNf%S^7(3kJ5*NC()H#5T zp$gq~j#YfU%fdzJ(tYc;bLqDA>ka>W`?Y%C`?EGJ?E)#$+Mo3SPbBe*cav5*oGu;` zku8zXBNgAt-e_$SJDukHGJu0K8LHKp?an7Q2%&Kj{BXZdiNN|LUy}e{_xA#sR}VhZ z6hzeP$!^#M{Km%XHw)6Pl%|l;xA%Qe*+k0EV?WA(EPO=~fM00?B7-h)<Q{8wA;=)7 z>n_aWjs#n_W8xDdPP_ueI;}q(^Xtm5n|_<op~jWl51AAxs~`PZzpnrGpZ<=1^OGMv z^3V_NJN$WYLe8>k9|O2K16GkjBOv3#Lx3)X_Y>D((Ev!}Tc!Q4BgBIzuPrjU!pGN7 z=^vm93{nS&gobIfx^TTAA`*!@h#)bC;>N@qbNNECRIXHO^+vPR?sR+o!4S)2*7ERW zpCLw@H9}qid^kV9x9-j_Lc@o^N4fp`>c`ip^NiRAq53uePyfMs_msN(PoA+g*B{~x zS@mR|{u^MxWUM12LTE%mn*oGEbe2SV-e;DKn1KKOpqM`m>n5umw{JdsQ89T0c3*iR z4+cy<noB(dl!E0n;d)wMLok^T3zbIyUVs_$n<+#l8neu^otYkG5-4HCpgcp0r=&#Q zocbLt1)>E-PZRbez4FUTj=I&^hy>ERrAQSHV^X9~hpid|a8JRIw@l$CwQoSkp8ucc zqlBt+iutvVuS|Lqyat5B-};l8j_IwFwOk_g+8@BggLI~TdJ00SiR^Pm^lO(W-L}*d zjMmE<{}VBWDPPHBSnSvwpUc2O-$tQzdm?aTT&aM(iIPL1HIJgl%zXBLU?MF6MWQ4= zvnH1jVbf9hrkP8TNfa6A#8~r$%+RUMgWM_?UW`iElXaejg_R0Uk!4FOw*`>U6B0@g zBo}B|gZtRxS=xAUyI9V{KM|Ac|L7f4M7}buAf%n_eEQB8@w*8dZ99MThdGm-dF`LR zeJL8*1J0uzEU@XlY4aka4@X;sP6f<eF~4HVd8C<wg5RtN(4v@j^P6bexrw&!dXJ|~ z8CYrC@4Exd>S-Be<rH<DM~Pn+U`ZR2h{J)*-rjd|u7DNqJZ;ILrR+7+lt}LmaU>tV z?Spw@Mc(DB;;2zRvTtq#%UKpU_bumG=*qkq3l><k$PG&@S%+dhE*n_3arXvMkzT0S z<hR)3??Jmc$8PYc6}ja}2fSwZEa10Dz!Iz05wxB)8(6n-ust~Cot=G=_IK25<XR=k zh4Qw|&F>O1h&{MP_p(Qzhc?zV9V(QaztobEaLt8O^fb-5cPo-jTYoEblE=f+SznP# zgt*T$jbiVIMMIJl46MD)$NVqc{OL63dRQXn?ODc%Q}jHSh=Tk9S;kqdomwV}TcoPe za(<G9VSW=8OTus6)yMhGUUiV*M+O>6Ve=tjFkRlWOp%zU=teEpnMUnT^KJr8a|&`? zdI}XCpJH+)C3xlYiTt(y$qtKC^ONPpI5-@fH3->XqoiGw3(B2X#3BvwJ21tGQc#+R zk|-v#Sj3@K>y?s1HWRaU>Ch+*>d4$xTe{zfQ(h6z6d#bkBYCo`7L*iouedWZ>`_Cy z+qGwg2W5saPiR`oawzHPVQC;+rXjhy*$rqAG{*L~sh!ZJ+Ki#EaH?Xm#tQ>KhS-Zb z&un-cfi-2VX~nvBSTbdH@C6b#-dPOMt9H{+r*+7PRgx=pZ+v!PsLHOn`=z0y8@;*! zud#NykDWVUDhQkTs+W%J$%4V$Urv>)qo|6z@X3Oe;cS?pr#OWa4oP~&gJSsH)y^FM zA$kjhE&#@S_^BQm(fPClK3Pd1h72<0Y0)!jTcz8$lAm)>F~AqirP^@p_O4W`wSxjm z6d7M;9Y>QcYCId}TSbK~`#)qQng=FH07_WbIv9Y418SS-M1s1u+$*Qn{j@NM1PyIj zU3BG}Y2i2$G_qxFQSFnoa2g3Zv*q(eoiEbDWhCgzmai8%kJG{=5;V2#+;YH-sPnBW z7AQLAENT=_?II#rWTb8`rUqj}4D}&qs^K6b<K|*N86RSq5MpTzu{71<Xby3-gg9D5 z+_8!3gA@!VPy_RQoLsdZ^-Y@;R~`ut06;U!<|dY_pGBUT19;vA6kCy-3AGT(1298K zlMCoNyDzpZU`twavTScle{Ju0I{jYR9-V&e-p5&`?*wOQ`5Pd{FfNVFE@E-n3ZH1P zoI+tp_#ryGKOUpyNrMI4ATFJb`!E<9diO+*M|n-#Y^P>WX$Ww3;Pp3`H%|-gUm>n+ znKo9_rI+_EE#-;XA<|f3s4Ylniud8<Gea4wY^i!thNm={?=Q&Bjc`TtN`+(dqq$z* zX{q)&e%6`>?B;(!BhWC<H%>d>uz${WspAqb5d;iC$o_PJ-KO4e+#tGjnRAdRXrImy zscCf?HJ5NG9|m|on*BN-^oigCN<ilhsLfat=KCPFG@cN7ibW?b5%s+aQfuov{vq`# zHHd93G|s$uJY70pKqI8uIu)=01L7gIuG6@754%Nl15B5BT~6?XhSc-ktC@Epp-5tl zSg8Xg|FG{<(l9UkHsO%SN>l>evfVTQB@$Q8wfdu(XEAM}%Zz6I>ptz&O@T_v=M=OE zIdn$aAYOKCr}*PJqC4`?#LCl>pr)O(*r??I5pb?O&Jj$Q;F32BIl)w406J(}tJnwX z&mM;y6Kuw}?{eJ$gB@U%_Z<61k=J!D?QLOyqXld6i0DD7UrK63s4uZ-ojmq0=pIdx z-Cn!dxN+sy*Z)sD?(^I~-2kUw_joR?q+mQz(V?nHQ5L9gY4r3g4$@Ho!a|q&qtqfI zHu|ePqJ|}jpA(l{(g-AMcU!E9LrN`mCuK0OXEW+lhxF>}>`fviT37@oMr-O&L2g+D zef^$L|C?4$AAX$KK|f<#Um3?}9wFHl5!=tJwuoEFO5*8s8I&7AI)H{=Squj$Hc(X} zuZKv2)cfnVi@Z5=gEZFfi`@4)pm)-Tq39YsW^6!1D6zq=5s3#DG2Cqxjj|XK6V{R1 zf;g6rbg-a$;u{W!IdHFU1F^;^;AnYBy>z}+ESS1+Ngp(huGdFM|J(c>2x4A~Rffw- zUs`OKg7lphsKa7EAnG4HgQSU6dFRfJ{&Dp)0`PRHOZ!e~WR*qo7=Y(gqGoJC)D-wF zv#N#dt8a_2Xm8855(UvsqZtCWR!)ePhse~fCRnwF3hQfAH)xZF$hbzOc^1V}izv=q z;I@`n>CffM?0NMbRr@{-l${_g$3(|3YTd!%+5N6`m*`Z7qZQ<M+l<v0cTW(2rRF~M zb#{a0-i%uHY40Z3Bk$TDo6p7eggmp=4b~Y!Nka-LX<E<o4t=r*7O;3xv>3)0jZ{{Z zFgYb&u=!)L&c4rzV$x`Dzk*$M*G@K~`4__-q-3arD?zzrja8#olt4YMAW2=CIBj_m z`4VdEn{eLduXz`q2@~S<X{LNUEcS@f`wA;rG>$RsxKoPgW5CPyO;74l?kk8DLB^+` zGC53Cyj(R#?Ebu2qGk$_DxjLtJ&TOCSQJIL6qoAPC6OF8ERaCxBQT!YqrU!ghsVn! z>$apeHSMISl%bP`em6@fAMO)nu6UfJR4&GZ=<Curv7@DqkYklkO$wyo%E|yz!&@pM z>LH%Y#=A(B$Ld2JIf_Jv6KGpXqkJCW`-4N-4v`g&3tqmTd3MpyAy^uKeT7mt&DE$S zms+;Jy$>FFp#fy2aWSqtaf&6Bw}MDUlm<T1#11Ykk(5wkFU>pmOfEqaVoD!L?iuZI zDJkf$Cmq35`s$e~dCsa^zh-?Rb)-Nbu*FiFkt%@^)uwHccIR}W<7KBpBCBdajEyD^ zFSXS$T5ZM2#oA<^Ak>cujme2YTp^KCKFY(~Wfy}^nr~miXmv%P2^cF*r&`%-la{(Z zDUp}TE%h}R%XXQ+k=eIfeV-(u-h7s{qQwLQ)LoCG&ptzY1tdOZwqJwav&Sac865F2 ziJcvXSHZwER?Uv&gVUP&Y9y-;C*bBhzcxa|igzKQqY9b2s8uG@iK_&_;_%8ea1Y$; z1}iVOULu^MWC3?(@|Uaz@ZoYvos$O@$%67pyhHRpxEaRbkDs|#`bf%91ERF}xmfGA z_o*Fl2L+U#y^fvL*iw*hw8*l1NomUaeLJo<bx~gBWHPTgl0HZ98VH||vG#B_Sy8gV zmd-?Hj6q7B>?vMReBd91jG9GP{_g_=oFz*Cdbg`iHp*6z++7)Ii3Ei6L9TOgXY0BH z-DM(RiVm)LxDQIKP0ME*Yv9U6d(Rn;7+H__*7`cyQ%PEotUj@;A|ba`m2PTrY!T_t zg+!_f&G!Z+a3V3Bfn2Ye7j0D*^QJTceo*=O<gRd2@k*^}SCe3(AP&8Z=!h5>SCr7{ zf)d_Tdy+S8(9u_^r*<{-@dgJIQrhHrK-mA4LFmBJjeJ6M4i_4iW9mZ9&M3r$Nj&7f ztgiK5^;QDuHV%Vc^Pz{Nxm!ouWK`(KN?>{~z77VyE{_9AL#B7IlGsq53o8=YaJ&a! zeuLU+BSR|(&kW&XWooLqkTVCtl-LR$HO$twc|y=Sbf!-fj5ouOd0!dz&B48GlbevX z7)a6wJa;M$*$a3HAz`&1t?Z7MWmV@xqQxL>_+X&W*D>iTQ6|jd*G7^|m43ZNq-K!p z^4Y`rq8h6Fe8rW+0>tISUKl3*O^ct?kYCQ#9;6cuy8bXrtTB;y#mh2*P|}L=86oa5 zbwcW!uWddgnW%WJ#Ew(PL^VTQ=z_;FE(P5XL;=wbl3e|D5LhS$4A1xp!>tT9FsSA7 z#a**baWz|FeMlN)Ic1Pl3)Zj(5+O$5Vbjn(@+A~Ab?T<3t{@Hi+_RbL1{00=*xigb z(SnaLnLMK^QWd#dH|IQx;q2&rbhd%ZZhX%`^G!MBz>QRul|Ve5m1(g21c!H-A5`D8 zOv;)c0eK7-p$@KymQEoJxG?W9n3ib|b~{6EwHo<|q#o9fH>>P$sIVDz{H5l^$U<gq z8836tN-_~mkMjZ+7)&RcfV*gO69xq<PtmH*eS?f@8Ve~ROswn(MMTC&VDKQh<8Ed{ zKgvh?S<;?*4gDFn!CU}YP5GHQnTD=3J;4(7-Km)l1kPT0i1Ax*5i_Nc#LltMLo!}H zHl^a=EDiB(_dJ)8p1TgnE<*r6t|#kMM>l#l#%$%Qpx$liHc(@to(MYcd#yH_1$Tt) z6;fB0NQg|usEwpp5~aRR61$d<Vt_choesIBz{*^=>;dY^2HbfcD;x|4+#@PCETrX* z(R4G=#%Pwv&JDlG<I0|6cSPr}O03rtN>~bM>rA^STEFo57UMH_J(q?eN-y^Iz)jwy zK1?VFFD7XhXXh}yhPENmA<9>U{qXOg=#~{#TQFIdVKLN8D7;y*(D}&SX53gS6Xn76 zz}8B*50fQ%ag&}w$qx+l5FISXw{5v*0?K{=!m&AQVUM~@h*7q7;A@<gO5E}(nhzu~ zhof5Ls=v*VcBT$gA<e!e4b#?;lGJ^a$p59%6uGA0gOpLKxjsBbo)c<l<?F$hNOZVv zItW#*5;%#-+>8a}_*Q_aGc#G=OR>~G^l;OYwKcPTJ4p~)Du-|<b2|G600R<hU`cjP zr=g6d+Ub5%he#zq=XH`|@$;lap2}bIExbgSWM}AX*N8%lM7oKiL<3NOw3rK1C_IBy zgRH9FnUtd-#p0o#3AQvOr<?{X*b%LFoy}gq76CiadQ-5i{1Fo`)2*miMJBpraixbu z^fyZBrr7YZ#E1$wFvSHJ&!*EJ`}5E32_nCq>oLy&mtfHHP-BVGr4C1hT7z8q2A7r= zi=_YS_ap>1tiJh#8ePB3Qyc?-x@oR6myNnSto638mxoiMrq?RSaRHzwZu6XZVP8Sj zmiih<X2gu1GWtvgiUYUH<ppo?yvn2P1xt(e?C{lIrWff@Bxk7)D2X3`Ea{VbFq`HM zem;ME`)DqN>PoH)H?`m3G@Tp>q{=Fk5`ki`t&>GIbA>2<E$u>aMRewRI_(NEW~EWx zbIkE+$Unip*|n<I0~9fx8xrpQCbBY}ze??v<A|G+yLD@qA<_k<mepr2HOy;}&b!`5 zqO%d_VqHrju7uC0QFAx$!h%JXz(xkXqC$>eIxD>h6L)as*8(PMDA(0?aHa262sqkN zI+8CgzV;gD-CPnU<F9QF&wIVSLDrcwk9f|pvdLkFl5LP8(oMC8(xt%&(O;P(UB}F~ zZCO77pd^ob)mA%}DBz5=lUprTut6HSa!uCf$?s`Aj6xVKxJb^XhV9n~l0!>7AK~;= zuO~up-qgv8*4n8}L}bh1u&x0nQa+`XEF5l@hsnjnns54K)h@>+?e_Gclxh)gx-y;y zRI@;AKU{<_rVv8gp$m&U$o*AeM}J&YM3OjWGzIyAxuLAq76E=#+L1i#?ahnh69T*v z8ZlEOA<c;Ic`G|ahvsx=Q%;aZ`3u8T46A9LAe8fJP_^5CzoNcf4Y>pCXw3eOQ*$@} zdC(WO<rtGoP2;yiZ>Q#wbiVWF;2-erHUCflVX~^oG}HbUA_s2Ht7m~Rv|R%u=nKy? z-oU?*#g^YeL=5-Ek3*tZSRF>k(;+XGo~8PTu<YQnnXl6g;gZ7I8r+q(qQ;~n+NjMw z2g*RLF{+X?uKMy*tMP}j1Z$IZe=&cU+P;-nuZq0bV?K=ol;+T?{P$`R9pt)jTPLB7 zvl2GYzS5SmBF}zwNJk$hX?)9@+dz^=#+P9vlDakM5!%g*9JjF`(aghhEM<}`j}8!u zSfQ7p!C7f{3n7#t0|dxIiS~=}kBv?Xh%O|m04N8j8B%vPW!;oM6Tv=Iyp_dsD}y?5 zDoh;W8?6>g2I!hxh-IA0$ZubnamR+~?LoN)HtHv8iFt$x*>66Uyk`dJ`|jP(NK<zl zj<eaQMW%I4<4tN)^2XvR)fbj(<2^FADs?@cuhZ$8Ze62BU0OgW0l45iong>T35#>s z-R67EHT~ns7ZFQzWuazg3eG>0<YA^+v(v<&MCUwiZ8fpsDOAAd_CCLUnRV#GeZ-W! z@y_{zja5kjQm2+&*F}K(T|Fj<mX)Y{UQ6x7^5U%Ba=|h;GH#Wm27P<HfYXDMllTG5 z4sc(8B@KpA`E_ia=cXq)Wszzu{$SKdnKaEID-^C9^st(7jWmsyKr;U6$B>s_ll%O5 z!#bJoWwBqBTo$D7(++q_M_A~yMnFnW5c9C4H!rU2WZbIwbAUkT7am!S0kPhIUDhKm z`FRi%s_>j6EE~{})5?Ipn1^d|7^xRew5s`4ZKP#CudhqT%0^Xtlx7d!H<KV;92mGa zumpK{rx~}MWP?jr8$HlvWQ)mrD&Az?OD{^%^TVGi7a_!+2SmWp?C;YYpe?9b49!Wz z^^*q}HoCL&nL!9QH4)>E7Yc#csp7>UW`a<2*xe@y@|f&$ktUrd9w(c~5EJ34;Lx*% z5gnmEgidS&I{qNV9xLFTNhMd2QT8DAER%PuED*Z>S#y>*T5QJD_16=~alrxxh_S+9 z**b=Oqu<2+;Y?n<&%iR|vBw(L$9R}kc%zv&hn2wLm%-5Zs_UO|qif87N!`;%%|*ZW z&`lT3Qq%X{d{&**h>jV_OjA%|n#E>YMl*MFR?_H)_4tY#2*P4PK60yP)L+RqjrTAN zxHaSm6MmHoR{BV~X@Fwx`mBVaE${@!Fp^@$;NA=dr`+oMms29&^}*$VI7_uMi*e^} zgJvk$uEOb<k@0*HUC*I~1qmw%zzC=YX^elC=dnL7zcA{D>yl|=E74gMQE8!OzeKz6 z(wpXyB>A2p&AQ}}#zKMS>LHa4Nm=K!)P?W%e7i~UiLY%!P$RJL@SJK3tW{@@`qh?j zU8O2t=XHd)((ZUgVC<7{#>-sFtJ*_yA9^4e97xjcojI@tc^I^t5wXL=_hJn%A4$v& zM3Y}s&kBX1XgJ?Da<zNSSSyh#UkT+|27^r)4)z?(J65qM&T;{W;FW+{P63&5vfaF! zc|kr}sFEUjb9v?Ja&+!xb+;4^ofKXI%M)`j!i=BI74KmlOIRO$#Qk}{0J1%S+?;Ug zeDZY~f_3;EfPOV#PzhD-WpFNc-st^!h{#p8Xl6~T+bLIOp@2OqEu^!4IHyQTKMTqQ z`2n>(hKjcv0V!6fN)fLPTPKvqcScTo9>Jv@rRFqk#KIxq^(?bRL%Zv_+)QGSW<c!J z<R#Nsoa%nun!(jWpalPW^FhGYVghvUh1}s7OIuHZ%OtB^bO>1;V+n?DMAGHO22+BS zj9D{Y0+jAdD1N5mAV|y6LMYUyB48r_WE{6rbm>;z^f(MZA=^XJR%5)@4_rw{=*6yA z1#vd1?iC;3ApK4@Pc}6Z%J4fZuC!_94LEJx1ZEfX?yj7FV-TQ!fW?-|iPN@T!zyD_ zCwG4*C0G&a!;Ax@0<`OMxxKv_Y_#?m#$>9ybIexDVtF!GOE|Hzl)u=q^c}_}kxa+4 z@MrO^Wree?^K&c+Mw6jWay1(DZ*l~eC5Pu$yU*O@zjwv9vP5;zHam6k_|Nrkx3D*o zm4@+#B`l04(Fe2Y!8VrWy#^qyRx#ufVl0DEK$hOzFagn+s})igmD4z1?7h!^6H5+- znq+5VC~RvoOrXp_%K!Z=SDLFBGiTnh%wtm5+NPGo4kYhTGstHV(X^ut7@vQxJK7RA zVAaa*fIHzCgg|2}D#&z9YwO3@KUz7SdvlwS6HlVBX6TbFVD<-zy|)!#t`w5uz(EaW zgEjTwIR%AHxv9mcRM5lvCBKgJ$<KoS>QD@uA!SIdAChFG>ooK0E{aQ%mtt$cT@Fhk zU{jA_Ty(a>O9_){yLOkpm>*dhck|hty7!QFvH_KDhy@o(;-yY)YRk4rz2u<Du!vwC znjhh_i4M3!d}K8qhVj{CEo6m0|Af8uSc#o*`f_v-9KWf9jaDw&Lwn?E<m@8e&Zk5( z2z+TaJ-iSpCF;&_V&cNgPhQVI#6!!8o<{>WH(L(#VOI%uIj`7C=+3U3F4LbTpeb)t zQ-Y^0_GYlyV^K_SscK?b`g@nMPWZ%I>1d%L;a7Xb>~8w7|Jc;O2T{rn3CEp)s!cAE zPn@k!Y*C2HK(4s4(L=&6QXg+SjsRDO1{HqUmcL`CWD%V=E}yM+Q)@tf9wrrC%*^3x z-8KdX9JAOeIQRe8ji-^~zkcfJJ>C##$&(tCQ)NDLctUS7Cb-Byg<)Nbh>DdAm8eT1 zou+M;dQ>!oAK{NiF(*+hCmFO1_Bc=R`%2<Kl|&dITv{y#)M{GUemW@aWOp`(<0Ze# zEP%iU4pLQ0z7DjfGNeK(>g9+82Mz%8?lb1d%)C^a7H>J2l&s0`$-*rrsdKc?5wH(< z*>y!%elM9;B)<zd>l3uGwu++Dsq#@%Fn-(^<pyGrk(B}XRXqf@fS_Fao1|atTjH;6 ze4yC7^maPTP3`P(3r=S#a`Ixbv(G^2&iLcY|6ZjIvAZ}vzElEU&ng2HDv?$~8_P|3 zxDik&6*+M>?hbN7(HmXEqkFGrlSn9BoW%Uv+jN*$1ZT4j_>C7>lCp@kx2X_;G7xdl zZ3q|;R?S)?so|s-&d6kLAd}=h6+N5=X7SKGPCjq^-P6Ow@V93*@)7fV`p5;Raz$0! zrNfKjh&~FM7fK>!BhVb|0c+>HxB0zfSIXoYwj@?P?6rke(4tzsNV;emmZ10lCS?{_ zyLMb;RP!EWpSGh9Y}$DAD2Q}57lo)N#&SM$X_mo&=S5NmN`)4R7z4w|!;eNjOE`}s z1qE#VtA=A!rhoufxmR_NC_OqoY_TCdn8kQ{8kM2LMvKS6X<cGabD8D)Is&XR^0!;9 zncC@o$ypKo3qHw{#un2YM4^|2Ug&gPIkXsaf`AN7NN8>VIXu|=z#czN$XB&hy5gTm zPf1#UrDHaNV^KXfuQV(z4FWEFD<sk|*bUgCw#;Ch+?^@)<IOqLb_6n+VcHbGlS;n? zdHE7xGtBF4x7tai%ow{iT=F2VPvNj7IB@F*BPQ`Ab7l;J6DJAl{SPCHvIPgSoGbG4 zQwFYNw#3B;eaQ;t9a#QA`&vt3+cl{qrlK*4_T-GGh7axPr6|NP6eX8h3B^(WwM&ZT zPAX%}^v+1|Z9&0`9h;3_uCXDuEwQ@j1!s%t%9^@Ir7WPgA)b071xG0qPz%=1&th`X zI%ae8z1xET_7O2>gw=D;k9&U<eCHBjKmlNK+oqQo_G=^yY0y0TUqp>jpo#zP7R0Z- z+KOz07)rr(h&2#(-ztlpdi7o6*#3@s=CFTNnu=|bYuEq*0CrA!|66QceM6qjmQ?fj zf23-NQcQ@Ew14ot^)F9p)-`!b5^ab`BV4d3t<(W;^EFp^mh0__maw>}a^sOlX=R!F zQ_SsEbI5R!&zEg(Ykc6BK}aD{=a0k%g_vUd2pqv^USvCz7V|%dY7Zt<ue>wmulw{; zts08OmGe&A@b%|>dj#vvR9$ES5fxtTe*}2G^)_XPUl`d?@=Wbg(g|}%Gu;atBVXB6 zyGH9n|6zklyv!=Jl;A<%m@6r08VbqMw;FNHkNSJ^u~%heykv^KG{P}P%QPN!#;l71 zqmSGO3JXn?e-q~W;M|*A*NA)~6{C{#Ne!Vh@6ybKPy#0PNRl>{a)nJVNAA206Pft2 z3!M|R=PA<`E|RC~4M}nKhgm=Q2gTG9q)kst2*z=d^ToKt`-Vhx-OIxps|dR-G-x_; z$pe|FsQr@10g*3K#t#15pW=g@g#(}lbV2s&@gahvE?&+<PVcPk3j$aa5Hg{Etsk8_ zT@N7H8IlF3LskkZL<xXInzQ2nhe5^ldCEVfg3cOXu|`OwVJV5m+CE9KkP|I_e-IWX zWNG`fC`J&hiIDy}C86Wsq1~qvg0HPAkeriym5DvJ$-I^f>(a`A2?|@2-HJ^<3BC!S zCAm3iXaCgz50Wmx=DqC*s5VVWdEQhoUm!Z@PLBM85nSU-_3wQhvel0&W4D=QcV^83 zAmiS3Ukcf;>kq%PBlwuysKoG6GACgDY`%B-94V7XFe_6ol?P5JY#v|>Z;E#$dYe=N zGLnn5B2E4z>v78Q-l4Sg4V~MRq-REdh5%&_pGL6_rtf#rV?PexzE!z`i*H$sQpf<_ zs7*H#JDABiMuo`Ev$}OzhIiEg$|cG~ak9uhtVnaHqNk2y5B0Qt=&=2>fHXHwKy9K- zd-$Ee{+K0y=fxy_t5qycBr(=(&ojJ)AQ9m5A+6&fm)%$Ma0ZwC_5ZKEof;mm%M1U; z!Nw<Q+A}9SK}*3uIiTxa4c#;<^7?h<9fTaG%3HNn(5PYws)u=hC%-GPEGi@Qs;vC% z?-;CZ;!ty6cIAuGJQZUW)e_7LuRA>_tbOUM_7Br|f`|~7@f!it52E<wME{v{(%i13 zCLSTc`wW;I!PW_vZaE=wYX{!T9+XUpa?;DZg$1f~*GNv7e8KF|#3t)_eg;4wL~+Zd zOMIb*z~j3%wcR{I=|%dy4PmGFo4#B1^&WT9b1at*4jra@&m6HAeJqdIZTtNdH*1F4 zQX<4DS?ZJs=7jtfQz8-mp~#lb2oyc>3>yipeB03%YAQ^Az$nbvFDLoW-o5kJCv;TU z&_wxm@W7cxcji&im?eMC-!_9qjIW8CFfyVzF-?)qm*Hhq@;H+r)-}PIt4(;?Usp9t z+U20wl!qmX8q3P$<t0hyqoeHf5|ScMkr-vZH+1^Q`GGAN+WCPt2uiMyem!qu@Xx?N z$%_xaz=6%%W;_F0`7%X&5lf8Q-*cqfj^zDuwd(oC=bVo{)6Lt=i;0T-k;tf7nKU?S zX!Gg$?}SRv=CMo?Uc+4~AYuwlv)tzU>Lx=tcXSsQj&STM^>tOu(^3;V;Cy1%hQD^K z7BIzS6_vU>!kN+GKATdw_5hgCdNEd*!OkrXHu4)X6)%?zpwv5)lU@RqIJcnVr1oEQ z!U>%K65x1~>He68|Lv#iAt+Cu+~ZB^rNO#z%fm~8NPQy9p>Z<KDH0rUHesf>k3z7{ zhx_GFF1@TW2?H+0BURu*o;EYO$XS+F2D`m{CyF@sNxEdp5pt6Mjm7UpZTWE~fA^Za z2A{EoNvguI^qTae6Yo7i*lZKKr)N(*ljb^s%$=C(k(MH;SQ7mt$jfu0|Ii}+tXZF4 zYF}SPC9;+s3?k(O13=EkmA;Osc4zVbni9v<(gr--mAT2z(GvxVmeoXOuLnWe8krNF z=S0bnHyGLW#u2FK$v`yFI<oo+iyTJySdjq$57G6EHHLfAje4v91)Ye*QjI_2tb-6Y zD-w#e1&u)`3#o<wzd9V<>|G=%D+AnAO^UW-F)O0|)M<bk$tl-vm(vuB|H+R}srN2C z(=O7vRD~eId1_M-Lz!#KNXbb4NEyKB!Uz(=HSpj{tI70~O*R_vg8u2EXphDmt0+-Y zAtU=4)yp7<V?aLdIUS1gH_=2+0@v5}Wo{XdJiE^o(V!}^bg*Pr17pa*y7qQk4cGts z0cgP~SwxG9OBAg%+jAqfJ{wrJ>rwzqOmk3;p$z5v4HNRKopmu6w(j3w{_Ub!fdb99 z!>QpuQj<^#2n1Srd2qgjzxeRs{#Y2sm2Vi_Yy)(7@S{G&GNbhqV8e9TCWZCCNAggS zcf$rB)%&uO|0U2)d4;bW@O7umn14{g>-fG4!M3{8v0?A0*dj6AVqgD%Pv|5QTWI1U zsa$RI`(tX-H=+D8DVNZ^&cDowQlV-$|L5YwX;e-LG5<d)y9so4K`e1wYkXRKegM4W zZAcL=FE1&QT;Tg>97c3(#)lx30N_T0zGGmBpD0R25`GvJk>!SH)tjej(P0Yt4VoD* zymU3p>#}j9Oa>_|$g9)o&Ncm~k_ZIbtFNtKUnXHZ4`WunPG~ompB}@j=jXByk5}$I zzGifS*i&RvhgI;CMI7<u)Cu?60*NGmx^lt7z+dU_n&sW{7Jrm526QeKc;|eM91~%r z_g5(jJ<q^KC^zrecoX@II=f`{4fCKl_z9CLn4>ZV4PhP>bWBvr#<IkvF~*5;R4Yvu z*6txMM8UE^l(&XQR7@VFcC}@4M^FJh!uMcg(~Q*Q1jpNDrYJH~KKn9fDhJ$rP0Hhv zWNqhr5cV^_z??lZ^gCsY^cTl7Lnn%GD{EeK%MzD%pk~faf7vHRrjCt+_V(YWbQk;n zE%3Rz^UjGe2|G3=#4WiyjC=IG9rZYU>e`z#WV+v$P5bX?`!a_f%TeLX0>_2vH2OH( z_!LWPKw~*8Fe`tXJH}2jQj>Srwz=YxgbhC2$D95d{qwMeM&HhckU%1b8G|P%yNiRQ z)U}Bf%)7Gk^e{F6GGu=}nATW>Y_$>Fvt}Yl1&e1bO~2Vm8Lj+2U2cyO3(6VhxF5=E zeso0NETG|}1HWZU=mkZ#;~lGsND9!JE99s!_WI}<lTHix)oL(li^#ZdS1KdeKX64{ zu01=JR&Z$Bl<!AObl$RL7&$C{2`}eF%ZO>^R3wW+__PB+3?+RusWO9(?4uU{;<w9H zdKrYFf1U9D_Fd7Pb2Le_-_knJ3jPe)6j!gzO{{WM*$-`^c{se==O8z7jCU5_MvVjj z000FP=HmkiXaRPmm|L2G=X9t7%9w6V;*?|cSp~i>uNbdwUm1CMQa`?dmZDv;^2m`t z6LEYTi}QfDS$w~o1n21zI*DYYEfb_Q3sLe+OqX1r42bx~>I&~gis?Q@;=@6H)Zty> zp{|~p*RQBMTQr?OGj$piN#ZUycs>`3U-j^V8ys>B#3Dd0zvw53H(D1kskpE@DJC^R zv@S5}tW7R8BluQRn8nj74VU^S9HLo@O>E8?kZT7mGdqa<?g*++#>)b}xVOiY*0fOF zE;eQa(Bo(h3WmO`vIm?$4`TQ^^SMv{5GsEj9My%_b{f(iUVgY-L{1+@Sx9FBt2pKG zTeV<^fFQ1K^jdI`K`H2Z7!6E!+b5~%R5HpWFfcLSpvMJ%x(in;_K*Kxozl!5b;|QU zZ5p=8J4qP2{^MUu$|+?80!5qX_jO%)j`xS9OI}3A7kVTK-$e=oh2#1a5L5s$|2s(2 zQ~$>A<Dkdm{#GH$zXfOgFb)say3<OjMlx-J+1&mh;Z0F~Hs=>symN-5^u!(+uW1+* zg4<86(yirB&!6vY!IAVP>gPK&mnrtXxqO*=HA)p{YO*x(?#HTdCSlR#ZfB$p4cfl6 z?*u-wnr+sVh_I&2*k!F9?KL%*2QU&rz0AzsG17bZ?6xA?FB(j-{VjtWQ8YGuh5krA zOPd<<=}vaeFJktcO8<lgx|u)&8xLknnsr}8%HaA1ol`$flZ2mA1vnCR`)NqwR@<60 zc!+<RGBbl?qRW%BTy~|v;BqaWnz-HnNIP%k5@st`kmhHP8b2*2(Wi`i03#(TDud&C z)dHfT+pwbJd`nycN5mJ+%~R)xII_61H|&VS-A%qpdwmNIHg&pswcF!-6c3X3MX8E8 z72I(vx|~zc5;Xh%tkdGqpyig`kv#H0+IteVK-Kjc+78??GCAq}ONM;?5^RIzW*UWD z`dyaXD&-n+!LXx$n<M}&NOh~8<=0e;n99A&dZsmmW;o*dU_~t}mFcD5`IhUkV&(K+ zh#?@&%!VhjQ@z;)R??p4d}12Iw-s;URv8*y8Q<2)>7Hr;(kGl!RP-~jKQdh6Zo14O z2%?3ZFUf*l0M^!lhpv#Fd|&?UV6!2e#b2FJUKnnv*mhK&GF?u^GB-q4rh4_xUkh$+ z$PyJvWy1H5^lLmN#1(<n)Tt<uh(b^{^I1lyyv1xrApC&?+O68|x0uO3cICVnuT}F! z)cV^fDpp0oS{p(k-#u_yKOpqsa=4=85N3+Z{tfO3{S@U{w)g&2Lr9F%V9o$LiGJCo zS+@sfbbCJvGSciSg?3jIiT(7kIb*z%D)a143VddMglH{iJ&3+d9W@K*F(@g;!^zdX zbX?ik!j;#>6s0Bmvn6V$%CaQ=c&JC53}<F2Izyy9h5w!d<`@>$6g_Y1R~0u&+&g1# zcM>obR^^}MS~(dZ9W;rGmnF*ri0d~mzbS6*&#W4o_oI&DlVJA+9N%;~6+rJ&w@^hF zaNW2gSeia>m166-BWeSceF|ONUS<>AlX6LO6Q)s4{UQ3}>Gdate6?A#J+A;&KmQzz zJF%1d`ZCRpr)#{DSdhs4^_N}VXHT(&BOC07p`P;>Hj|f%q?^rKdA|M2PJ4$>icP;! zZa`CCx@ET*q=8AgAEblYLRnhtyiRC4y)xI^BpmpME8m(V^y8HrkM)pBAO=1u*f=i3 zx=?D9QMsZR9?7UG@CNdYDW&~pKeI1zH;3%Z^Mh#=Ryot{-#5m!r}Dxdqm&9eFNkK! zcxTg&d7KGJ#`hMN%vlO1w&$nW{B4uEX>8{^QQK^2O_|%oVs?K;O_qL)XJ+8C8-z6Q z=~by;n1v53Uxh@;cRLvhXwmoK$CG0{jm0FZ^-j&Vt-+^_B0l10Kqnpln=Ag6LdsEX z**t(oa(~JyB8+FaqzMoQ)o*6KoXtgxa;K%zZh8!Sj~*)j{2B<z({%2!&zIMx-jSJY z5^q+w^yW5|KT1$!xda%Ywj;^&my3LZE%Ml$KE!O<{Zi;g+kJY!xRn$};o0+{N}Yno zUt~1xzD*S$Ge(^m`s$cDgElwqFns&?8BS5%utp@uHEL6osUN;gf@w*S)_|xU#iTyV ztHeNW9G{sT8^-L_x0L!!o;=U*Rfs0vqtE;I(z3!>lPY?w#aP$iQO9xME<3kLQ8uT( zX8r#afffW(`a5t3Y6*!K@P};vde#};?oEfxFOdJ`T6T=foUy}}RUc(c`gLB^FbYqm z(Eh6q?V4Ya>Nb_KF-g$H_gUU#r)!LYn`%d%$GF$e9;WLrd}p($P0^tbtc?a))=fjU zpYCVGqD}buJe)%lp@eA!Hp)fw3aG?tqP!T`8S16S;dGJ=6Gh~JpV87%0IMt7&~QEh zc2U?wq26~LAl(OGCG?{M@AqKKkp}SlEn;d{D`z71B-JNB;>bD^o%7`l%ElEy^+SD< z4=to_l8z3Y5jK4b@|Fn6kXT?S(Br0p!tv{M7z%qnmiZ~^iW5!C=CU_O1=5fLZU!Wf zg1x#k+~K<m&_MRlW!fcU?~kXTWSTSN<D}jpT;%#VhUk}^NwH`6Vinp<)w7!Ce@Jm! z>Eje{w0<0rKps}<ACSQtEsD)Rmo*2b*Bm~47;)u8#b>K#RrAf*5c0W=rTTY$^fEwj zjW-g$>RQlEq}#JX)v+SnXS1Ad!7!e&$@b$iyV(qbqUD}gg8QgI7-{s*@iLlb$~M=> zq5(bx7Nyg-DU;SbvtrEOraY}FQpZdP#WK_21Bo+Cx_OXMEhoNARjTpPKM%WF<oSVV z3(PhGec^;0yoD*7w|MDnNz0V#2(!E>iGL5eGUHw02Wd=5xYwPe!NA|gHrXE_^O3By zu$p)Rws!36<1jd|l4cYyg_N(X5I|9gfMKO^;nBW1Z3Ohfi}QZW$b*F_C1dWXy8WOT zbgaTs)Nm<lVPXU@Xo@xVP6`z}U0#kGTcT&61tg%=F#Z(?KnB;UV0{y`cP9$068<`U z*4N9X?h{<Kr2rJA65}MUZyPjOC-1xVps@U$=8SDS3G3dJUZ${2r3+$2l}vM}^ta5M zEX(Kp=z7%fIP6a?<pTsU5T-;fy)G+H*8nz<(TquYJ?)JqBy}TN_+l#-gJu$FBqD^R zP0y5%yyQ~0tek7%1MqDZ0KX3&exNTN1RvqKEkil_R?1ekg$>;0KQVGRpwU-;Ehlb* zYtSS*ivc>7!iYM>D<#`~jOJ3eSfVk{d?LwRelP+{fC;fMK=99-M<Bm8B8-(gq5pOY zU>oqbn{!0;V@t*pvk6R$=&XEo@|Di#$aaw81Ss{;x)0#x$2bJM$jOLpg5c)`J9Q}F z6Rq1L$ZL-H`vVI2q@MK4<uhu695p77A2)G~mfX}kvnqJU7DbOKR18<dzv!*dXnU*7 zBRxP%!ED8A1miuAe?cB(I$+pm_E4Ip^JrnomUgl_y2W3GlLhN!73mrT4)ykEi^wqq z0ULn33(KMqTlN<Z{8!v;ui3A@(lNjk5V|n*<OZ0#5`{Ci{#zZT8~ON72}7zt3W*ue zIFUS2mWiyNLV!MqX$ic2j*Yj<=a+W3gNvUP=Je_SI!-xN+N(H$uc!qiNDIh|tuRoM zk@>R-N69i08`rvJ$mWf-nFaY^8?cxI)~9E$n@FmpUwd@s_72Xgo)Y9|jM#psEO-(I z7zFR$p4yKuYKMC9ykq0bAmT-{W_q9wq3^I<hob1p6DTT$`<}uXK;P}Vvdnu-YNz)3 zyI()kfqZJ=p`2gBeZj;`aQg2N&l!0cYP#I@mJpN3q$^XxhztzFHf}&c<^74R3tA72 zeXt*)*XM?NlK}yP9-huzgs}Qk-<zFuK%i1YDeAo|RHI8N>0Ks!__b`HIMENr7_AW_ z00*R-kAg`qQ#=v-?X~_Oawv^vmHl*caAZoB@}&iKA;u(P3uX2SwxUP&UUs;x6{4TN zun8SRlIZu*6g=u$j?ZFZ^zEdf$GM_Ev0=KsU5Onnj;U35h@I*K+o_V)HcGYzb|$G( z*fI?OkhiYVU}b)FwX~an28s&m!!(5b7s=h%DT53mb`u4Z+npD!1>pR#j}*Y?<jtt* zzn=)vy`PwG01x4WpJowZiX=F2VM)Gf(-MFH45jjEO?YMxI<&^D|I)ki+zcvd_s(6_ zhn*OT?Yx3Cw_He+AA%s8?1&9WqC6?q)SaO>%<?YO7ltFCq#6VyAjWlcH-5CRH2ik> zvteJq!|(^j8qid`dLhX#L84sJQ){b21^KHz0LkGw(Q_2virHwG?B)_OeJb0kHYkG| z*ATdb*oj`pw#o5u{ZFnXz9<5T#I4#!0MKI@=gUHXVgOr{MZgH&z7Y?*KY(;W;c`Hw zoNBS3Y?pOd?kVI1!Fqdi3T^q&vaD>xt|w04>06;vVyHdT9!Bm-2-CF6aMVE&v)XFf z*=K^SUUi%QQ9^E#P+|;N7PXu7c=}_WUwaRj4?9X_=5pqYT^X?@DB|g*3gWWY$^<F? zrV}GVnBXeZ7HScokcgw>99E|c0vQ5w3^|5>`*zs$G|atlOg0K3P{_Zsr27vhha`t8 z+0}1aiIB1vkZVzNW_PrzAVel8`s;D%2`?B^OgOUnJPlT~KD-klv;I6qcg3-?;{D-= z=3rw83hE1~%6uc$Msq^&UAv{uBtg~#m&n^=^Er4F0#D}HK~GMdk+kD}wK8U1N;L8P zaulXeh(r?FcQ|3H{)$QZ_dmn76^#L6oe#lcu*TpuN-lvvkYv3Kbaddu8D?X`w4C1= zL=teS_4*X~@rkB2J!jFsg^Zt<m%nfk7>OR1&hQZBvk7!qpA52VB|U5lU_;)2wax-! zxE<!vDXxH>;TRi)0VDL;z+>#ewq5pvb=F4nm0ZOp9C2&hVc^c&aS?QI(ldWUEAoA@ zBGE4rNOE=4yD^Bxd!aVBFZulX9~W1DLsF%pQ)hkr3M6<TVHi{C7j*q{Md&+7S}n4` z`SQ(7FH)YsN=%w@v7Mo1EHnZn!n(z6E-2fTeiwqt4(+w1g+JgNP+t##!sfHF`Zonw z&Z_>`7m*)4jOli!Q5+Qp1y3)C0FIm-JD`wqBXacUrUT?9*Pd?T@J8_*<}FA-FAptn zXZ)EXZe2|jhF3<dhkdv1vjSft>&!g#x2#e=kz&}={vSYoK?Q!{e_i0hRZG_&&<=|v zTfdihk?A;7baZ=mc4FAikj({0{`p(}@Z>J7kd~Yuj3wim7t0|ecAnVhD<Rl)1b^c4 zX2$j-1bJJ{vBnzHt;V|^)IG~~R<$bcS!HaC%fYp-^EEm3iF)S5-(pB$e);T<GB30) z)j=enoIO1*C53}LG_$$T5|NOU4T)a=3Uy3uutXmd9=7ES`|1>Se<6#LMRF=BRBqH+ zCdIrtaPuRPB#m=}9lO$SlTLP@%Q(@^ll`Dt`?a!nXm7B^hjD(q{kuAl$dhM9g;|dW z2C$ybb7|~??QA-~X#mHw5U_+@S8nvMoh1rKmz^_tJM^E!Z3BV*QN3GVhh*gH2qbs> zyA#|%slR=Sm*L*DGQAs=R~$Qj`IsYh*x}3{+t@@@)&T&^({(l^<(skdHDR?fO*y0w z10z5I61eK{iw^*S1XN^7XR|ZpxBuC^i1TEhnAP}2mn)b?Tyd$ztv<@GN@|X%(&p1k ziSo?xG~~uLVn9j6j!P9HAsk{SX*Pr>|ISuX2igYyg20+&H9|J3^Hm>}Q6xVQ<?a=4 zU4$;b$$QFq9{9idAV>T9G$P=S`*vRkFauM>WGs(PjPDIO;m76AM~{b<#LDpp56Hqm z3QA5PIY>$(>8E=AkI+=kw=xH2zSH^)PQP8)#VById8NFe0pni(vFT>qR8Dgih52!K z#uRP5`bC-=8@pRb#>OFaAca|fh^%+s0vJXDa{RE?>@$@7@8->H7^KNYrr7L6jK-&Q zY3f3yf#4o6oQ|A**KPLBhrLk4ou+!IgiE@yAUpgK5ES8_p6p#}50uzF`Rjs^UKRB( zGjWHOX1N(yAy?RuhuJlqwK-nD<0*3LS0z2cj<mnz9xEz@sK=x^GZV?awGAe}iuV&P z`7)TD-~G>@&KaNIZ_(wF*)f~V-W~}N0J-;iqUopQWo92$a0oFO5Uk<n6Z-S6ESk1Z zu~U~if5U#o^%GwM0+0cez;0;8teIxDN49UxioF~s+6PO{vR$(!r+kQ>$~JA%*cT(k z#$K1#?tDC8-gY1k+*_fWlSW~RPNbCw5lJ+95{&`Y`UE70#)g_F**dC=>58CV*jK>m zCr2M^@OznjCht|eDv@&X9RR!uNoRF_15>JTUxsj}Trd$8C>qBWPvxjD>yz`<tpIZQ zQP0%jWA^#&D=bV%XyvuJ5);5b4@jo>uEBqUYl@nDfXiu{eX|3ZUY>4SW_WPbw5UVW zHNk=`W+8)LqS24ja&5Nrj=c9~zwZ2eLfw@;KlLGJOy@<B*+H7em)1RudD^+UJ1;!y z=XxY)p0qT5jJ-IY%5&e%?(BZKiUEY}-*sdqL4eSaqd&`OXSp2+vhC9F8Ki7M(#MHX z8?=l!xcEk$D7SzR%HBtbOf;4FCs!PJ850BgBS=)*!^YNmsLBiyB4x}(b}^v7K`t@Q zU|m&zq?*QU#Bs4>34vuUd-UnBkfTWV5d2QFFkeu{Q$RCkqMC#U?ATLyV2v%h@V6M4 zW0K8a8$4q(ouRbfPdvlrlWzR%C-JTf9~h6+Uc6ArbS}opU1Ch=Vjqfx*B-n7-s{Vg ze!H1Y*5!0;8Q7Ax<nD62KsZjF7JL}%^wR;n*vmK*hd}bM?Am-fva}+xt97V^H_>ts zjrbl?`YvuPf}pZ=gkwO7hfolz%mHu{1UF`bxy!}~zteK8qCjKtw2xvy2L)95^h+?F z8vta;)E|ES+50i#a)IF)SgFl6dnGm_f3<tG-<>wbdSFgOb;_(mXY!TJO~^g9`-BLk z6sGGfVg`Q*77Jv^-`NK3CoK{Bl%4RA9`K{6-&Mc~q34=9E?_ROmTyo#T@k^BpdxkS z=beucUj!(Gv>^L^iEe2#U?vKm<M`7q0KsBggZ9T;4l|i}M&wfiUIpe9vHMxvIs$I+ zxi!p=o?){P0ap*G@U}{~O(}Gap{BcKQ^G{!PQvZ^Y<j(PZ82~M-JxeZc{cz8)-pva z_dIg!9(WV=HJ!z=50h9p&zz2x?XbKa(o0bsyk(ZI1`Wa$J0%<;?YQZQRd%^up>qOr z+bx^N!!@ql&GR(Ym-bu>*ACnAXX3hxF1J#+x}4*O$h`PmPv9^d?e!g;0f2QDA8$CW z<y{-xMJ~#j4Fz`Eu&A`I1Kzoir}#My4c)B;#$E^(JIbIKPaKgJrc^7T_h>iYCZ8kR ze3;lR7iBR@j4m#LOT_5Y9|<A=&@nqiup3yhSAqEz+jfjJPdOtzT1q(4u49sad_oL^ zX2z=;F6$P(TxB6rm0R}HsaPneMK!V)1+xPU;;mPL=_=4Ng384%QhrgYn5ea&!HCwx zkHr>%RdNw`xuRj%UU9Idm$Glu-G<(~TT?}T#>2+Vb09l=UlLU@r>P^^Fy9Q`Yizkm zRi@jS2)Xjd)uM3Ixlb?17|YoV-twcSiUJAb#uwHLf!EB`K&d8fAqI{ZP_e?=ySoCS zrI;hY6{Z1dk;*-B{~i!Njq%ip6sErEd6DLQaoDYma*6Q<m+r5R!_o=qYuj!?5Wl<u zhWq;?WTHjRF4vv+Tg$-3aPd=rhYu3vjfd|zjC0C^E0aRCp0|Wz3<hzbZD`0U8qmYh zZGe-r(`G#wur0ZK*RNlBX@bCbX_h5^1vZ@zqE^EmZqcIY!3Ipu)L=3<V+~e4-=rFi zr9MMk2&sB9`TEQG{aw@UzO$I$T+gQERD!ckQ3X^kG`b3<MzvNdh*Fs_MV2@_L3Ag3 zoSdHxddYZQa<@t5xYNjI<SZ$6$hxl<389Q(oigd!u<Yte@W^C3ZV$Tlk#Ws7Ee<*@ zLFJkIGRr-IX<dn}7OYg-D=e{qZKq*CO5@|wlme_o_*VJJTaRx>BX!->_d~6c=#JWJ zB{q1sXKy(X;9A4tV%{PUm%dB_31%sYXA!~Jvx*c0oV@&E$78qOR?P2b7G2wlw4;s! zQSa|og~&9YHsuQD5BUH^hSJ5QVaLA;D{5d$B)fy9ae05-Q?QY6qt7pp&y*}LKf>Y3 zmCsX_Jb%NZP~Q+H|EzcFxoGhSsaXv|Kxr`^o~#EMvN5Zhww4|O@C4#$CHcK~ySpab zGaVnRh1~XLnuuzTv)-}o_gR(Ab#~GGaAKLf0gbF{c`9lT_iWu|P#Mv<X{yGfOJGF9 z<eH6wi>40ayobWHu3ise3%cP=Xb0A5%zP6hAVo&q_{;N~!&ak^bKNLrf-t@fWf4@a zYSD_1W==a4lWjDZ=ZBObbN%*<I5jP-#aM1BZ;>3iqm_v)N7e*smF>9EGhUv}uvrh3 z8)Jr!k_Vg*6}_Qyp!eRN$K~_zGD-^3qQ#lWyV<P$3FwBF)4%cLqYvM6*U6~gtydDU z)gqs{HhG0!F<ZwA&)jy&Qk>!<(=|s_(}7SpgcOXV!xt0b6R@b5wg8~%Ovg%ASSZtx z4{F_k-d(@gW)B`gAR$tVAwXd5j};X`D#?ypCKu<!el*<HX&i-_7}vj9_qXrPLQj8f zx&NN9<i&q(W*x2nRxM^{)!<mei5+Ync^0ji2n~dRY!O-nDWq^`iRl#5Y4BOmvG*(s zqy6$8)Y1%-qRJI(&&h?6mR(wx^v?6J8tk!fX^@YX9Wj0w?++XAkbMmD@Rjk1wapd6 zpSx>T4KAKHr@O1@wA(&9=;<i6P4a(7+F<iaYh>|FSN)yo0Ppq%7-)SfrSVfV3~DH& zMS~b~v_LW>JD%)jm8@&R;k9Q>>Ln4~#oo%e`~Z*En{jwVjU92Zs17xupS@<@e|O50 zCt9^~xWC&}vt#Pay*XxhxX0AaZ;+xj%EVOTS(<R#hLBn~YrOl8i?hI9kBl#8B{4_M zejFNHfVKWKqfPLDJj1vzTC)0-Ogt<z>_s3WXmt&at)%9t#>8Z*y|o+DqDtx7GZMpc zcga)+dhglr#vvaMil+%YuI-RWo`I=;o_m(??Z&k4pZori2XCGBI`LRfvnhF#-jttY z`&BLCr?`WepHm_A(iVkQ?^5EzJQOO!r7H?7c1nY|R`ie@dijO6hxaq%G|}@8|BbG^ zxiexU`+5DK$>=5zG?>Pl<ZAT;_jLs)EN?B1#EdCxvBFFmSVGdbtV(E5F6!;^!=%kf zSovDAaF@;#x$*Emd8U3vMhT$<oKjL5mkew<)!f%#_V%<ta_1dqC-vIaMm~!INt%&6 z53-o7lL0_|jWM_M_W~yYx>>Oh-LOl{&5Sj8u%j9ik=aBuC1l|DmI?cp$7-Wi%qQaO zWi}0c*CKP8tFHIhO2%G;InOB*M*O;zrhze4=zJ~IA{0WW(LtnA8-tx7hiN1=RI;+M zEwuNjgM2O-TlLvQEwG?PucE<Q3$bv?X_e0}SwNJ<+(Z}jFnRpZdUf4B@_*48Vv;?I z^m$)WNo2&ja13I}B#_9kBnre38v~u3<lAoBBwV0@O8c%>s-kkM8eh2U&X-@D{N$Zq z-u2}zle3d}bUKd1&ZPAH@w+J_(U*$9YHt<N(&ykycNCb^!mrVfcM+3SYz&`4d6tWH zWSM6iW0A8MyQREX@_#8`N{eMDPvD;I=AEwpkG#w0l;znY&NagYtOtF<oX5Xl$xW0d z2Fw05?)v{V?yOkWJ!8_N>Y!QA9c9j&byLo=M#sz_<mHq+p6u5I^OI0=Jpm|IPg*Q0 zw8sK5U*M@|j=>p?8DyHWeHg8?{Sk+*tG=Iq?#U+}zJGibDBQle)5viw9bBI@YOrQ~ zS)h<|NZy^!l6m`~OF0xlW@?BAhIIzg&H+x_(h9AeKNgc>3mvQcM5%au31j|m>Vx<D zW=(zS{B(1D-Yuj!h7Oe_0?CA%GwD#0CAV0{lJlPfBuAQL(Wl5zH{oQ|>(r}92Z!CV zEJb<#O*R-ItgV#FOpYPQV!|=A72Lb(-!kRh$bvn$qCB)}QY$z|piDe8FAz`M!%!NT zV}zrzr2N#^#Hyn><m>4wew6LANgVppl&DiJ6Rz&NQ(O~7nA3>og8o#oB7ASgw79@3 z%ZmA()jalu7r_tfF8vLJvWwe&;SZiy_=77|e)o)Fnr#yy;!+@_KHSG>Kx@mn7lByl zIRaMk#XUgAOcVQ)jqU5@U=c?cTeeNBNVm<&KE5#<m3ZB`r%jp0rqRi9Gu`~O#UXOn zh1%Qj@q#OWBDKejs3wtwKq%5?pSn!O<A){>MM7yTwvjEV?mHYxD*i#Ap0US}KJK;* zhUy@`W;hssH0-JA=i~O{##hAxKMdrv2}gBg-TCH*;M=eDc27OmMrtk_n<qw+l2i}g ze@`>A!^?Km?~$MFEhgNZsnPhBcWae`rK6HS*2DhZZeItk$O=Yn#;GgfjjUQ?BB=CW zxGqBZnG;)1%5<8p1<@6Ek*L(~;m`(hEOG$60+oC4X~MXaN-jDwrRgNg@V2W`nKPMF z0bAo*3j4G2-H46dou*D=%aq<oRf!+``CK}=rQ<{-%$xED62Kz>Yh+R_2Un5&<&}r8 zHT58|TlJmZK~*6!yzc&cM{Ze`pQ(^$^G+@sYF#0OLnfjUsKhp$;s|$^d>(QQT*FeI zw#dA^aL&*>=gVOt#&$Y5z(e~t$IQLhO&YYT^UX3{{@Kz&ELEx*ZFH0!J&w*xREYtE z-G@>mM2H=QuWI^@(Thu4>|e5jK8GO4-2=zJ!)3i7U+(5Hz<@z5k|UL}{AC1zUMsxH z&1|_(HpQLekoc(yxTM~H<gTGUY9DpQEZbaHP60htq>tOMp;nW0s(}Ns0jse$W?*h1 zY#R=)c^XC1qf;t&g4%7pQ}e?M?-!QuIQtIoW+C<5g7-T0&OppUp3Iw%jqsYoYZ)Q! z5t6=v-Z#nbJGXdG$K8U_oPpDKh?nnhDbvaNE=4ZPm$HyR0mPaVNJ-P?uYPvjkdx#Y zt-I2zhd&wc)X1wuXeG^?ZctRZ%3i^q1*DXzPvMLvQ%+sQ2yOI5b8ICUC50k;sbpP2 zPO{j#HXmhq5c$u3^Ggz$s^4nOw3>95-mQB3d$Nqc>n{ZCh9H#?1ZZUU9R$4`0=cNQ zLy*1gWp2(0im5}Cu0PHJ7QGxKy2zn?%5xZp9!lsL{Is38EypOmO*y8ey4%ifV*oc% z8CfW&Hrl(#IkJ?&9An7doS>K^IZ+^=a+0O?YS$femfVt2hq<5H%6<FaG4p@BAC{;P z>2EfK@p~NAc37+LIZpeb_^euCv|L-#f~|(mw;mY3#hd(kF9I~9IQZ6<;JZ{J_BRYv z87ia%9Vo>3^BQaQUCCM{qA#V!=Eb?Q2UIUr@L&6t24?#%eDDLBcfB$-(|J5;1O)f7 zS4v~&^3WyNjqgg<UGmZF#ZN6_{B*30{gB>n4}J2w-hHpCP(lAO)f*kR%0vUf6ZI$I z|Fmx6xpaI6j5;4X9MyjWBjn}Qt^=Roak1O_4HTxXmj}A4MSvpGzEe@sZX5qW%hCvD za#)@^jIPSWG8H|_W%>H><cJUI2g+)=MYlMPl=0X>Nosb!jq$Di^<D%=&5Yt%o46L1 zF8n=CG#jXLE1d95J9eE^Civ?eFGnAP2ND=?-4gQaU0IXlUBz92mfMrw)(9J?qZvNU za$2ANNd{<{uhwe8EV%(MD_f_WxT(&Y3od;FS<%F!(rswyXmCJ#{aOL*`N|(RO%Ey1 zYlIb=-Z&^gDQfv#lwToimBxchd~o25%M_gTZpx!qekJvdJ1U4<UE8P(zZ%@F)pk9= z1oPDLkO0f=Wy7&mIVOtq$I2n2QT3z<Z9HhGc3V{Fk2bQ~B7}p<25Eoshe~CaRL_QP z-sk8{5fkP6$NkX}NsLTnW)@a9b~p|$9zFpf5iyCKLvD+Vf-gTx{sO233ZfP)M5wL3 z7PWN3=@~?b6eU`WSaHTMGO@C;b8vD=kSIyA6sgjrbIXt^OST-j^5iQ}sHh<FNBqf9 zfB^y^0umqt3ZMcSppzzEy(1Jdh3>zt6b)$xwS&6B@In1x<UrKEa=AEIx?CBFZ}1=Y z=obPD&h4i?PQ_gN1-gxAi$pfx=AO%BKt8{2E}Hcl?*s&ZQIO&F;GGpIJ`J$%bnef* rZzQNP6I}3DLl;`oa*{bJM$f6uezTAPJ@5@jd!H=xpQcwzm4^WU&I_Mr diff --git a/docs/katex/fonts/KaTeX_Fraktur-Regular.ttf b/docs/katex/fonts/KaTeX_Fraktur-Regular.ttf deleted file mode 100644 index 9aa5f674daddca531e771b400501317fe476a722..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34352 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 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Regular.woff b/docs/katex/fonts/KaTeX_Fraktur-Regular.woff deleted file mode 100644 index d01450e91806b0c316af1228ce35f7a31788e77e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22088 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 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 b/docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 deleted file mode 100644 index 7eeba377932c6914b32e8e3da14520c231559e77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19784 zcmV(-K-|A~Pew8T0RR9108K~$4gdfE0ET=308H%w0RR9100000000000000000000 z00006U;u*>2s#Ou7ZC^wf!G{@t_=Y;0we>23=4rs00bZfjeiG%NDP4r8?J8!+!*RT z4!{mN)+nS|gVM(0|KkA_xvr$!afleY3uvOz#Kf(n*QFWCjUKd>k_!wg<`#B19BgA( zVknr1Y)uVYZ=K_pMmcx%@m0P~YFrE7XY|pl{BMnd(t5W=pk0>MP?}$8>(}F3w<t$w z?2}yX@ci8R@BLWRUlh5GZA2}oSP^4`6|_NxNR*K%h&B>tp57Tz=bc=5R$OUc+1DQF z_VhEm8%Hcc@F)<`2Z3jME?U%Dl;{7K_y6nmbzhKSz8K$;3F1j0d`A;p10Lj?AY)eT z*?LdEr?(N^qT5oewAOm(`S15*zxKHgk7Tuw7&^&HfzSz+M59!1kh*B&YqIM<)BXE> zt~kq~x0*mI_9ZovyjK;d!n0xSFtbgE)c^m_t-ANj-@9l3ULczY1%;v+1f@Hhrg58B z^?D9-#?9d~;;-x{L2)K)o|Lt_Cx#9MwSfqo?DceqCqXE2db4Y2lOpC2#3L*Y(b<1% z=>3Fu6L%05MKX%OMMy3@Um<bF&b+#XGFT!PU%JCmF1IJpnb-e+o7%mP{vUH<Eg{bM zM*)<9<pAl7<pstLxZI}MrrEPq9aXpe68tsE(uuFeH5f_qABczlt7%!CaBG(ftZ~9J zY8Kf0JVEs2jb;^wch50ZId)&GeyIhPK~lHWwq>&{Gz^&K9XA9{x!eLNEm|gNySsP) z2#3%p!8wO9IEC>1y{cvV{wEcdd$zfbye`rq%Bc^rcG0?Nga;4=9>^mINW0|61&}MR zMR3<kVZ%s}Hc=NOIpw7NT%A|oMM!&r&RU05h1kU|<+5{8x~s2f+!Su@ws+I|wG>^5 zCcC&n^Xm2Azx}ihHOX%0iYXH4@_1Bpv-7W8m$5}r@%E|UV8MoPzX=3pWzLBJJga+v zEEv6e@Q4mX=)=zd_<hcAzx(PqPl7#Q;9ng@k5jEje!{`xCj<z9&$<H;nf8L6<#TEW zf-JD`utVtey2KpbSee7g&8w#Au8#-DzmEOhAK=O8FQ3n9;9J(O{`?pI<h}U8_a1uS zzPk=S?#*y>T|nsNU=*38&T&Qfd_F)2d?zth5%t=rzf0QxIRZHa<s7b3YqUDO!DupD z2$HheX#2+I*7nXW?Ck&~?z%l*hV_XsThh`qGObxQdv=b)>2iB=5n3sdS6Qh5p=KNa z>4TmB_)NBeTOWTu69I)M+2i4dKcUZ%Fxeg?uQ07hCbJzu*e=^1!c$zhC(1rjT!*+q zzQ>aw{g+Jw-MP$^)`SNLS4lqtf<HP-B$shzNr(mb-z&2CG_Kd#Rl|IE^Vw_BD&G&@ z{>p3epulFlw+R7dY9lSUZWK<9vv^K9JN(yy74na%0IC`5v}7Q4%y+P}V|aW;%&R7Z zj<MWVwWP!VGNgs~1-<glcSq4BR9Xk(uw)c18jB(<hs&A?__x9uzZ;}ocW;A`m-+Yg zEcteVEMEKgN~Jf!OW@S`TOUZdrs-GQLV=W2k3c*Gq9GYyK)~rlvQ}W;u|}asArYp| z)HMIctTU*bg2zDI*!Q1K<|yspA6%t<4$6rkD5<jQn9}vh5NrFgo3Mb(feEDhxm4sJ z)kwiB^@^SzO-PmAEQ3V{XrWuHkJ?7B@mh%vm3Fh*ETmq;r@0Nw4x<f#qbEX?G;kN# zKE>{2ZQruX-RgN}7XC5oW;acrr-=Nx(HR3-t!JLT^MyTqI48`$^26xYr(XM~Z||#0 z$JFrN2c|&R|K8}*2yr@gGq@EHxYBxH?ODVoAjLt&hc;Qm%RfSSy=xKcCrXM5h(gq- z&cK$YY-^?0C16)1jC~6LUwWbl7b6yYJpItD6_DUN&#pTR)aY2MP8<$#OXlz1p>>!6 zpUaPz!H&&$YIQbPj!c1JZK<1r2QWARlT)xbgE1Gt<|0hE1XC`<j4PcRgNArPY71W~ zg8zehZq_)Sma+w#vn6)0;shK{!Q~7*E`ZNP2)G0xmtoBnBi7KufFu;zi|7oYE-08q z6epdX8jexNk56Tp8DI)Lnv!;<nq6_Wn$lsf<U#6<jagNMq7VLRaI;dtv-qUlrNJ$$ z&5Hdzc&ZY_G-2_JO)!1|{nfGDligCTi^{S#Xfd_AMz{)pi7Zb{bF#JEU11aZifH!U z6k^Ng(Re{{(|h`H{&6aAkl<&kI^Gm_<8B@GW0hrN2nHE;b>iw&R4;QXK%1MPM>tHN zkz|uCK~@1iUi7p1YyXoRn}do5u^9@k5w>L^yF9i`w&%)}Q52hzR`XXOiVGoCTA)&@ zCgmtLqOP2^B*67p$RzD5p{xlB5>?mpj2KGN@(gw%0(Ak2be$^5VqO+Z8Aa4u-EkU? zaKW(-24GSMAZpU?HdCh!%I+A(M59W?jLt5qZWLf$3-0y}TG5K+974H7@O)K7ZQ0{n zuokXG)3|^SNi@^>D?+Y6QCy_EI;scMzA!m8(5*Uo<I#qur0cwHj$A&{s}(rU;*4+m z3d5!r9lg@<Q0FDun0a2g>1L3rVJfIxm1YpdNc06Qu)HhOzuFXeduF?c{D+vPAUc6f z=FMB#RT}k49W145e4>NEubtGg!`YnRIJ>T-=cqG6F&am^qjmL|Tt9t-DS)LaPn>Zh z)7&R;(=~xH2pUYXo4|5zHc?sSO@$e^m@x+ci)Sg0Ae}*2hKI6@ERE-oKt6-80uL1l zGx8D=C}$AH@lb^@Gp{0nS_WZt9%>Ml&YMV}l|fjWhj_wFyn_U~8HDwCp3*0tIlN8# z$EwdfA$7pKaOl7?VGJBqoIG%xcEU;8X`ObAfwPM11<unhxJbL;GVOw^5jPCdZWyNB zFiN`*8xt?D5Ac5|!*?7mIliRJN4IY*5)u&XT^G$%7`+6iSPJ0t03bUBy59*TQ*V1r zxIb|Umy!`9mx~BlihMxyOWg?eOBs6_i;NY@ukV2^)~^g!6`5{l<X{+&DPT17xg42S zx=6;PGo|9_h;$80tKoQS1t>uXl>!Enp<uCe%=T81*VwYgnMqM|<gsDNO<q-3S2pwo z_O9|Tu5PbU;@I>^->JQ2d#14D3-7RZlW+gv_6uKmGx==x`SgopbI*?d<K8EeUn3v- z(CX;iU*G=Wo1gvNX!eJ1CvT-*O@8-l-?=mLqj$0&qCcK~+>ae59&wk&DG3B}IwM*B zUr4kc7k5dw!8zldTE36yr0fn!D2NU?GL2{e9l)}MXPXHBx~9%1wMh3tc15S86FNOo zr=yw(nV0zk#yB1o%HSINVD#ecP>S&<;kJ9tf(|?jlirLXsmG>(z|P(Wk)YINl#doE z4j6AfK~(+KthmD%_dv8nIy_XJaN!p3k%}FB>qagx<}Wq`33!}uA>0VBpm%woi@*tg zSQk|igdNCsKNcZWY<$O1=ec83zVC3Uu_MBboLPR?dm9E-P;b^@1Q64AmD=HwSMr!t z3u9`V!*`ORQcyLSvu9|vXJM<0I7>~4?~;H;5p^tx_^m!vA9#9h<f#uVq%5?VPInZ0 zIxFTpJL|Ql0MBKE1<+nc6~awKHv=K4i62Zl&lGsZy?CW@8<Sh}a_zsA#Mtfs*Vxj} zbVHo>h8!>+=nJ1sp7lfF*xYCi^2{OPQvVt}x0x~CI^ruo-gPa#&1xedrIkkXB_wN% zSYF@Q?;JKZNkT>f5LCiSKc~Ra&!8=-C<c0r?nikb(4HY?rv16B62)~#o-+31@*vkl zJYe*GNynkaw=F5L{kmS?MinS^^Oz74I+f%+@Yo?793T`Ur=;=?qMvLtLOF;Z%6?e? znCcO+yNfns8DX+n1*r$apFqsbp0gX&@h3S)IQV|59Q1_A1AP$S<PF2W6Me=Kru2hP zhsY;5bCF9(OR#fNAq49I;|gUgp;}^xbouy~b`J!KUjD?BU>r?JAAJi%ILEZ+*f-P5 z1`s+s^KaQcV(f^~vq*TcSBtUGi>tPifb75Yh$&<`HZf*tS{sbmiHGS`*SA(<l?c;4 zps%%Lbk`8&rg3c_cjO(}9|lCO=?E8Co2nRo?Zl!(hEoO2EN*ek^xvm*;6UONYX$TY zbrLH;EH-``XmK(vOU4;{A}{P4mIxQZRCct9vM9T@zcbgM`rcb|=!~K>@e0M4_1KXb zco&}F<o~r>;!r!H7s>^^c%jRo-APn2wuVXTi_Tx=`BR{${*C1soV^9d{8}3!sQUCL zqllQ5Qp@r{TEW3nX9<6D+b<7u&CQQyf$_X=wM@yq2kph`o!^ST)27CrXOU`}AB(Bk zPBCo=*QgAsQBb_?Kv8pNQ8+zP=TK>tlt=g8#n6v^%F-E2Mk~O$hAEwmt8wlaaU)Gn z8sWl8zYj`Y9!DBhspN}|P##f@>8c|*Kd~gAnZb(G3*c$8vKbGiD~z+yX9TCob3J%x zhW0S^-Qgu*mEVWrz15&PD5CK_+ZoWA(2*A|lw(Wk5H?)xkP^yS%(TR*-<IG$5u0;F z_vO%%gagKs7x_sd$HptL@r5kDSR*?#Rp+!y3jeo30JqAik`AeJ{ge&yEE(TsJZQ7N zk2V-K5wA|DgkS9Fb)R}o8!|i7?J5~M;<7%pSeOxQHMvQJip4tI`73MuDvlNZ1A1ig zI)BaRM((a%8|0W$k&A7Xc<c{o#M9kSUqZ`?V??z$uG|AH=ob(DaDa87@k6IJBlx54 zBU=ZJ{*)T=Uo%#_EgKRT_xNrfb8UCm3KH!~ctAp&VjY9xCu_19udv=NPDiWxJ;=Ub zv2nLZQ^ilFal;kOCa-MQql-h|JI5qe3(9a0ELTb8LEQFpkH+$d+_7OoM%S#uv2^uK zIuQG$M739_layOdqmh6yNwXz>VwFPYRNY$@j^LKZ?mB=sf~`r06D8m+oiM(gX(N0@ zx65czs?U_0a!JAZZ|iZK@vHP~a<)T_YKe{Ir7r65j3lpx!!&Qq1o75+lBkckoQ-MG z-sBP&TCm6;SO<_rNtq3sW5mmutL3yt5WkaFSWC5X?}jxRVoM061+?*|5sg!tUMvvi zr}&*GBX@vfqv`imTj@&LhFAfk3zFh)M5b>L+z=BtjYB+2l84w_GalqC>}3+#?M*wa zmhK*^W0nyzns2}-qzGHHMi1se2~RhY0L}Tkg1^RZN2SCMF37B8SyC342PEe-VW(XC z&%#`4CaH)z!uQ@@Zg9g+gk9`z3?l?zgJ=l6QqVNRvNlgAf8}FkFVqP>QgJbrpqU^p zWo@yaL^bifECvaiYOCx&4sGfCq@WsFe6C%Ruf-0{BXlg5lO);^U4vjb^)kdbnU6&% zH%#d^P34~N4oCN?wyJ(AHcmLYT-aQ?SVSA2_8C18CG6N=(8WtpXFh}SZN8n)><B_i zPU$6*jc_;bG0H|PAB$f1RQwX=0cm;G^%ADLV<Pt_?+tXKF>!(lc3**n7)Y8=!Y{=O zh)kn-4PJD@DDu|tB<w`!*!v_uR|qpDBoDIdjLB$er2`cDUBDFDh^eMsQ1O%o7ne5W zpBml`K8Sd$l}I3NHTU?}Ywhj~v?XiWeoB)TNGAsu^()(b1$fX|sOR{~T1y;YIm|~a z_CtWrs-*U4J*w$uY@`KYqHSFq&b@9@O*ld&>=eQ$CCS&pbE6I^bV)MuU5>5E*Nr%= zRarI5AZJdS2yB_v%Hm`Rre5b*z8J{RMiO|%0;$VQr4!ZkSjR_9Za!iK<q;_ypm8SP zwC`RGlFlE69%}1{HZwbC$@B6pofBGkCyGnP44mLVL2Qsx9p4BhavQVjNawh477oqX zg7YOz**QWe-0+qeQevafbJWvEpjZvAyYwPaJ&u-GNunTD_b3p6bx5FG8*wf@j$ekC zCH<TA$@nOMeN@7R288<531>p&HP7>bX1vbbBy92RtQ{lDzOB{X$3ZAR*6+%DY=zl^ z$Rpkbqt586@X<|P&=M*oC!E<K`BO2XKi~hUoe;~H#v0!-eiM+wx!Bvz=m(Y;2g!7b zA)2tb7UFWh>=!#GywDpWS`7$MQg@skq3D&;bL~l5@orXvpp#y;1FGsY+E3yG32#e^ zxT9e6*pg5Mvy9g6N*G-b*&tqZe{3gOZ~oHJcxPTHYgG$DY`&WW=H8dO<M;#L2@`h9 zJ^rTBr_kk69X6@QWxoDxLXnEhhVYXm$(Eu;{5Y-YwNqo@Q>27J($Qx5wD~vG^x!EO zEhAze*BG^x1BYOBVo#e}vQL~WCN(PRqi{7nW!u9k-0+$YF0P^p0g6>F*;PNhH6@H4 zUE7znqyVD>dlQ7|hp8{UQD-QCYMqAKG;ra#(bP_XAO%;{=7i^dX<44eGgugLQ?uC^ zU4FUtuFQ9zKKUglnV3|lzh<<{#D2?7X<Gcxhg92Vgo&2G3-^|A?fsPq^6rC@t^nm% z2Z#;;X$jm^?PWyHUsdp}^$}{h8k0}#;aLSKb-69-MR71F!NtR%lgk8N9*>2?O;j0J z4#0HxMk8sW(T>ev!wO6I5WV>%1mW0dX4pGDusr!C`GmJQ;Qp8Nev6ucgUgVGo8^wp z6b0^S-`PPm8b%!ukW81pOsa{O!o#O9h7(cOPpOu`o;S#!tk>&L!K(+G!EN1938X9U zavVvobbZ7n`{<q}YX&5{sb~@=Yp@HXU0-T&$C>4t(C!I(9%jC=PeSvTW0K<tJ6>f1 zHK&v)ggxs3IHd>?7vz!u57=W&?%r@|X`Pf73^2cH9;~#5a(BtgVvFmnRUO+W*ZhfQ zE|9Al8`d`*M7_M+y%*O>c!4y8#Ea27e<D4oOptIi%}F~Q^%8B66vmRb+=UYAWThch zpmRP-){^F%?S0s^+{S^pnC|dm$^)Av;_40JQscYd|3PePOc;H%<nn?PoRrvE-j`}B z=-zJ%J__ryK2^2(wif+)Xv;}2fD!k`@%JylvA86q9#?K^AhS0%2^Z>4Qn_V|w%`sX zf}Sm1dl2%hKqInjL?S#MF&Cc`VehJHsJ)`5N{5<MK63L<OKMry`3p>j76DgYaSrNi zx%SYMM+}qG+uYP0HJUK`u}wxx-s9Q$5$h+{`i$c`r!;C$2VT+Czq@(q3BtDrCeipZ zeHa~>!CXCEPbjz`&-GT;yShKR2q%d0mrD2btXm#nJv`k{QtZXOh=O92b}H;-!&)dY zvSJl2YPFTfs}vYLuCQM{iW%7ip`nHkNMA;klBkusYp17+N|pkT2YNfETtg(mFs5uU z=aNYgJJHg*2^IT^_nXL=2dQ6CY|kGkAl_$;2fxjHgwzu@gne=)Wj!+LUpNV<JK85d zdGJV@hA*W3_<$4c{F;fQzx*;$&ix9<@4c6~UY~@TKJ&i@gKAaHm9G!gL<N)B>O*9z zPTRJ)d(CplMK)z7W8L{kvU$i947GTu4dw`yo=RvlVE~%@xN)B=tvZ!4`7ZBj_e)i3 zU=JvXszdm!B{&u21D5-8)TxGeoHi<0>0{&8-X526jHY_AOJ_tNj~NtdD_{r_bzf4y zU8|ZJB?$L8t@R}pZG4CbFNcMRDbb6>KD5AXjIU;Y1yUH7{D(s55H$Q*$W*@J*t*fk zOjAP)6r{jbmv4&17R2T<n~N033+jb^(iCsx@O0$1nTvZr<t=!0^B0UKCvwRsotKT2 z71L*_?4d*WIv|Mj3Wq*W>ZiaW_aKepd705#22Pw9OIRC&3BLsAqmaS2s*J$cVSG*- z&fYe6XDw;c#nFu@N;sU4n8@Qf^<Y>&az6_n$y+qP`U5L%bkS98|0;2dTo5Fi@PUZ? zpX8nuaE=;D<kFcxMece;Zsbg&Zj&l=D?U@PiJf)JzKaGok6a7ZaA>5*7;20edcZ5F zAGf5V5{tpKZ|g(Fh&<=r!5k!|zyDs<ov~yHb1(=HykQ-4K-p(!$~PZ4u_!?f%qvS6 ztp<NJP?~%QI)dD#eJQpyNY<(L1ibLaaSYi?pOMH$)@2-fc^Tn?++T=arPW3g&OHTW z;IJCnHtK_oe)6BX7?7UTKq&X2(%~(%K9nc5W(sOz7rc2;_sJ4G)v(CazQEqms}aPs zuO><vwSa8yiSE3Wkd}!=Jx#vXp(LaZnBL!**xf|*(cGH06WP|Q8d2l&PHIjD%&gtw zc*Q~9Ifsv=%VMO#SW*R(cB)gHu(JxfNT~xsTDNOd`;^2KO5VE{UPX0!m-35@++SY6 z>rEIsCrLC(9Kl?rJ5Zry*5oY~e3E=y)@CVgag3tK=u5J6XDtMj?SOKYk<X+_m>rkz z@(dS;aA+NJuZb<Roi$8{H`B=c3=ow$qTUlIOD<<{5fYS&-pg)7pu2bTp_f_NY&;sb z@T|pJD?c-C@L}C!ZMNRb01j?Bavo%=Vq#<DPi}bCd^-{laAqK{GW2&s8s<lL+zPCN zA=Rqs4(j_h$n-!N(`yL3X*0V8Z?HhHYzo`RLA(~1GlcLuoM2q)OkbUR2f*nd4YJ^e z4fFYrw~TNN3G)<h=P}Ab=q=|xs>D=-z4<90LML%bM`L5~3oRuqe$fB~W+IHmWlbKo zD(QZL%+3<kqPnO9x3KwwGR$)qYL*l%T%_M80<6#dj0vJ>PM<19Ruc<B3(@8Rr94Rr zY8fh<{<$eT!b#T~#SWz%-pXehH>XGA=<xhBB1uyd(%9ZqO$jo6quja=eM>G-ww?~! zdzVWy6hc(0jBMu&RwY!TIx3VMs5R`Ul#kpRi#%63i%=IAt7+A8elUnr!IE$WtOj{y zG_Y*wY^b%xm-aSa&?wBO>%mW|g7*>=Zdtrne#YucVT8YloHX@mWA0CcAAT;Vgq>nQ z#$B~d*Ivw=sAOiESF_YA{lV5}ut;Sb+W5*(ttL<qScpgp%kK_TMj1W9x0+AsRn&RT z*9($_o9x)-4E;xoT<Xkpf1U!h9r_e)$g;QRaIb(CQtus;?7Er(12kJJnrq^gg%SU& zy@Jcyc+G9LmDqhosr1PtywFzhNN}#J84)K<y!Ev)8ZQu2hYE%LvTle2Lt2ulx?mkc z_Ls*-a_-o%x;WLe5}z4Cp>PuW!Q0B|fwEbm*nYL+in=cdLgp|r=`o_}v?hxI?l?%j z#6&5!47gBD?_Zn@tzSrAN>mdBk?+$-nTC;VR&_@iD9hlvm=v^YTSighIY=-oF|yUO z;Y{Y$cOWC2;4%{1<1EhklrwSbs`@k&_0pLmNgK1CM&Mlul2|{}vx1S3=#&LvsA6ZC zEIM!8$C+F6XR+A8>@IV*KP>;#M=*5q_i3vgT<`+Y4~lmOmX8gzHf(_pdd<{F>v@No zHPcZRoZZl;uBhAay~V5ySXHNHK{c_?hO#_`U-IX6<{_5dMuw(Lsrk}CQINIv``vDx za<>wSmrWyu&{5jzGxDGjjU=*&b#D+-NZ1f^qBA#PFOPbyprgBFyt{V6;!T*JzJ>Em zj52MzK4~9{Y;v#3i^nhw2j~?-T`P2JI&x_$EB=Q`Sd!%uVmDlPn6mLI$_?;>99Y|9 zo!8z&-$*o%xVaE{&I_0pmhjd{dBd8vuX=)TU>lz{{N%GRBgOID_y~ozdZRp0z&>P{ zYoj@LLusYGY<y)GSNh&w;ie_mlJL`jz0G{lV5ubO*k<*a9U4$#MdvgZ;U)&#>jC%l zI!bqh{GDJ(dYEQ=#e|R6K61yc=8P$t$UN6i6>pVB|85``k=u3HD|3gzT$ZktEMq+g z1x8Ht9CS!}O6}wlBTHwd8?w(@r8Jsb@>RmXux+vq*%76U0C=P+&{(~pY44F1_wt!9 z!b$R<&}O-)!>pp5d#N{%87-9G+iCTZAV?^l{42PtEOANsT9J+ybzVA#H=DOw^>}L? zgN|eXTZB*6nU>o7Mo0Po5B%ve6w5s{UlIBWd-U%KCJ6{`@D*C!ULCcnL6z#K)=MIU z+~d5XZW`h}4`M8OoEVXa5wR4CB>YO*{u16AFUfQqltRI0JRfgbF||;HiN-vaTEq%k zHLZw7XTbDt7VI#jWsRM?7bQ^$nqhcVpziN`9xdIdHbv$oS{9+8XsU2rG+)}c=EqgO zem`vh0)SHBAKx-)9>ixx|F<9<?1fGZ>{SN^R-Q_)>8o?G1qV2y%ykri02mF}{XA2C zV2Y&0>(HEDyUyA)f3+v~?Tef}$9$8#MSd4!gFiop0(!O?Kl<x?i*-~}M$kbL%hRT% zSF4?40B%!Xv8?b7@KdtA&V@n}(r880QBtY$2i}6+DIUi6(hv)p#<=WjeZi&yDg{$* zCgY4Z0~_vVu-Jb%a=#H49^)Rc<R?*&duJT+Y4vS2D=UOve><Y&gZv`c&E4%gfoIFG z5>s~JUMPepf!YYx_9KtY$QeY@vv6|n;qW>-@)KD_?d_5Js>OVQ%L?^oeX;rST`&O9 zRdzenQJ;ZtIzL%{XZtp*cV$NTk+P(ia!;^6E)#6p7!wb(L406Xd9r_K3&h%bUi~E( ziV8ZSY%Z`Cq6Oz?Ln&`o;;y|mBAE+JrV96*9t5hPEI-8S2Mt%Fkci&!AG6M%gERXB zB;tTv1c(frcaJU+7oL_@Jk*I}w#d>fy15Cy_Y{e>nE?%4n<2T%{CT#m#Ufv~sXXWI zw8b)mOa^cUQ8}HbJBxHrN3)91jl3D*SvGPhHLSl2HynHU=ms0GSpso3r13UD0MI^F z7@kqJJXSg=)ARSS8BB3o%XlO<fR9<tx-J7F<cLUA=99mCRW!iMK&cgwi&3`%J#~5s z`Eqn&LNKE9QE>hFHhGvPBUBrRvRlSfRNUz`#4YcKw5awwVt(Q<;(mhiopYdcl9yN_ zVY}uwl#Jn)O_BiEFz&;#kj%aUSA@cN@2m+sgK`v<GTM%e8evk*spEkX3}e$ZffPu^ z7hhj|-RWfk7|LV`Xu#q8QiTS1a5^f3)ezF4lOAdZ4!nrTT$NpJpB>x^T3W3~>m#!z zMq^HL^sjwAv$J#ImP4SYr;>*h7s*`%U54~;4AJ?c|FviI=_?0H?2s&zeH~c%*V4-5 zt~Af+H2>9i4c1;-tnXis%-tnlc6XOmw+J;9(2O$DF<uL%Q4FN}ib`wp^!qXk7Sbqz zb1Xu3L6X5{2wyhA>}U0}FzFrnjK+j%zS$VWuAhS&!5}&7X;#~lBUXwkv3kjzo8>Fe z0A%Y{%dbb@Hw$l$9Dl3l<6Ot+q`&VnEE+*cVy+?XY!2g<pewY12r^~8wudFkTwn%w z$-|M2Vu7~x-z%*pW7+-mB`H29#)k5^!OyZ2j_8q&!gemu11=!Bp8fx8Y)^ZFu6=6L z%$Oj5^Wr@!&x4QZVAeuUWr+r0lUMz=pTK{5_QQ_SBsH2I<qmvLtEIe5w+vK;<`o`( z#o+r;zl*)Nvw=vs9p|z%?YzoD*MmtR?wEw=F$=D<Vzi4z8$~`YjZ-Q<18vgHVxKfG z(ITnv^yr9A&36*1Rw8vs!~nrEX{-})XQF_C*38Y(D-Own_HGe)^Y6KViovY~s<`Wo zh(-f#Hi)zWo9**s|Mdq&&T~qr<bY~SRBW=voXo=)7&kr3DJD3wjUKS|ND~@Cg`V9F zZfQe?b1rQrsod}5`;iAmL{~);wwLgnNGNL(f9mm!DuhL9Pw|({<Bqp1JqWfhAGs2% zTz~W=W}tq2sL1D_?~6Nrp-BBq)P^Tblv$Ygb*jwuLDPHl+qD4tXTE!jDl#jqFf+p5 zypI`veW<;I+Md7B7{5%JR~X?ogjiyE?1k%Vqhx`-JKJX;=FTyysj4zCvZ9@*Npokn zB<fycIs}v}zddpoSH3`KL3VXwPrT02RLdM+Xx}cA$GLW;1nMk7+8#LxZi6M6p^s-# zNvztm>4eGd<>ZEE77OXqZ`JSHJf5YlUQr<i@QcGcT0%>%UmdPCu%!2n$t+0*>MPpl z)Y8J`EYZzS<=B5X_H9?Xn4IA;oHz^Xj!|S*1Y+<ORXH@d_<z!WL*Dg|4YyS1x&5#q z6nJ+9T2D><(H;MJrcZ~^$%smDA7ttcxS`$CyI|cUj*ExO&>!OL>R`ZPHKiTmqJj{L zY!5nvH}|ta8uZAs?2L5XWb2lqCkM7T{M-=ys;@TFnYSUGk>)=;F3=JU@RsOBNjJ|( zQ@AgkZ0IE6DRBPT6(mf_H6Hg82BP7u#V*(Og!M6ge@@ZOEPId}a#blbds%%Qf?vb| z9=Nuyri)S*X}Flnayi3Wq&V;O&#$QsYC74fu-3&4y3KlW+1M}vK!HI&VFIK`eB>6@ zvz5j2Q3}2{Wqf@~+ug5YvyJm?Mspmy4ck1#!Mm+xV>1JRsY8C@?{1VaPwG2rmSAsJ z=hDy5(O1e0{Pd+#UMLE^r?iFD8aGvX^34;u&3h`cRbJ!$I*-f(Z&aV4j;>f&&uEo9 zZd-=_YpW^Cb=eqFp<D^UeSLKvgQf0<!p(sl{rmJzpskQgx5cd9K921N(((?b_r#u_ z#%Jd7?0(7O$Q8#8Qf#_5{Tt!`LjTWu%ptwtR(X66Is=j!Bf_c(@Ek~v@eRd}41U$6 z23_n`FT5o>5Ai;eQ?3?#_M{^{MdkCT3k4tVp84XZ!Z(p<d@}k-@jI7RP6<`K)tZLB zm73fY4_7AmMHu~DByQo0V_$KlKYE2|Rc&7nMbY_trIqcGLT(3&8^c>R4b8tC;RgT$ zkt4=_O#IOTTbFQU{G7FWQn5;_XXOb;v~Nh3Z}W7{ynREHe$S4`js#zU*=-Q4u;(cu zaFtD4^R$|R4d=Oxsd=nd-0C>nr*?HxYl4;Ewt+)f%oY=82=A;RWmV5T*<5zeocjS@ z67Si&!2$rdc|23r-(~byvd*sKYK|~(gQieqoF@v8c;-%g0s4Mo8ZN+6KxbGAa{U9W z{x=6^Pm*ktQaNGczOUr^e~};kCs~3$A4(VGX7%|Ov=*^#7EdOpIYHZ#3#T<L*iph^ zlz(8Bm3yL?%)0L%h4NGvwaysmWzi5+DyBly6?Hs=hkpX8PPoayTm-9twq`9DafTcU z>k5<8ov}r`5|V({G&?7xGrBe*q%de6$t(W-(w!YZsD`U=Wn@I*)PqwCQ#2e`@CKi; z;WR5Hk3m34_eqPq+}d(to;qMU!xfXR@C;;Iav25@-}R?YXMgkN<=4H%Yd_mpF(s3U zT`e2Xw#i7LtSD^4fb-(Dx7VN(%Ae=e$xXSaeRWXL(oB#5^BlomL3gGB>7Aw-a$QVj zN|dCQG}{`*C<D?a=}Z}k76_0_Fe;yRQpA^&g}#Gc?zA+E#W{LadkNKiVniS@IqJSe zy$xuUtaQ;&-eVNBtcB8(Ok;Ejm8))cus-snaWxrvG=Kpb+E);7brpGX^f!Wk`(bp7 z(n$~a|D@%Fkg*AvkjQSEEQA~!_Y;}kdBs=$R#0OBSB1=>^|x9q^t86Vcak8Cu8d}8 zO;zL@mu}ec3w+ZyuAg8&YKyQ=PF$E1fr4uu1aL<-3If&lgWFEs9bwqmF-!MF#^)GP zd_(fo9vW|QR3C*Xn5@acQ3Q`ODB}7j$KOUI-u&{pKNj*_PWm)`_phQM2Cc2wK1Clj z5nwHR%6Eo#o;rakR$KL@p~=~{tc)f~DHS)KNmi9pnImUOj!*-x4ej#@*}CO~gImHa zYG1yiN=6BIx%|JDu(VHCnbRs$RKqoZ-F!KC;VBV|->Aw{6CfGr+<y&1LSXl~+{Bez z45oZ)Da#oA&-K_oMmAX{2~}B6#h@#$1od|8CA<|6kOeE3BjP^tCf&0`b$VXr?)LRm z#(tTbB!%hvf3kXe;e`v$$ixKHt1kM^U4*=8&AgzRb6n(ijaHi}o34Sasy2Sn+)0nV zO~~wgP8@t3*B3K7ZuHfs-2mO-9U@-TG(RRNh0oagsszTyo-uV4yE9x?_wtTG8HLz; zsO?MLSC3okrLa>`+Fay=@ECts=6*%}YzsReIYO4!4SS0EgL1m(MK%=O>L+|Q80Vk~ z4h*V98eT|^pAF5z=(KOM$L?muOE`~u>%16&eZhZ9Flouxj=w&>J0+JiS)UK`*e+G= z=t~+3*n=f}b5&vj0tgY1>_Mii3GiMoQYD<fSapxX>Jvcfg&%il+1Z6{U;KWn{72qR z215p$9PPbbMP15i%XfMhOKC}|wlMRNm(dg|m`TCY=hEzi4S$#+HBYRW73_H+YOHp9 zxD`HjSP+o9Wu~%a(kAY2z#E(Un=5>ij6ZVcj{iTbpnLaw|3cdb^+OiEug|!b9d-7x z99rlFW71J5!<N{*ZN5)C{gO%yMjun7BXCOAqe1BkY1hIvak<g{e+I~{_M{x=a8f0t z_{{4!p14uLz2pD202gMbtUoPci(LCnS?!0yGpiMsk}UaSr@G?(Qe9MQLi6T9uDcuo z0tn&l)ES7H!P~mjySyco%fP6mPD61xWzV&N72VhEn+AwcR67L&C1zj+;sw;)!VJN= z{{-Cl`&U-Sj2^Eo`(*BzB4xOOf>z~s)p}#B?uWNKs&}4W5FUJYf>S_?KBinhs=m8k zQ4VMP1l~s>AXNd@HEAmkTQy&QRzYF3#FqMf(ix@>61iCZSOs9Qwld<iK<M`AUIyS6 z7X{Yedbx@#8MDa*X1*?}>&-^hgJ1uF$wT@X#UwJL3;}=rCG_VdSNKo7mF)Y^QXHbd zmmC+8G3DRQ6KViF)+bV4?W*aTs5fpA(}@A<P6W2#Dv@ROg~o)F-l1Jz`uacGJ1~Pm zutd*A^qJh{!(01j?B>(d({E`_LZ*Di=|jPvYbbC%mQ-j}#t8x^GevP4zD_let}Ogi ziS5B;8i4Tmd<2%a<;m;`jCa%?y0;t~<yI^094z~d%y(qF+%%m*kw#?|e)2j~nH~T@ z%cj->MIxrdgrm>SJQJ}qDb=flKUN@*%-lC4i0jXpHnM1Fm3_2h?2?A4g3L*qbf3RR zNb#Vf&%hP9cD@b*N4bfAe4cl`d~>o<IewmmJClAt%QAhziW3t!ucRHHev=uaGhcR! z6jqj<9VEkJ4|%K3_T^qi-)Clx?@kwsuSBJ#byD_~a-AIiV6_h<7-3BJ0&VEiy~F9+ zSt!Nt{L|kDNM7TtYe6Z?sSdS@rg?v7`4ot+a22O3js2b9{t)v_K-CPbT2_!T*Pr$> z|L2V&|54L-2UZBoj=bNYL0ah9u|k#j{A0dX`n>OzegQhQ;q^bGy!7%2PWZ4oU1JXs z{%!fl{;g{PSav9LK*g<aH2E7o&Gq$jr=Ds2AltAaeQvfdUsbm{PT~29R)CGkP0W#R z;dZSO7?c=0Ey>J)3G&i{7EH1S(UL%zre`D!_onuOV}NUT^j6sK@XMQ3Sy<%7pZvR8 zGmLZ8mnX<*rh3cjQVk~{VzolRJ#eS~+|pJypaAQsJ3}A+b_7zySYxF37VDVS38D3M z5dF>O*td@ry6f8D6bbtRIefFxovL8iCfjg^xoY7SW$f}7m_)YyMDy6FJSQ`OdpvH3 zgf>X}w@BwpsY}_lof{bxR|oQY)^q_jUpqSX$n;B@!IRU1G)so&M<NgcJ_fM0=h}j; zDn{_!`YAHug^RN$4{V!ri^h1)6%nWZ>lVletFu0(EEifvGE%=XrL9B?OU7)ZsV$-8 zu&iBHt|=|0;^HSE31-W<lvayMOS2n`eu}>Rp<&GDe&PLg-#pb!8fWv*TS<d8<aI$S zlVyB9yrT|8kpMeMK0C_H#E7*Unkr-Vr7;Xt_N?8J8HIq6JXJbc()F<O8!o`vmu;)M zeaW&c)eDEVBqxyGmXkjLa_^@9`sCm2&<W$Ue`zfbKGd-am788?POO9A`s{&Ne&qxI zQ*+1ue>5-MmN|Ecla`#tw-}1m+1Zg{7eV8(!mA=ioXky<Jf1nQ@W{nOp)oG3g1v{& z%a76nq}fObBalP}Vaw>4?^HO6g3CZSzG#0h$v*#3WuI|5G!z}_vY*)5PlSi1q1X2G z?(*g=J@mpR8r?^VBvBRpZy@hs+Ut4#uyO8BI{&xBpss!8#0yL+1Lh2>EfMOBv_KYH zH0kXAB1jR3+%^Y)9H{$j$d>vWiZ8s&q_CsRjT+knlV)#<<iJqkUBGe7FBUSkhL($u z{L_$hIY4act|Bm)o>4R}EYNdHoUI6`_+pNi)52Xn``HHnueesm;+Tphzg?PkYnD~2 zQd~h|qt9%Zs1ss)H{CA`l*WISJ~~E4I({u5$d<ba%UH4;XLkLs=VfDDoxL5UNftlv zJHEX`G4}1luy6FuiPgSGGh*_2%K}whXMLkbuM-MddUtR8X@O8o<8@UjAhY<Kg8h@7 zox89)PyUNdx7nRh_hp^od_mUbc><r)3JKRu`AKl8gCRljdYptbI@$7MCfL6pS7alJ zET#sjCdbhLE^*J&P$bebe!P8GA*>?O&V&i0zFoa_|AOO38(YRVc@J-^sjsOUU7|fM z|3Ku`Z&h0(SpXrmWZ2xhRIHtxQr@7K2eC?=)(8R@fhQpQ(iU{xelx|7$WO^n0U1l` zF;-j|!xA!4^%kA-+Z?_aQDpp{Mr?5FvROpyIQF@~#x<lB7-cU`Utxd<@;8dd*PO&v zux38FMi@=BqA(_8kh91!UXA9^XO1wMwkY>EF5S5KAU+LeQYjE(%`=avGJxb$<Itkv z@ASv#A0SwaY11lm48+B!vOPUD%{*V$i5<%;k)_*x{)GGhln>T4eZHLK^awHBpDNbH ziDO*Qt#j(30e}BlF4%jc*I@kZM}Zf61i)(j2^7?|76*>CeZj^!<UxO@sK^|SZ`+2e zKa5!DPIn>{Komtaca<$rrU3=|{eN6q5~m$UocQuC#OVh^Y!f|a%@#{qz%`|?{2zH~ zHM9fAp6}ZrFyCL%wc9sVZ>mXF4{zD&<<PnekuUn{DjXJH7Hb4KI!MJI#V-%l136c} zl8TBr$b;wOXXXAQ%)h0a1%ZHxT)t3Ia7-oxyM?YW+JZj>E4N7`><@!L0l47m8BR+e zAW_*AX`P;mwc+slGoyDSduiF>VOa~%7`$}7iR_*+UU@71Tb4Msf@V+UW;irk1AJzY zD;EDe<sTsD0U&_hr`*2csupN*D^oIVppPi{t1P-R>9RcRRXR0C8YQOs7!v8LR(^z) zyLn?Mqj3fbaor<@(Vs3S3kwMNz!EiGVYh3qYooF(D7fVBVi7)+Ewd`HW{gRN1nI(G z)s=qO-;r!G|An~xr1d6uMyh8={_Kw?1IMs(2U1*5b#02z%FFwN3+BjIARs9P`G#g< zyPCp+iLuBVE!Gmvd3L|zJKv1jBb{9UfFEA1I6nXWd>#N{@AIpUHn|iphl6Ov*>TgY zyZhUqLbntG2f*zqtvH;$rO|F9ayUW^#ivp$XT8j?S^MCtuPhUO8<E#5YNG%Qe+jEb zahD$HVF992v6nw*7#u`_ENtn#iQ5Cu$&tGHe7_Ee{^dPF9aRG061alS1Qh4~J!tRt zg#1HtoV@??^rR5#@Lh+l(!O%8TZ#+!ArA|8oIDdTNx1mn|CU?JC?7yx^je!PI|i%G zrSVcB^kb8Vg%cb=&J~6X>>4}?En+MsiYjHO+bko=_iZ<!KhjZs_)Ei5`DFfNgtBJZ zM0}b|NR(F?lKLtjYwi4L;1iJ7NeeFXcvD`9{L;AufBbQzp%@(ds!xpn@S`zkET0d~ zrEIjnJY-dN-`I}<Dw7DN_$MTCX_NMU9Ej@Nv%qM8OsKhk`b;P@%^ukmSoCy3-Oqz_ zOv5d05N&PAD{ZO%j;mAvgsa+;olJ2BAZb}2sKP@p4k7iE0`CK}rF_o4h+d#nZOY8W zoc1iD(PstW*YpVl$@ET(-JGzvq3793zra~FOO$m=C0C20wqhuyY(8-A$##YdG1;LB z=g<JY*;jxO1{kGmZiRRS^jHBvcz{`?$7#j_KFA}*A8Qn;uW169&922SS=6rNIVeEV z!oJ^MSX9O>3xPx9)AE%f^M4M!hH*7jOrdxj-hJ>zdrrhWTJ6WaYBz@WeH1nbYrB+{ zJ=@-g3{aW0?I(7C!+=35YHQe30#K@)mA~lBWark=7f_T%M5WFy0j5|}%--Z!Du|^* zBPT03ubW2f0{+wubaWeRHQ@&8#RIP&4=^Q1wDd8V^!*O?Mo<mNRTzqmS;U*yiAxtQ zUq<3purK)Nlu-$P0gH7CRVt&<h(=yf%GNRZB`*L#v@#Rfd1!+ukmTxs_hhUQo9LjU zPJS3cYd7*Ap#aAF8hf<@jK>2N?WYeLsP*4<j`*j@vW{M?tg78@braNsq!xh~O7Yir zafHk#ndW7R|Em8lb4aY4k9J^#`vD?Dh{>7y4HNZbwKhDJ5zU9F?-4=_kgb^Is(PNH z5b$or`$~k@+fHJ%g6@dWbJ<1)_k%I<@7mojiepmf0-IL73XmkJNZ7@Cz!mqKb3r6P z-RYI&`u9dD0U&>-(ltX)pW#t;W3#ubSpGh)AFv{m&veoxmjT3_m$SB;MJ~VnkHzux z^r&UO=IIMyFvi7m5ko}X{Mc+qDMUv?|J_6R|05@aW{pS;#$@Z05UUKagaT$k*UlK) z^xDWx*K`5FjE1V%@~4xR(0;iAO5lB`h25voj2S0Gi4|WMSvZy|9JL?t72h?8>c)Vy zi)&=tKtyJ|-}SL>WK2l$_GAF?_j`Y*B{hga4VQU2tm1tZM4${9U|ciHCw?Wu0hv*m zQS@00S&+q>!%C~HR@BDS#px0HJtCxPELAW$c$|uW(zK?21{_1G;&KsP@{cUhwBOo; z$1&v<_n}ipf*aZkTPZxRokN(X$G<9Bhm6t|McQ{86qnin@D679sy!KCyytf1kgi-~ z#8Aef?GS*na+<P#5U*X%75>oYJ2|%?2(E5S5~I{>smc{tw{;_zy7bUjJ1`;sPo^bt zIA-2Fz(e~gxVEbY8rMkQ#y4!Hlq2r3J&s-YUMM8TyCx#07=u7yKAXsz@VY@aQ@foc z8w!f1-@Xl7S2xmW%k_n9Et)}#yP~-`dmuhxR<O~8V>JDuO3=0ZztZi?`61$4b89O1 z;@GT>W45Pzk;jTdPAci)j+z3>F+B~ZNXQ;V*m9t=d<V#k77p`^O?pzmhZ)h<XiIR| zCX_;8bhJk{Ti~ca8NF#8l#v5k@zZqWv9UXNwgD?8-Fq(&y}7FI`HzWsZu+A1&)DWR z4hzQ!pMvz5YgX@woUVZXkJa4Sk`?f~7$fI%vqu4=>y)?G9z&(bf|=vYakPlbN%Nwr z*RR(%?h>NRt&vlTSdtmu)B_(%2CnRYUE2+Dbt#1yg*C2a!}S39&8s<bs=c}|a{hU2 zVF|rJ#3A@!r9wVavZ#l`O;v^oznDSlR*3d7n5T=M4U4>f{o|ic9>cqU2MYQ7$B=#m z_cv1*ARxtW$9^o7zChj_Twu&Mz*eKKeftQLADYFec(*>ULZ+Q~QLyJEAEu?6j%)MC zg7GRcaqX#$@Wp~3J!UvF91{OO924S8s<8j;20v=G;zY0?N)S}F8m3aZ|Hsf9<HNEu z9>miS#R|W2S4{7|zwQynKNWuYOjKP&b92A682!vz2q-|W|Gj#`#L$D8W2XlH|6eK! z;OlIDS$%pTg7BrS>I*lC9-$u<55P<HD`hf3XE~H|>y~?HPe0)mxD6xbEJFc~zia4P z;o~Lq;)>4doXzo3SX5rfPlCnt!}`)AR1)RZ#+2m~`zr;<h13v7YKMau7kxddSRwSK zVgw(VQZ=pr;M->dE;%B+ml!z&f8=7}dj^hqc>#R}*GbZ`Ii`4x3#>;|-MNixGyujZ zru{{@%l1x(%A!S-K*t2Of!I)({OZt8;~R<WAONhPL`EoRH2HF;a42H$tD<;Ab#=VQ zsBOk@RT036sBc3JCE;&FrHkoVQiy+zQqBz6CD9;01=~J4HGuN<#My~Cp`p|XxswVB zc$2%`-2Bfi112N^2n-A@+t;@G?4?$Wb$VSY-gahhfmRrRYy58ir3D1Zl>l?3IfB&o z4PacrwR~^%`{CV7e%$&MU<*J66@ll7uOn$`qwy(Iq@|;47~)jJ0TQKrdm06)$Wxn1 zUpmKqk2?p_!N#`mZ@W%Jr{Qw6y#v5D03k+*JCG`c9BDYT6?uDe8_PetO_Hfbo9F%h zA4TC(f-WiN=JcBZx<IC<Cfg%dG0?spBv1(<+bj3}@Mwxx5kasn6@QmPCI0<pK|B?; zj8K}q6*UjjF1mw=?@-#H%O(HDh;b<hFf|8;CxD**a`f*xJOI)EPwspjfp>Nj7f=3R z`v3o4S>J#2bHn^!K=$?ABgf$L|CbeGkHK~3#X<geM;IZuMmPPwx&3}-6$k;P1z~ar zSacDh6WW!rSTV8`GE{VRvVp74ZD_OM5gdY5r6=IC65Ug&yz$ni!{*$Gw>q6$g)CBZ ztv8qhU|iq5a{=I_?Bu%0F~S}|S@<GQc2{2elQo@lB0f6m#Hm518z`F=3x+eml2{rN z1W8&zRC&zUDHGjLmEAdN6P-L;=6l*Zf_qOFb^Uu$aPt)@hIrL^1aoZoe5B#-c%DsH zD$L?{J?(?T{dO~zWW7zk31el>@;$v)(Lh)BSJpaSdAY*2ln<sTMKh18_oUn>{fiCW z8gc9D%ce-2iP^ogjG<xLV$&L@6Y&j|pqZ?MHAsnr5C~P)K;Rqz?p!hkBSgesBo$em z5xbx0VPuO65ROqDQMni-6`xa2u&Wu6IXmJlr)q26w5edE2?0&Y3l>0_h>Ug*2!s)k z72&;#6EU@+XKkZ;^?7m!ic{C<^$<~m32Dbt2o{7+-6RAS_o5RQiP|JzPQ@95t=xY) zdlhB|z@2bs>nfRZhZN^Ue8Zq#hNct<2xSX`lAbaTmJWH$`h!iZp7e03ZkXU^R4&5U zCd9q%{Bhm@+ISWRP*BrVfK&LC&1Q5c#W_FXaf;R&Y?LEVhB@n!0n~YjcIdOWi`T2g zd};|;Z&}~eiS!zI)NvIm*Ajmg-hMx`4hp!U4-Io$$r3ilr)XJ-pi9=Kf`xj}ZVNm; zc2}6|*9DtOcMOYYm!S0ih`6Ld-MArw$vV7$VTm0o<2$a=3~j|!rff=gKWJQGA3(bm zW~Inc_ipDG#f9gLP3_<o^d(2l8tMoIEJ7kNV6HCQlB@Bd&Z4BB!5!<Na6*>frO~hH z(RxhZjtUEs#R^(2FhHp-MmK6O?^N^&en366oRU?TiS_nYZCeAjYgXVy&uzs{xfqm) zs{{VB1amVwFOIyuzgvZqP?~e2n|^oOUWqeMtK!H4+mN(GK$i6<?Bdp?VnyT&eD`+G z)zGH7_Ey=_Q<xQ=!Ckpmfb+oJmDjH4K;$ieE*O0WQtqL1u*&X@x~gjJj$3Dw>b*F& zQgLNuJk@!$$z#)*&rCdc+|}_m060PO+>_2?q>Ly_*wdrbDLS|cTY#Go#e$H)q;?Sw z=7WCA7Pin(lrm%VH=l%rj-E1}MH%09G09du9QHzT^@Oe@Tw?$OkMQNPBlkE$so28K z^<2GSVHTK{3wSo1&xCV1hZm$W>C4lfWEE!dw>1mq&F*eKpG@ANy`3A+R6hYF4chXY ze*4HBuH#|Q?`{ao&L&jQc#vW0(r^@P*QfVU!u!ImZ92*LlSpF*iC!=!m=zdT4{d48 zZTAsO7Ge?3mNjZzDV4Bz+m+<oFQ9(<eg&1@ob<amA*IS<+CZSAt0?U;A>B-B=VTME zee{Fy#4aNDVfcteN_WS#v~2-pe+D5%fwpX(TiD5R8o+`j3?ML9>w^%8mHsz2$?n7Z z!>fZnS8|_LWfCptfg)J3wPa$ojHyX+m};=gG))`m3!^FY77i2v`1DTyjh#Kgt-riG zU`p#>p{VwnKA2C$%<H*rjKb+Q-c6%9!Gu>i%##+=I-oS5NP*OX8;QYFF=?AbI@N~> zxk(^CPrs-;Xw<T4A45jYax2f!Z#^LuMjAMTTx)a7>Oh(lw5tVRt)^g#)`Cr3>3^oP z1uHtSFRhK8G!(_kzI2^U24*Hr6yz#55X3n{n5r@g_6q?Tbsei>IX6}k?7kUu3rinJ zNLSCXSoYOqLJ;{BO7L=oQXPM0-F`pAfUPpAS~zai`XCr!0HLBKWw~TX&la70{JA&5 zN@q)tj9&?*qLv(Ul}dHVaqTayX-(@Rsn!*W^;ckcX$ms$5RNlPor@wqxLcLLS`F_O z<8F+o>&FlP4@aC&Sn!~dFd4+{OPiQ7tj|2VB|Cc;Pa0*1K0N@;cQzRD;yngpkEwYs zlQ6_rZ3Ct8InE!si4}PM@~ts8CVvqHw0wD?xpniJ&gmTPAF~BXsfkW!HVU<%$Z(e% zn-o^W99m8lpw48T7Ym>6jV{d8Uy9~_S4f5*;0jD^*g>%^E-``he+L)NZ#b$~3%PL6 zecadz$j;RK7!yw*q#r-cNrok%(DD(=>3z~*!LT+dnEbydL0h-0_sgf*;JNa$Wp*J@ zrg`Y?k8g_bv0uJ}kxrY~WPvUF;F?-b`!U1K1hK~JgW+IMm_DFDWgsGq>rC{F8UM`A zer|mJ$0~qiiCAZ)*m<V)wrTA-?HebAC&bJ&xbt(`7Oni(kMgOli?Y8zh8lSL#Up_y zD^K3UtS<`#S|tTfHQVci3!Yc>!GI`)X+3vgNrh~VyaAc|8wyrg>+Xc6yLjA6sXzUj zEr~5{AjJ=T0Kfj!w`=aMsrUG%-{(9OnRf6#-ydD3VRqyi+9+g1Z8b$uJ4It^KX?Xk z#zORQyn8D|2Vy7dQTxm=8fkcaI{bgH0!Y3!I!+TvnO?mY^n1@Y>+T!x+PmrTE8)J3 zp}&8*WZ`HLVowqiBtnzijwCO}U``3@593hCuAY@dRv}1zB83Dj5I6y2SbwDHJh5`x zMOPC70y2J={Ps~gibK-XpH{6py87tyWz+7B2S*3)(<JtsOqHgD*OVxNvgmD64V2mt zLY2$i9KxUo&zi}R6o#HU<*7t0%zAhA#oAfP%HpitB}H%na@yEh(vh1mQ6iYRmxUft z13*G3)#SOS4=Y)f#8I1rvI*<98tIhZ=RPg6jkA|k(%S9SX2`cO6KVQL!lAVc1b0vp zQhRwAhl~FVXZNcxvjDc>=i;o&IS*<6%*WSY;;R?wkTnbzvg~aJggGqg$Iy^yOs3m> zmFEx=-MF=wsISVFTGH+>{l2$z+~e!jfX{PQm)n3HlYjgFIlZA?zq@jE64|H}FN!lp zM5&1iD!93fDsS}4DUYNUdTeyeM_brPV>$JQ+cQnz(Y;4iEljwtv1$UGJt)Gz7xK#o z2fAl=jSu>Kt5z-MJ;x$jE#Niu+RSQwE^pkwq1lL2wInMvk)rMi8ExRks9p>mm0TUO zpO~qNqz1)YXo}!M))6@fpcPdkO-$@qeDd2j2UjP@M+dD&sSpY{4`q?3>(JC&BY$Nd zYgf%<Yb1)B2bkl{*Re=5$ifPRZUBK0t%N`d%-DnWrO^brsnQDDlOeo%@8<aQI2rdb zdQVw+m+w|*_ckF^GQH92Jlwh)MZT*h%WuR=QR2@O5QHLoier=pVf78|K3j!T`I06a z%;sPmL7<Zl*_*$ya(H{Wi|0)}Mz|-tLVairk&-%PsO5Spfqh+vGT*MJcK33_Dq1Df zP=Y}6+l6Hg-At2pZc)q!`Qq+92CuvtEtCe&#Y{~}G4)!b3mn~Yxtg~DKCA@0^~KPy z``k>gLBO&!Bx=HbI*%8&M4d|n$CGh_aLt;tE3a^Dr}AQEgViXuK{paI1Z3I=_bX$l zv=^zwR*k>h+dN&(TRNO9tEjqh`*<wkJ-<3)(3Gw-v&GU?Dhyw4FtV)&%QcH~qEk1F zo2V|TDc1oG_Mi$Pw6-cRH=o!kooph<EOdyp!jaFU5)QbUUFka$%NXDX8;senbUSoY z$z&~6qhfk@U3Q)<7nUKaDdZh%X(^dhvh$yCLyO#+oh#Q>{#5bf-0BI1hTW@kMh&F` zJ62_r6!WQMJZeqjEqP7<CD*u>@|=L%w`)~*3sR9?-x;WD?967lkPo~mQL}<SZMWy2 zjeS2-STYIS(l(9G_=q&F;~{6NL-=AT#`HSNwhmOR-w+TZ2eCdBK3MQETNRye)9Q(Z zdFI?}1LxidmK9amU)-@LVc`;K?QTOiYp`@rJfdc{OD0631q)TvHWzKx2=f3`&stJ5 z0+ZU447u9Qe%Ey@GRq4@82<vsH8C2kZP1FPVHY*i!JHjJb90qX%Ev!dA{5-)7g^gC zn#waK6;GSm2wBs-vyRMRud~pooVuOyj5ZJaJ#e&FbDZQu$&^w|$HGE1cEwT)*sFQG z61mi_U>&UsJ7_Jm6C!xG?z1A0dzFH0M%pL;=34{B-e6$Q@v!dtI8_BPcUN?OcdnSy zRHce*g{Oj6f(FugdqLV}&@1I5PENx*t35prAP}=b6r)x!egqD8I$wKAwFGLo&YB1~ z$DzO9=9~tvGXcZw@0l&kSnZ5of?h9^{t>cmL&ix4+^nPLQ$nrTq~Do=J_87H`$hY= zyH-!h2ETd)5Gn%rZ1~!1s19Bn_!Uo|NP`N0v6F3JAO610U|r1*AgQ0wzJ*|V4AP?# z5p}&~%~+RFt#aN|6J};s=3<8O#7Ran9W%D%egwNVtsi{zwo{L)uPwKBwyNT62C`bF zR23#0m#@BsDul9v`ukdKzE)BVUS!O3TB50zM4rK<QZ<vO)?Y(iz5VG@$p#U;zG40A zVkE0#lMxw5)c?&}0DkcmK<-PANZy!EzfjUC^QZZuA-6<qCer>PJys*psgLC1Afr6z z&s$S@&XpeXIo^LOo2+m|DFk90fh6fMkvGW9>3{hPAh&?@1JHRTp4T+Vrihh1A8aPu zdU7c8&P2ih*ak=?Kmb50d<9~FAP6)?xCM}vbO8_qK@d^cNHUjpA`i6Pk%uIY=25<W z9;5rv#O=_pONonlR-%29XS4I;Qc_EbyOsbBGz5^1_@~b!yVm51P94ruH`c{Gvu&Kb zz;5wN#V+5j`sT@Z)FUUC_B!4_?nl3Lx~31!K;+t4+RDp?HITF)cc$w#8G7@OT|9os zpMQk5wc~uD&$U7W{$w;BeC%QTz%}FKryLygaXV$h9XMl|+%0~tusWh`qu!@y)8eO1 z?0nqly7l0vRTanl?ajDeaF5B2!u94!qr?xI2#1*1W=&r&8C}DbDNnTDqbBIvQmK>l zpL<E)Sns$kr$j!Q`0$nLHH9x;r@L;`)QcjE5-RwYv^Mx)vsrrS$T6{oUg2BCFos+- z{q?k_Ly60`8T>9>x?|wTfkPuWqb?Jio$dAZkYkg^191U;Lk{!YLtZhgZ+2;$fQyn> zj4~nr9<)uiYsQ%tFPpJ-UJqMm?ub`dVJ4>~WMEq-M#jd>9p@hyJ2eLk^WqB{jTFNU zP@6^B{_;BD(pDf3R~5Tis#yb2QWu^L_180V6WnJOaX@LF%dOun${F6&4;^Rb?WzXA zKZ%D4hi-byC#=i({fYWb0=@SfS!dy}SwpbQoAmM?9It!FrSQ(KI7XOIA6a#`Lq~=( z%zV!F=@ud=tH5TSW>y!#@=&ii)>h9F!fjLgR$f%Xh_p+N6Xh>Y$N{1|-i!uXvu|<j zEgbKrwvG~JN5|<>92|?+>#?#Yf)R%aq!epj8q@lELtO;TOYaR)HdIdG{@}Mt3rTRg z82ThX&SH0Q#TJn28<VeyLZxYG>*(s4w?Jyql4UDaty#Ac+Ghl(rlq52U}R!uK_F4A z$@Y_Jw_($kZ98`Dad2{R^I)(7f<nR~qGI9_l2Xz#vU2hYib~2j6;(BLjX>mIK=Uw_ zMrSZFv*HLxImYqy?D>%`zak&qJ-Cq%6+`7vHB=9s4DC|;yM;o5q2RTJ;QJk1cKheZ z&*q-tHY<|L=RiFE!a`mDxc8-&nRnZDaW3WYFDO7%2-I`HusVt-pL_VbmwU!PxF2DI n2OVF?Zva?BJXPQ(nw4k0m+j24>;DhV^bq^>A2=<9jQ{`uLNZi` diff --git a/docs/katex/fonts/KaTeX_Main-Bold.ttf b/docs/katex/fonts/KaTeX_Main-Bold.ttf deleted file mode 100644 index dc0185a12290672e819e1d62ac9a955311c60341..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 60784 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 DvjTj6 diff --git a/docs/katex/fonts/KaTeX_Main-Bold.woff b/docs/katex/fonts/KaTeX_Main-Bold.woff deleted file mode 100644 index acf48e66893be130a57bb66b9506becef9b72f81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35464 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%0DBe5}C#ke#N-e*jJ!67~X2qoJ9|WECVv5YL zo|OF3Xxps&ht5W)UgH5X)SVqL4&f0-Ft#QkjA3F;GniX52f8rZ+R;y9oZb4Cm|=Gd zr#U{~HBR$68E0zWlLxD-_aqe)8+znQQ>Qr-OKJ!Gq)0C>lGXfLT#%sl>Qb4i(GAnn z3q03{0SvcHX_8#swM;iCMljwEvLAY>dEYTHW8FoNZFxQn)}p`giV4=$D}=q=@=lXf zRE<y0On1F56mPj;(mr=^puej6?jj9!8kO7{I2-zH_&pQURTwo)U}`-kFr6k!Qkjz? zy}u>nh4fa^S<oYTNYJy-M};+Zp$C0<SXW$Df(Bs}O5xs?S|c<c#1qO9jID_WeR$Xn zBnRqhUKzn8s(30pEb3nWJ%}uX=obYnt))BZI}waw0#mE!L4PtxZ^kgUW~dkCObx82 zDGNgvdM_$rPDpZCz;g0fL@>6N0Sw_0Mlgm6%qA1Rv?efhMhnNYmSqkLSpES49Ny;u M000310ssF14<m4J82|tP diff --git a/docs/katex/fonts/KaTeX_Main-Bold.woff2 b/docs/katex/fonts/KaTeX_Main-Bold.woff2 deleted file mode 100644 index cf5ababf46d8d4d15ba26b1c72fdeb39500b3d1b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30244 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 diff --git a/docs/katex/fonts/KaTeX_Main-BoldItalic.ttf b/docs/katex/fonts/KaTeX_Main-BoldItalic.ttf deleted file mode 100644 index 4346f173ce347459e433f45d1fb06cfc74bc8eca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44496 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 diff --git a/docs/katex/fonts/KaTeX_Main-BoldItalic.woff b/docs/katex/fonts/KaTeX_Main-BoldItalic.woff deleted file mode 100644 index d2cfe4e319168499f33686f3b1524addca1a596d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25352 zcmY(JV{mT28?N7)yS8oH_O8v{UE8*8+qP}n?XR}&zID(3&pBUCX0FM~GxxocHESl5 zCyASagaiNt@UIv_0}%dK*UbOl_Wz#$|DvG6$N~Upfd7js|KqR3qE@+)je-5Ym?i)K z;sF4FY76;R8ydMf69E8!_Wo(8{=oy9gIH*4Z)O7kXf6W)5K#aCT-qAi=AD^=(?74# zKM4T(|AAy??P2=wU1I;bm<9k64DXPQ!puz!i~#^Op??~V|AB7myleh12mq)b{c98c zgAAGltjOHP+5MmHA0U?h#)75jFR*ECXY@~}%JeVx@9RJi;QLR3HU{qh`c*~x=Og+b zAYJe_wgxsP0D%6#aeyuW08p5_qou<3c23R!fWg*3AL&1GlJr=&?Hx`2dH?+V=kxtX zWMcH^r2EE94}?G7Y6j6~@2!%Oii;Yn6QTm*Q@jK{LCm;evx$eXRw$KGD_Cq2*Yt$p zOX_~AEu+_4j)^Hxs7dM2)qt`q{m*k{m#4&$@qXBOLTT5I*NPh9(nloGIg*Gkfu2YN z*+x|$$UwzH#+qz3H)@q@?}24m(2dnk3)t@O`0gyESGsS!i9`<WZyE@qdfzivRAA0! z2@H~qzb%T$mT0hY_c(w#I;^P&oLDHAL}<Ta-#;j94k+@b+mmT^x_M278KHDWrYi}X zETT#~GrCp1os)O#s_tN#tB&L3)U^GE%`V=5cua91iTo^vF+z7|ii1_+|3kJ(Fh?F0 zga7K3Qq6QU)20$SP9EmgB&G)n=x#lWkv%n<v35!zdNbe(U_3mPij+-y+nH8Hf$8Ug zB71Ev4nzUP#0H3s@5|yyRcX-_m$5D-)E|*SP|I-@Dc&=<_)_faccWdXtadHergy9Y zjNw{R5-e+0HKDZ};Poddzh7fX&r~0Z;J8-!oPBF+DA{7opBT>E_@$r|+xmKwd`G*I z#>QEXr0jg4n0&Dl0JT=2TAGrP(E@19NTL*=!fWyU9!K#I!D~l%OIe%*e(r|>A9J|^ zzE5|aybbBL&NxmB<>PS_w6IT)P6EnkD5jm;>otfw@jsV`5}^xnuH7&L$g8|0CXNy@ z6P3xcvUlpx)RhAjUkXe**K=pz&bf;~JYE`W9bI30<EI%r<=a1=9%RjA52+OCCMpeX zlH<BO9_}kCvWVjB&XWoF!0ppcjaqj&4n1HFBXDfFqIbp*PSEY)XuKSFO9`7<><6bB z)s;vfh@i@|lVx;HS?!T!$W{$^c}xkiZ#1Xc6Gu!hfy>*ZAWW4y-=B5!^Yz-alkS)M zt<$_b?=U#-*7ptf<aO^CujS~!rC=K$=7lI1O+1Fegu05p3Ju;M9B&`t9V7)8PB9?? z&1RUM*&r-CI^8tPwl2P{GZc%j3-++~Oq-^#)#my$5Y^?m{g$9ke7lwqPdq_49SX*B z^-_>4JMG;02o?=pxOat!!cw-wnT5W3PfP&3qYClth}Axz5A**62Jclj>+Q(2;Idq? z(tHTVyZNZmp2EllMd0_XxyB@-WL!5RRT@^5ECOdq-_+1*>5se1o7k?t60J&Q*o7@z zH3b>Aw6t_Qu~d2;4s}p71%XO(tUVcHx>b*@;EV#waHG%T^_LWK|1}`V0J+JT;ZGCw zs0a=w6I4>UK>+R&Z>qvD+xYFiX+f{=Y)#<zk<9st{$6g#GgurYRePMmm3wOe7++gi zy!Vqa#nmun9vxNe+xTT(8Kc+YRnU>uz7S&*eDd|m!X6@_*o^zW02+mkAG|D=`x$E} z<v}WpNw!k2G#4{l?Yvm9t!MahI0L(nsDttmk^wv;ikJed3=AfW5GaBfqR;5Fd;=-h z2&SGT)+fn6pXa7D#dpE#f(~A-#2{5;45z^FdJf32xjeyLTF|%3y4SU^T#t)URG>be z>rsF2&c)XBTDVaX;vkghAdGQ4&@mB4qi4IYs21oZq{#Z16Rc2H%St3~=ia)07a<E7 ze934dwZEk2DCF+cG*ik$0)-+;Z^w~&(S6Ypxgw#GgIM{!9*kF=ZO3t@U76gb%R`}~ zeooo@&=t#IZYx<twSBXwB$FXe>2|pmZlXMrO)Hw9N^ev4EZ@8+;$u;*Sqz;nmWu83 zsBl%Mt+?MkibJ80vN$4N(ojx1KQ7G6pM+w7Pc<dK?TtWPs!RgenGp+Z3$l0WI58<p zGc(%1<uObXCjcDOK9y>8b_}z{!JeYe(bAm<DaG-$W=5Z-T%yl$9I7m3<czk|3YI*T z1N1-c>$>x>zLjRM(|53xv1XMz(xoV*lvZhBz-2`xsTZfCD4{OUr15b6V770SW}Y`F zrGXF&`GU#!x4FL$lF$5^0V#P2U-VRs`9sz_33fosgQ@GvDZZ*<`dh|D&2p-ST)_of z12*6!)0%*%=ZLzZe9K8mWA|0A?)R0aV}q<=4-*Ke0<s7JK5bFiM@NzUHYJ)4vJ@pY z(jwrao)cGu-feRHr>Sk^St@z5e6w78PL}ugL0@P<O5SU%p3^SKN>i#>?Y(_BJI==m z<Cfn;P#PO(@l$88M$heRg7I8~&ydOAl>Q>_AK1VDQH~c&Dk+QAU+Bx^?cg9Ogq3s< zr=H&aXmVNO7C`OplsmJ3f+b}rc-y%jauU9#hB@b#vdw)DtoGjC19Rc3l748<_C9)U zyW_HNMr8c$ox3BW_lWa@JzZ56jZVlwRPBW#gKbe{^2ox8P1r|1NTud+OT{we<8p7Y zp%9~E&>WR0wiet=Di|D0L9+zFW+>sfPQS2ae9wC>t%8f^<RD}zJ~yr8^Dn_%^RYUW zKRq%q_e`W1OUl*5H!<)nh|tHUZ0TTFm1#p$xjAo{D{wTDc1ZTVo+Yp-rkkZ(><j5i zf)gl?XlPkdqb0$8BAhg=z;=B8L$C#!;Y)%ZRq6k*_4s2-#mK&lS_4#{U<jDF_6ZVb zX!w7F`0M@^^2e)L)h!dC7c))v(zf}V?YwdgG*BW)$47Waff8tbnNT3JxmVNQF0aa} zSjj)>%=@<F3ih0it`q$w>~@F~Qui8+91(gXDR@%lGivwE6C;7{JJ;O{%l#lnCT{wk zxXvr%oq;PL2Cyc#4GYU2D0r;9u~RRXigh*P?}lHtZ<+i3=yb8Y6jj4cAkb<qP0;qT zLdspFc-fLVOSX-#K<m0_ieOw^T~6CB^iACjiV%@#$cUN3MPx-}CY=`ngJ-3V<}wDB ziXtLI9=|KwlJ@7oGuy(>FCw)604-w{(F)#Q>tag*71ZMPM#Q+&&n=bbUL0Ix@W|eE zQ!>oGwHrn(jSQUlLK=4W0YAO~DUWu!LO_FB&7nn|>G{CDX6*unNKJvjyK9WlLHW$2 zf-%6mB7gRc3Q2|1A-rmd9O}Q{Zdu|@ym&?7R;q0x4wL#HAj{u8IH}o7w|`CPik%Ma z=88(2KT+%a2=oUSNfEF~Ki=IAkciL%Y>~tql_G|MJBxS`$Ch)%{n;+hiz`r}@NV=F zG$gG0H3QhJX@<rH%f&(|Co0wQhVtKikTR<owt=OkzV`H+Jno-nl$Fo?$Zo~`9?a;m z*l;V$NA6cm+}HGTdYXLT|7D;~#rZ5Xz};5II8QF^z8KP9nqGoFM=T<K>BBPGk%Ei9 z9vg@9t+6TtxFDks`hQJ5YZ(&_9EO2Q%B+hIYNq4Q_72n!b@Tr6B4sUp^sf2OS}K7+ zt<YT^R-6&c;H;0wHc*)oX<o+S!_}LRq;BC72|J^)<F9O7v^-u;oHXWt2!9ScXOE>$ z?ngykl|@6nec9o6{fP>bohr1HewkOdPX$8vyzz4S;U-|m&QJq3_!O{-KpYo`j$IJb zvW^5_swp`bl=Oasu($vrZf_`|Nu{uCQ3CKFcFBa;@SA@X;tHk9b5IdIL`~jH-}UEa z;@65?eC-egI(oK^1TWUK_{Etp->fV4(8WXD2)Y=$l<;!$FH~bTW;9nM$*r-497a4z z0LE=#6{Rw+o@%hz58|FB-({M#&ZizimGigPI{+$^O%K;E{e!octb`d~tl@KMV;8nS z`=<Hj-mTcw69Fy(Jth@f8&V~?vBzY~^hw9jBGw!T0@$#uoMT7XP`c7GR2~v6xR}`; z-3O_GaKh+_MTW9kgJp|Qk%Z_H%J>k}kNrV&Za=avUMu1Ep>2r{E-xmn3Ec7GlC(ld zDT13B`E!5I3IuI)wqXFbsF?MxeqOaCsZ-SrIS?@3aJ3u^&8;bYDwl!LSrhg+V(n|4 zigefhId^9f$3j4?zsq{iKk~{^o34RTv)m#l!hM)bjVFzN1c@xRe^m(*Y>pXGDjsI) z1d1Me+o8zn4;=$PheQ{(Z$LPr!Qd79)ByIOu3^d>qs?$w@QUm{+&!XzTc}CVmeCct z`tB&dPnYnN(;-yJhTe-F;2x&lz$;Yuu<gw|BI<6hOr@rc8Wrb2?`>h6T*?8jX&-gl zqv@cdmlMOTGGeGqWGP?Rs42I;pxM166hUFuBF(G9CRgR=%4{IE7+RcuoLX|J^}1nd zVQNVZ`+PC${wg6UbT~e|HXF2crdsoQCL)l4y?{j3G-J>1VorWL3IRaX4l10tDtS>Y zY^qa?E*UH}G2faHKd?2IHn7`&R-^M`xh8s><L?5`mIph0BuWQFSpTJoL7J>6`^NT* zWIFj>e^=1kh%WMnM41A?!k0<Z!F5DuVWT$?z_QkU>Mc$g(>0ALhC*ETei^u~Z(N8f zt4g4PGJdloYvx$ELUlx9FhvBlpMPV8y!-G_fAT@y8?E+<y&G8qoxC{K7p8IRe^nj; zicnr!X-F^9A7RXa^P7Eg#m8X?R?AiXbL>%@!UnpHsB-ruoqjY8zua;q!n-G4Sv4B8 zMzid1Ncm{XiGrz~j|O5(JoJ>gs^vb_4+ob9mDF^*_CrdRpA$3aUN~@UiTl)wU8r)2 ziqiuPquLNYJ9==}De{BwGl%T<Qq(aLnt^XkF&~}-h6titm@5HCV_KAI>D}@rk-&Vn zZxcnIwJe~zjvCR~vr&D*?2vh4LYm6}KA)E<$nKs1EOnj0DEr>T3h)#l{GY0KO9+e$ z{84TZ7jq)@>XJ5@l5MJyI$c8cI8TzeT->^>U`(Ka@#*`9L6!qGGK9|qzf0tPczjWk zBw_r0+AQY5LQoe42Hg2pa4Zo?gxL6OZcjX3!a}F8k`{bf#ol`KeEYsK+7pkSCZPD{ z{#p0sNrhQHzWZR{gIZx@Ceh{Lur4$JtQr`E#x5QgtVfxQBryTGzjc~iPV!#x_mWk$ zM-Y>vZ_PMl;Q~J?z#hcg=wq2tfHqV>0}C!?W8A%qSll%k-uovY*U>v2YSIo)3D#{g z+{Nc<d=wBxLxi5+3g@To?2)X|{IcV|wK8hP!;8XAtDc}%)YI0ru@<#|akk2j;gKM} zmauGVQ<z;v5pB${PmP%qvW*t>BfwrrdK&(2Smg^XMyp|~kU9#Cy;ReZF&oX5-n0{N zdL9}btu21}PolQB%bq{89K)I3Z~wddAOz8!W0KLmyFN|#GNcK>1}dd4M1*f%L_+K@ zl)O&_82L3Dk3xio7LRgZ{}N+194z@ZD%IPonHNj*DqNz}6H?4yLx=_AbD&;FAGDG{ zU|b>S(Rw~+BBqufjo&m~6cxj@ifTb~ia{m9GuLiFKTTmIF^-bSx-OpN2}K>;ciN1{ z7#BL9{l*Z*9J**zTFE0`*>t%Y5Ex%OE%79hd8xW7CwC+Z*8H<~Vn$QMZYo}t_hH8j z5TWYWAGl<WOT9Esz#|FsJPFWqdKQsY3<l{x0Ebo<GLcS*GA3fY;kt+dHyV4;q|LIW z`Ef^@xPl7z{%GNLKLj)BUnMyA0N_DKh_O^eyDP%B+`iN1T*?Ir(zcX*9OGW5(TiQ) zG0;~?Bm3=NIX0LHPZL-jr!kAvkzt$NCt&}#9Lec-GC1e}hUHd<$)ueXDtEe9uEzyD zItWZ@a?PQ+oakoF()3%CsdIEHR=1_>Gg<&_VIF6eh{LsHvU@&?Ql^e&ATJHwYqKWv zOG787@$L8x2Cd?0k?I30e^G(%)X5M*3!yza38OC|lMMzT0lLJne%JeFS$(UL)xSwN z^#xlnms)FMj_2zAhEqXZaU5)bXMu8}VO_)@m){M(UvHxwQO`cbpw)m6T4uVuf2m<U z_sgMaflx+{QG21R&R%YAc<Y&oBp%J?e?Bki^oE(%ehe~W85!|e;-EW93b9~KmUdBI z_)l!>)tZ4lw-BwuT;r<6TD^HPM7`VIwx-*VlKA;N@P7Es>NX<k{Cr#bSMDs@W5m`o z)HX8deJQI^aQ-cvekdUYaw6r}JCbZ8kvztAS(khKO{WvtxZ8-464ALa8{t$IPlS*d zI7Qz>CFDlr&H?X_P3?j_S$}@V=ww#wr};RWqtOUfh-Ghw_hVdJ#|pTbuAzO4jbo=@ zi&pP-CejB%K_^cZ!R(I9GIn(C?TCN##d@^S?nkBx($w7oZ?uU!^tSI;o|g3uM<VZO z(ISc~WnP0z2K}xhW#5iAPi{qyrcUgIy0NpTh@qNE>v-0g(;bK*lEz3Nv;jbB4^~*_ zo=v#;lv}Y_QZ@-`iG5phEz4>e8l6-HQ{}}-(EIY5kx~!x(th4WKMT7zg!Dc9<kZ!d z)4eGFttxnL=+%j!Er*!!65A`_j%{zgEq&hKe)4M%-!YOC8igX^S4st;A10;?5=nLN z#~MDIzkQ*qaXnM|4fOHvYC>?wgWe#s|K^_5DaUYD40jgCajLO(dV@uVuR&Be&u5)O zZASs@0T^m$*b<xQpmtMr5{cyeZ>?Z#G_I|iu8!{^c#gl?4JZq@!pqW=E1LM6$xtJf z;)GaeNbg^^nsHN(rQuoX?}j>z$$CC25k?j-%xyY*Pm~E_0W@QE)x=SuW~lW70*TFc zcw&zWVib6G;)$Nss=3`AZaRsnL~p69KasuBf+F&XVdEN4(JoBQ9($vyer4yOwb)tU z-1fn8BoVdJK0#QZTsQ>QF!vQ3qL6?dDXJLi!JkD;iB?9w-Kak=C?)r%E&LnR(W<fu zgwG}F0N)DmG(!8wo+~2J{UL#Q{tNe_b3{lF-jK$D_`13eELca|N9X<X7)L7`Nxq)8 z?BHFGQ`!Z~7@7Epq+8F^B*Hi+Xx^NWTi@jjjy@L#Pvc6CMrHOBj5s}}2gF*uE^P-C z^DD8vPN<bz2eYb?2U&)`<prHPR7AbiL8F4FoiNv%PoN5@R&vNvP1WgA%FW=!P#FiC z{VqSd*N-40rG;9uJVE7huD(TXiGS#8mXs7&gYV&mZo_BnpQ5J4Ps>1pI;4#k_Y(lO zMDF<rLLkK;puNbPloSa|#G?=y{8t?k@s?<!N+n68?3i#cvqCUME-b}1%MJYWk5O0} zM%n1)iY^I8gY3iv$XX3nQT2h+pVzj-irdO$g3r%!Gs1iy-D%)DsVz`z_};m%=NHJg zF0vhLO83p+ZON}9c2Kl8JG3Zoff|RJOfF}p%%emW(_u<h&^;(W{|xe`aBbC-f^E~# zP*p3kI#otY(Ub&Hqtj9A8lwF4%)FdKW;zQr0H9)^&_Pa7=xmU$iNyX&2e|vmZ9FcQ zTb2w$#P_~<(3|#-p%RDGiP{%7ozQax?$KdpF7y|L3huQ)T#@S3L4~_ScAQr9jew{4 zd>o5kf$A46LOXDIytN{>@D2Rld3T^?<#@z{MbdOsa%sDgIFUxid`=w{G!F)zR(yRT z_?=4%{9P;MFNQlu7252y5XOR52;~T8rQ_bm-=AJ5*m{vh{V(>%q-h>D9e8G$3e)Tu zC;qM-ZmhSjUDk|DreA*K@_Eykh_#nx52kLipY|Q_r?XkCT^bO@{St(|Fp+=>Ia=l( z0z?oZ3O5N7bp{{rMspITrMv)@*FiXFtUgUS`rXgS6=<J1FF-(O9rB&={OP&m-PpK6 z<9d@Ts6iM4gQ75K`YKfF0);RUG^h8#fI-)|G?o9Z(GEV!Xu4f&3nKL<YZtjr;ME7s z?X+9yeUZ>0_~JG&FY%&>#H%lRl<;;|rPb(7#mp=UhLOj`o(BPqiU}<{gPP)bkW;^P zNx@G`<T0NOID=bpv8bKWq8*)Si&&8Ws}o29`gv2p8VB5Qu{>G32V<pGEd#|OS~7lk z;-!0^?>Kn<T&{RF(io!CI0_*#4Bz(7aTFUE<RLy=x@9H!Y8_{Ad{@JV22b^kv_Yo9 zERUvSsjqAeB^fFR&_X9S_B5=%&Z%qI$w>f_RtL-6f?a}pzT}mT=>Wn0_cL+=z&dD> z)m4`{IcDFFfKxx(UEkemuRCrfV?QG&q-c<2z>kOioiC_Sd0s-jXmT`YBw>YJdHW3= zg1k(><Budfe=u|N4>m~qM+LiY>cS7@uj~bA6FN>)^~5e15JDFAPuB;mx7a^qrKAA} zM7sb{IO7w9K^T<U@i31*&Txj1<&d%7Otg((gW<~=TX#wAr#@Vn`4*!PhFAteV22Nw zT8L=SP>smh=ItBId{Ucn{Eo*1PGx2YY-YTqj}_ag<6a(gyycL}Omv8<n|o66u=k?L zGG{a!&VeX(wSs@{M?>(&LB%(9MB4HG+Mj#~0*nrqRZ-A!_u*5JSqGdGW-%G+=)}`T zNw=iTk%MkZ%gGl->@#Scrn8Dpa@nsx-7BtF1ua5PYCMw*zc&~WhR2?4`*HpfS#=Qm zMqTbu8cEx=x*V>OD(c0;*w$zH39rCXo|1_$y>Aym|D9Q7WKDu?*_!X`$HtXP1Yyua zOj{k-0Fo#X)x(Mi*yBADGo0Fug~Y?cWWm0O5IaO+!_TvQkt{sd*A6ZMwXyy(at+yM z6wwNFxITZgI?dSjxSIDiY<8o&wH3%4*apTGIu}17ql%?2<n7I9lvi<-AR%|lS-gaW zX=WXWoPw(8NG(k_vnI|(FrdLrNOb>oB0b074H=A9ub5tC5yv)wPfdNNajv|Z9w)sA zH<FzP=x3iZ>lpgi4kzAVY}HEyEMWct@ORH)$TV$#;?_!bWH_l%bf<5l$IRQvu#Z-k z$Ch*5X6gU1z8ffX>J#5yi$C_~Z_BzTy#&RBbdExsBr0`C7efaAZrBeiKu(USQ*;Wq z7l>BqUU&-6yeU7|35dE2LS(C3-6VaS88R6XmD@IpzZTIGeydgXVF)KiR0|f-bc>?= zx-;cWxWX+`E30F=O9{%#<2%8jzu@EfV8<**wwW@z<~`ycEm|VAxGwq1Q9hVFKZ#|6 z(}Iigk<X^%>ohne1Nx?yW0(c}UclAjxsp&<NIB4N>aZ6BgLbi10abGIEzXAe*t+PN zo5kZ-pRmlC#ZU)Or8#6dy-$tP2B?14<@UNq4bFH^Eno$h5pZ|pS)i<jC3qQ1S?VIg ze`U%cMA{4ztC%TUV$#ny*Sm6g0V5K0D0wo&;^2vQFnjNwP&Mjwd!6^M;BrnrXmd{9 zEX9uO+T79ld41k7g+Ywmn0ham!*KG}Gldp9f($^ZS7WMBq&(+fHtCDpPiHz}U<6Zm z_;cp^K4-mm2n|&^w=ZW#CHT0pw%q<&ch5<aW{lxz95U~udDmYayA9Urj9H(=ZI@0m zNev2o;JNpG@7Irph55v^#x#+9(?Et_oy_n;p{bj}3>*@ur;Uf?rp(9p%i^~=x>DoG z^ldh+>4+wDnbfC^AAImSF{+nHL8aqRsC|@6@wELN{J<KuJpPaG#f(o%Fuxp+D{4+4 zsW10R)W+3yH(-LA!{6y3Lj5r_>B(#c;V(zKju_rn4Pv&Au*8S5*+SH@&woJ!whv-+ zcvGax_<zlnF~pI4RQ#XhhT>UJ-pE<Tg?CLPjJZfh)y84QV~;Z`T-lSL!^Umb=!4pI z1?#q?--(n!N7XYDbc9xN2o?9K-a9C$AyL1j3lgG8A%y79m4Cr45qnQf#)7nOg`Ccv zG+hKO_iWTlQKxcuUoMYSY&|HbJycMmBKgbg<)j7+ime4=!tYG|G(9a6Xj-L6I6g;| z1c1MqAj&g(?G*PoU_`ta^U4O`X-eRACP9d%sUADfqq5Hw;Yd*TtN*=eg-Ot%;93LP zch|XJHYo^xpS@*toVu|QNOOu#wh^Y1zB4&pi8*>b2PMY{U$j)Y|9KJxESA*}0eW&K zd=8&-4@BE@t7|!ygL|LOrk4m&D2^qpdCb@t`|hZ%)5;C}l^hs!Czrj?t=3lVKJlbD zwON@ml<8*DF`NjwyGO1Aw7T?M0cJVpju3s1T^=+z1|m$A=l4;+`9BFn9j6w^PW~hG z&)W457x^tgg$UkU)Zo?)-(OS^EzI~wT@}$)7S8<J_8^F)_ZIzS2dFH^BS87YeCgB= z+p!SUj`M=Q?y<)vWEH_A0#lT=2_A?hMJ=eng-FZM?j&Qpi6(h39E{W`lBzr-|F}>| z!QwV~$7}LLWU@FX5#sPSdWSe9kJj2$MS#lf=E-=-^6;p*fO)l^OtO9WDQnOhEJLP& zk?^2KBNDCRD7&Zh*UY#;@S5N~QM2@)vk{5q2Nb}KzDOs1;-JrqSw2~C0gRwx;qN5K zgyfq0ahpS5GIB)Y!^!m}$^U>gu4cR{GoC!iFjj%qgGQ<Pvnz<{({6_^TQ<Hagj`Db z4`4cODvoVQef(hpK~Hl*Gg>mJI@JeJ`zlJK?ONF_K%0bWjZ)J^<&=X}pPYJqZ`x4E zWn3yw42RlU!M{+;-#d9;esCo>6zRi7^;Kqcl>g?-P(w<%w<3ZB9u#h_zf1IfF`2JW zep#<seam$D`!?;^QYHSa%n+P@_ky*B#p4ho*mu>_S#=J?SX_}{t($aZJ?6sIsG`MQ zP0;Sp4scdl1BwM9ALdI!#Nv<(bvhEHo036ms~g|jJtJ@Dt|iH?L!}hs7VUHGU^<)e zDN^reR%|KjTwKlKFO`30285vrZ|apGsWKvNaTC8E(;lzd2*hl7OqBM&6zN?XHMvk+ z*^?y<Ie%zE@||<HKX2{Ut$;Ssm0q8J`pr0j{hI)4WriFwgf-C_uj?%8l5vM8)6or6 z86>jZy%FQuy*QLK+4vxzd<tkoB)2l>s!lsAqDFS4Wnc@=d2vAnj~Rigsdb3u?HUcU z6gng*Xz$lZ9JzMilgT~v;lP_u!Sv>=-s=RgRK$y-EDBpLbci;_`DIdsCnvV&b8E5y z@h2bRzpdF*v(ud?g|V>&zevfhC`A2Ct7Qtyl+Zt(z9$bW!qLT-QzJ!3=YKT8KoQK1 z5xC&S%ka>|rSvZf?Cvi>u@5o9Cfw7w6aLB*;T#5EZC^uRsE<E9I*~TGuB@TNZ9Y2d z<SS;mwW4k1*ycr6LSy$+hF4@ChTaZ*P0!&%F=+cI0QF$<1X9qEN4*IXsNtGBkY7FS zE*%x@*jBf*a^=UarMNJ{6-?*r9XI`Nb&+f(1mBFI_h-(J?b~nBU^|aDD$J+a1SgtB zJZ-2G;K!h!%}vV`Se=gOJi@)@7#)8)jG)pfB(JGL44_>kph^QyG*|#2GM|77isH#j z@DCccn!dUNB<y7fpL?~0{mQk!DR!=Wul*yBVXJ8v<9PR#S{pOS=$bW9UetkP1G-3O zT|qo0mt>G+(rqZnJ~DC$KEMD<Y4ldz0&kH|))`|7<12q#g|>N|s%$SWj*gi0rdvNn zC?=JN54!!U%`8}a{mHe4%Kpmy_uT)yIq-05Hc;_?TJRL2=Upw5H#+lOJM1S$OG?y5 ziX49~rf096f2M)W*npwp?ePBFYW(Bbh|$-x4+{KKdp*mQir6ZZs@A?2J=WP7C^IKp zo<QH_`Nw$$cf5OmN2<3?9;su0OcN3FUN}O5u00J$gfJKTYiO?tOF9lLF(V4~P!QfJ z(U{Mzc!Et-_I@FjiWavBf!9&N)XvD3-^tp>jvjB9)1lF`WtU~dFn;%8SbtmE`!AHu zwp0&DVRUqcq*!;O8`#%5K}X7{uQkTba+}nlk%`~PMYwj@eCVcu<Seqrkwy|PVI&KY zD_KEmSdKd_qv(`--F{PUHOa=&4YLd0EjH%_0@=LnaiCmYjw2Zjdg8>ERqwRc2%;*) zp|VnK3kZj$E0<b_4g+_L`#0Hd$_1D)1*Pm(ZwXRV*sjnZjyqLQZ_JD+P8r=@W@Z7x zzo>XYv*(|K`Vry3d??aIGKpm4aW_erWft$F0>Yf<A`<={^eZ_AWb^k}xisrhn=al{ zb-<^w@4NGHm4jMX*KK4gh2y$Qq?8h#RI9&lwT4^~C7-B&<~O;;-q2hcbqk^rWC${c z5)R*oNPS&hIiC$CL-Hyr%#v~*I8-d0ngP$vvxv9+h#WaKJp7oEH#C=tb&iOs157fg z!OJR1#y-~d2?U$E|B9klCp|tV(5iH_a{MBGsghj+XN*eIprEJEV(Va8k|eBh6Zn&4 zgo0`@sc1dAQIQNm<xX~kH!A@h?<Wu46T2%bAFlwM<{-+VClMzn+?08<&xe*WQ0ez) z=4;yGRBj$X2nays&6Y|B6vK1c=DduuCN~^nD&4HNG_vupl%KFDnPlYp4|UpIzG~iM z2|I{%(-^0}`R_(brg_KW5?qaCSE*e<G4RWJ(7sEYG8#KGAm9P?>Lh?GrAhJ&(r2mp zBsqWIc^pCL&6$fJ%NK&Dob$EKN*WlImz>?}_H?%)S5010W}h~V9I{l({}X7&wVoH7 z%#ls8A&$p=;H*%EB>(zl!b`Gb;Z4k&(ew6(pI5G0@lb6Z4s)fG(08-W*>^tCoph9t z+ro%i>aWsIq^OKN@j*~MC@hu2@FDbgoudOqI<VJ&A`QyzY=1lXIW0BCN{FvQO1Xce zu69AQvnh;^aRM&_ITH!&jN+y~5^@`;<GUA#WL<J|6F9}77D)I{kZqt&XpK&Fcs_BO zP#vLw>h^^7(9`tG`f9SonCyvG(4!3OIq!S8mBeh|t^OAy?E*=Q4!qRo8{v4a&bEU8 zrzB$T&uq}Cfa9LpuQPjKr<2th)A$N^8zf9vMTg2NPR)B0wjlfITCM7iLJ?xf8wh-$ zyRbNH;czeEREcvvqXo3ex|{o+{>;!0zt>A+Lo;m2<l=(0Y1{dkES#I}=-vr0S4*RZ zrJ{L@_DsBd%-OKS`$KT;Z>e;fZerU%D0i=@3ViZP7tE59&1&wtEbkkuG>hP+U~~hZ zQv8-yXbVM(`U?p^ckid`n0)EoQXHNMq`!@7kH9U-*+L!4n;E#oYXJq)5+Bm!LsI45 zi}*&!esm{hQMrCM4U%=bvDbE-J9$}N4%#MBT==%6yEm>M#U++GN#7g`rGn?PcNOv9 zcm;d-RKvzd<*+1d;UN8HE0_!4Q0<E~|7q&;OHdNX^2bZDFlc&-t+M$zzkr0E#e5j> zS8QTdrA+z0s@8p=o;~uqhUZ|9wyP`P?GQRvOtceh8_fH?RALOM;<#Y6{j~nX9xjD% zVV!YD2O6=$#_4(;B?O)JIyj@*ML{?M{1iVIi&n*>LyCeL0TnQ`wb)N5bvvKrzcnPC zUx7cN4_RYhh_G&5&;`4D`!|P2Gd-^(*u__(ahTKvjQrSTLZJ&Fa`u&l#VE<i9M1wu zxB#Tkp>>@x(za;c{JVeYdC%86t^%7r7Ozjwws&#$ou-1TX=_-&n9y2|ZQ16Sf&b~X zCWa%p8YD@AbMm_KwbKmEW5d?<3$W}>GueQ~<g>Nif6zl8%8T+zlObAJ$STunaWUgR z*&J>Qv-W}ZTulb(iCiyN*WB1xB^68(9#?uN`g6sWu!Uu^1C-_8UBb~6=w3{Fnc@0$ zIgutLfk9Gq=q{n8QE;)3i)-bLm3*xos&#r1ls>2G67Wb(7MXYo*&9Ovp&z5a{Nvm- zlCpa*^$1_%E7&?}ialgAYek+kz?!R|sFmrFo&bYK@>uO+u!lV>Vh<-~KD4y9@MKv1 zUcg0JN*6(phdP*oz7vB_Vl|DX-o!$wG9zerLF*B8M;tTN5QI;x^i$OLjb-Sk>zbj2 z{X3$@c=V$|EjugVw@**VTZ3!Q*(fRTL%jR=)1_sg`zcZKIkCa-Rok&As`r)V-Z#~I z^*5F72I4lz1Li;xo-eBIcu)nN)vUN;nxO2(E$3>ECO_-oOBAKWwIfp4h|W^<N+Stn z`&Tyth2QmT#`|mcTLboqnVANR<mvf@U-lGWnBF-<*a%B9Py+80`h~h?9%uq?JgqOW zTRY}YaQ<tEALG76IF`z&uh+ZpXGQa;YsG{O-u_6IURZ151h?3{0Zn)S$ihYmF3}S+ z(*R;BrzM5^kiY|kzKfIGC6XYEph#RFgN_xfnsWXcrap9iQkHck?)7VFEx~M#H36Hs zw{8+NL^a5@08mW7e`{z90H;dyR7K0xyXT)Xi!|&qq~|{OUm(9es{|EHNQ~TfL*O0f zWoc(d2tGOF1EjUZ-Z$!)z>$6LvS|~tzM{FnBy7TjL0AAp^7#A2liW+^x708$H!dK4 z!9PK0Qe~VLpz&-}q=`tNQh$dS_xUc<>{<GPlaTBz3pS)267b|aNw!|xf)KZK)yjI& z(>gH%+q$83yYWi5aIkB9DtFA8L`g99_aMUrLj(cxB?I(nLuG$Hz7;Bd&ys2kUO)Fe z*It4RxQGvo@ejBJ4%@|uK$m<|X#aFd+3QtU4)xUrd_Vl{p`YS8OsKE(indu5m2=@| zM67*+3#tlGI2cfXOdI;;%j&|4n2iF!MON59l?oB_A_g^<vq_jU>1TXFTihl3Z#73) zjH2Fp1^Sb$ca@hmCh+gBGz99CmQa2|`>0~;OmV*ou%MrW72Hck>DejSQ9_&xp}Mw9 z*WjvP)ekn$>x)rJ>Y7>tAhO5yP9hRRpfW65twpn?bYqu>9sb7f2QlmwvsY*ai8DCi zK4vRfEN}<=2Ug!dkD}=)KW(6el!ysL*4V!h0cjGli}NxVh6Zb!Ec_cGSy;RgTxNp2 zw!p6Io!si*p`Izz&mR>ZR=3^Ul7Hz{kv0w2eSAe`iFHN?0VJ;s(Q{M@57_nLOU<Ui z{uQ8Kc^KD6t*xbCJPTr@AS!35PH_^RM@NZ-Tp)pd6<MUBIG&kwrWm===@<6y$};8- zT4jWLg?X6~gJw;Ll0kEGQ<y@I_kW<{tR?ZRFmJtaI(#JAk1%TFSyjiI6tNK$)@-7R z%^JX*>b35jlo~rS@QES<>)!-D%@YI<#*Efr`H*Ol{jKDR$(TZr1c8itg<l(BAYkzb zWnwqXBMM}-wb60vGV5Y%BsS(#*MC)St`#Q{j}n_!#!16-VW~&{F3Gd3G5q?kz$tk~ zfq7mewB)T3?_49;Mw?1L7LT8Cig9*#cV<;=Hf<0_E`7n+mIBQQ?|QoPy4d%54CFm9 z3&axHdsAnNH7s(ZPIq<?Y|0|lBy=G%2V1VJu)v;Gr|&ks@Gy+OoqVrs9L?i(g@Q?7 zOH>#OYPJ@&Fa88_pU)qXcmR@uyzfh^{ta>|`BQ#b5(i!1*DYswnrRC)_&$&;mxlxT z{#Vd+A1}xKJVO*5tpUMnCF!cDZmFOze|Mj1W_f93jEsO_BJ5@Zrlh6yIxKlH6R^5= z_)4ncwLi*}W4+epA;MK&B3s_pX4M~R_j*A(E}J6ng1ph64btBrKHt_49r_T=6Ps9< zWn!ITU54j3_a4))<VqCJ{%AMZ*e$2Vyk;(6>z`cwsho+11a0bbZOC)sxitxCA1rd- zwj|Y`cF~dD9PaC*S~zv@VjAL<4SD06*_L=Y!8bRfWnD`%XnfaW86l@^WsHP6pV&=( z&>@nJlYQdDi9wnS<;*YuDSS9#4(I^Kjf~VvXW2Tf$+4~LSJ$niZyOJrO+$v`Tvrg_ zPSy<F@8@+CL|lx)-(?R~l1DOWCGL+N&*ZfzZyr_t8)b=e$@~l@&TqJpHUga%5r)-j z{*H>URE#*j`HaY6H)B1gCrZ=~sS{%CDpdEc$2vFWW3_`k;=YFqHhbi|8MOUCSl?~v zRw5W?s`l0XHuix&YQPuD!gDl1a2U^*i}MmR4R-JAB&Q}?$5`ROs8^lef4wWmT6L}+ zO0>tQtp!P}_7d2w69VAd@{4tK;<tSTML18$9U)2|OfRCh7q1wvZ4g(dZlzHc6Pg?G z5c5SOU~q7{tJn`ECaK}kdOK<|vZ{+CB&ZJ5hN?t27jLL*xjhw*WLh|vIQJ7dsK>gM z$*F*r<+73G^+C$~UHkwL2;lj_n=*-h#zaY_NAuz8pqd^oLf_*`%5NiD<bPMvc)=&O zE+5<Xly=$HCBJ1Y;3A}v8u`a*v<O5bU`Taggoe*J)G0(>F)H<sBp8upSXK8Gsc)ED zf|+{X$ZM6{^T?o46m21``qmm5O+uP{LN-h-TeD)ct2$Nx6SHk*Xhq^dV)Yrtwr0V< z@k?L@+eUkOx~o!!f({Flq~OeA=2x!f>jWd+?xYh#eJdfC>i)P|l?%I4oiIrd@gk2t z*@QjcLWrVAGYVCrMSrNh%%yT|oH>q|r3vGUD@oe$`x4b$b1-HgcA5~0^lbfJqg@x- zM)xB##11GqU@iNX7zt@9<~|7s@2tYnTB!jbAfuspdil|YbUOb`3MIcPbJgeFWaD<M z>hAh0^Hz5?B^|cNXir?C+KUi28)sz2?W#%9!r)zH?-0;$UAQAzduHH<(+IXcri%IQ zU*&*ysunn#-sTofW4W<!&Wg1=8%Ijx@b@rv&^DsTUFFIbjd8HgboC2{vbIT?e%HFO z2wBt7WmJS<VvrE?vKqk|<1As$x@sr<$LYK@fo~GiQ#zIiKJmT!gClSt%p-=7@YTal z+ux7`oLYpIjMTl}FqB&V)sRB|FwvWbt^9JbELMaO^^~<g{x?Y-fJhwHmDK|zunxAv z+)9MH-~?6Nbnnq9>hytQYgT^q+(0@%xH^^CIHcuSj8K&UT`6snRVc}DmUzj$KhFU$ zF_*+D9H-~1?>W}1$R0_Mov4&d#A)E5%fVf~{+GkMT`&K3%p_SVt_>VHd!o7k-M6s= z^E(QF1`f2O?Opmkd1CfX1jS0fB$fPeo6rCR7Yfm!aYM*o5s~gWmF5{v+zmb7|EF8S zu{Kw~(kTbo#UGS-&zqG8f3u@sgz$8kaP2WJpSn0Q;n;|FsBS<!Njz;x0FAOTBH>e# z8?(7@fx*8$lYEkt5MkLVL?0=19xh5~>fXbYqHtOU*N4H8Mnh~#v=lvRNc~4{btnwT z_+2f~FqSyt-pQjjPG&O+*fzlz$b@}$2!jg4J<5B|_9tXyRUH|O>i?@4HBTOWnSGum zmZqD=8IGnKiL1khQ~nVEy%g;)**88p-lP7meX}w2?{2p5(;mo5MxByE)vpfDy-*Lv z+F#W!$XA6xqne;nZ>J!+z#O8Q5^LbF&1bbDTfUB<-#VUiw#R}Ro==#bAbSjRnSf3; z=jw({CQWRzP^!+0OqiC+xdWU_y!K#HhOSd^n0!;`QekSg-eE|#oSqKnFZxoGY~nho zRKcH!Jou3dR{6->^qm363P&A#dQ9zXRh!XF?P=}!QU;m(RMG?s^$X=hLpSAQV{*Kp zBrA)h{LaCfiq#+38(@8x7z1$dP)G~ipJDJN#!3iA&2%&Wh0VFj%YMxL2%~TBV>YJk zKO^0gGM)j~+*x|izx_~dl4$6C<`&v?u;KmX#4d;u&EEa+8fl~j)wr83l*58BUQ3?c zWALK^?^~aS*(XWzG%srz{QjE-XPb?%upd4AX?-W_uy*8Oo>q+N$m31GqN8?se`rve zqRF0J+|baA9V~Huct-}tOdc8Rq_8~ShxeAKQ=OTP1qm;M(!zt$dtuSS3wkYoW00`* zyQ9>L*Tv*8EF4|m%b@Rj=*qyAfrVO1*`E@Y$CmU2Lly$u8g~r)03~B*!x5P)t6-!3 zji*K15@TyI(wzDkp_ySm_9eJw_-ZB?+bQZ~+GTk>9V7^BR%8Ba44hy7m||N%kmhlB zCuJxedSvicx@lZ--i+JJ{8$rzHF@?hZSzv@Wsz6EYNS-w*nUKBB$5T^gTn9D&l?<y zhl{ncK1foq={+<4X<ee8omeNp5gPQ#VF;?Cjc^9gOEgdfE+oJa1(W?QOR|~YJDSS* zOdph|mkZ<jX8t>vpmG;%+z%>VAc>0cPX{j@<_#?8lh5~K?pz)bDNQT-Q_LN(8%j<d zkxi0TwpikDGfNDd#@v>J+dqi$1k&>|XhvhU<+v+}O6AsvD)aq?uJ?Joz*T^>ldb}@ zOX(Ir`j#8!o>M17=KjFRz<Xp;EKOf`lli69(2^Lm<!NnG23-2;S*`nM-(_a#YJI1| zr(YUc$SHtCk226o*F1}-ygpj>L7=lH)m-z(W@)H0asTdphab)fpV8ot>?ZRRk(IrW zg};HD{!eP~8qH%BTOdi2$yIG*$J?f~QGM6aN8CH?UjQTo1dO2boi4l9jUM!7kxjXY zlW&K<K~Iw*PbZU61v)<gGr2Wsl-Ptrdrws<<JI@+i)_(e*G7SaD>Kbr*?EO%7h;Tt zYbX;$-dbf}vr+4};H6|5WmCTmB~kRcYC17K@=TUKuRiSOwWA1Cww*iE_US;gC<0X! zIXtKY<V!ZvmkiRb$+_Jy_pkv`gN<vunpN&^p6!BLq&vw*RwSia8(!{K8~PX6i$k9- zQ=}M0TK-x8n27|V)AFxLQaXWc*;3q8CLlhR|8H9Eyyhic<<S}0-^H_`r;jQHno*$_ z;^^Nr7$P?rMX_GnQ?v#G({e+1lPW*%R$k)u8Ec0Xw0D|i85X{J{0#rrf@)cXkcmcH zL8AzbMQzF8fm3qeix30lD1^}f6(zM>AmlECz<XfPPI-%2GKgE)8uTYMA)Oo7ghBGA z$xt7xiLxlJZ-pgR6un%QF7__&e-xG#2gxeZ+_s6Sm%6RGl<}ZxP<*}JwFYXa&z=je zmrWhZ&XX`<l~+D_kz5(XO<>&D=XZiHH_j=1|FK5;J3}GG4iOP5%ir_t2Rk|jDdKP? z27|Nzp&37&JIJzQl7o1|NqT-aUPpX*3x9DZp~C89HDxNt|3wxRxI5qc)hqI$oLQJU z@L0{Fo$k4`(8;eXQ~Wrw)mVhRSmC;@=5%rSdnIC4j9%|=Q~^5wfDy&;Lbgq`Jc0Sh zV@ZQGUmE9z(hv(<MTWYK84nP{LRS4EN2F8^I=+d(x)*g<?!R#vJ7xrM41b3!;0{?f z%j9|~xXi~kKh#E%0@#Q4wrbTMJL=qUL_G99h);E-$#slhO6hJN33D~EZkw<;YV9g~ z6!ZhavT>Un$1%n<m`w^ydZ*(QqZT#rRMqC@beNjJ^o+F}=^27@^-Xn@9b0qXho?Pa zj5!mOgS~IzLn)!l!&`$?3}z(?*NeeB1j(!qy&7JW_d6(L$gMkcf|%^bZc8I%BzZ9G zmqhDdL%$LOj5$At@qj}zpjf17%?9w!S1%RWl0jlS_)A|@XA_53AD(|^_RPEDqe5Jy zX9u`R&**rNy4xal<omUCF4nTltC;iL=bvMD3{pR4c7~vv^tWA_RVP~^qs{K-Uwxa+ zy(WgGCRoZAEm*w(DK<&d13V{#smOQbOZ83}_-gzaI14-cz=#g}@^tHuEpsxG8)Ps` zae6$v9GtB2t8fbld16Wt&DqkulRk*w3a3lgAh!h8YXz0IVWI)(OQCthw8oX3z0le3 zxnz=)oMB%YK3*(}Gd2I|It{-h6*N5t1aMJhXZO)AgC^o-GCC3H3{sZ4trjkhSI3pY z{VZ3Ent1Cd4qS7LQh4q+cH#Y^k73cC>0f&5SXZynV5ruQ%Boui>R${`*H8^^NcmP? ztIyVGyhL2Jja7ToU}(<Oy1607=bdL@YADuY`S%OQ*T`a_Cr8NbLMCi!oJ<M?{f53U zRZFM4Ivoy-P?kV^YvSIraXk6Wdzt1gg3j9{At90FG^70+I}$8yd;LF`rycvgAmvju zHON={9Gfmxu;RVnOCgs0sgoRLD#!e!u>u`;*kuRiDbds&(ZA8N<MPg$`5b?7%C=Tz zKn#1JowaU#k{EVkMP(whCZIXffGeDyn8{ytv-oitmB?B>fE|CG@>k}#pn_FiSJNiC z&}x|J0sBRG>azN1_#@?bl|L)s$t}reBFV<)%%+u@`SO?Ha<kWnfItRSz#07OgD5Wq zkHIB)Ku{fp?B2;QL(9LGoZ%8eq@3VX-2L7i!~sZOi6ET>ONjctKU*0|W2f3R+AZvy z2dm4@ZJubeoVad6YUGij{I@~Fo9eTN2xWZZ(XUQme_oqWCx>T5Qc2ECDfa1xGfb_h zDKftHlwUv7t!HCz@#@O>UW?(`HCR!P#XM`#7Xl*|n)GO0wz9Zc8<wOLN+VIH795{! zR`F(9ODv9DUkk|K=Li=@#he@p7iLNfU1^4kjupSQx{4w+Idci7&+!@+<uWI^4m*~O zH^N@{a)J3ixu2W44FKpc;Uq3uRw5>$Qk-Y=%x(gyV<N}%wop~mhIX;w@o*wB{vIFv zgpCtS{k5zvqnT9QK49l#um7zej)AW_j61KP`4L|>VQI(BB&!?jYfgJ)5J4(vTckEr zc(RXo6;cc*xD!;I8`Kp`{M~)fb$~yjhOCoib)g#jSn{(o4b-aneSnV_!Q{lfWo7!Z zVMB^EXS9%o@MeqG_hB2;)YBgjAJ>lM2eQxf`-X=F42p;K<nFg%-%6d!(CZ6l0WBHJ zPa%guSh2H@3OIt@BYxQ(1%#&;OCo7FMeLo-V&%t8ed$VVZxYd24j)oH|9D|U7LeY6 zmJiWqiJ=TU`i~pS`Pun4F56p=l9vX^5G>_z-x-sBDPf~Vkf4dka!4gf!Ty6^Ut5}+ z9NXGM$eA@x{0U~sIO`o_CoJ1*QyWLYv4uwYsqW=jkrGoK^#m*?0we5pB$1znUaPvM z|MpI=DyJPx#XC^IZP9vbgv4oLBxnki2C$B8Er1>OM{Mj&simMUGkRRnft1#><w|L) z`3AC#t*2v2D$xDH(x#OoG}54d6Muql{hf-%O}$O~%b>wPqpd4{_NobkOYqbxAdwiS z)a(x%pe=-dms`F}ayXGRtUNnYt%A7=i}G|d$g;s>D5P2Kf2LCx|8^+VT_%yA4#y-} zzn}MWWXr&AVS`+rX?|`W#m<|~EN`+ew0LRCVGHdO;ut?X(tk4K7wSadagS>hmZ&Dm zZpA8o3=i;!q&idqW23S~BEkT%GNPrHa?a6Z6CDydzqW!Q4P<R_IOVq0^61rod>CK( zV9XWpxElZN;Z;8|-I#GCJ`bgzn1Hkn{<1&c%07@4xBZsy6n-u^;CX(CaM%QsJqcUJ zxY~hs3|xeA6>9R)tokoexg5Qc6$r$~9K-yrBq52B&gQ=&GhaOMre{W4HA7?5G2z^h zi`>t=p+_J3fDj!WP)nMH?WP+Ybv)r}AW|g3GKb|7rx1Ib+IU^qz{;@5&06dt=9H=Y zxMUziVF3nls9%e3J^SEl^Q=jcEoVvrAr$DMzNnB34!-OWb5A<ff{Yx&)<khQ$itP% z)Dlm9!QV`U6wH)*$Nec_m{{yt2^q2%06^x_ty=lyntW08bKCTScC$J^5g}FiN`q^W zwdqZ$*e|&d;nulSXH9|lAtZ)IG=zyem`lX6Nb;%Ms9!-}rbsR?=M{d+aa+Ab5rY<% z;VV`Fc)IR2Dsj1fJ3XdpoBTw!wt(SC?XJTJoE7j`Q^MI<+)!H`qx{0p=ElOT11$-( z8Z=%=u8`c0<}lTIZL#@30hc0Z-4KO*NUx2w%xH;I!6*WN;3Ye(#S(;gB=-6$d<%(_ z25I!FIfB?XbLmrk-UT3+`rE*o?(CSlyt*UYZ64G>b*o-*&C0gc@Nkt=x$Ft~?xXE+ zoaW!04hg-L4k1T2Il&C14$#O{Jl>31+{!b^M?x_gWAn%XkrN~b31L(CJ>(VgG4k(v z(FRbqK80NK;!|6t!+VKFIn|VLBPo|Rh-K)8rP30_(8-M$$OssgG&Z6FDP`yVSa{e( zn-Kz+LP3S`-{UlqZKnwekw<}i^ds+k&pY4owzoXv?a_bm(uH&P9X_ys=jvj2e6&{1 zq#QdW$Sd$l+~2u#=jJKz6^{oh*KY=y^iz+wHY<|1?axY5f16Ju2>bQfd)Q-tTeqTt z`8(q&Zq;=TW-HQv;KocB4y1K1tM1|K9?N0O%<M4mJHI`d;JQu)g&br<kOOKs89}+5 z$sOyI%8A89F2W^q6HJG_TFA02jdLv;o66?%Fx}fz&bryWjv_tiX>>ChrrgYrLO%cD z1N#klFae84V)}EX)#%_+3aS=e0BQ=Lamw|mEhut>{SH+rVHxU)dd1S;^)A4$y~AdR zT)X*sO_T9TC?Vyu&jN<rc)4{Tfg<R5Bus85Bz^uVc;*?7(|~a4rEKI~gnG~W`2qS4 z{0lNq_L9@&QqSW(gcEyrQ;G%<O4p|W)?z>gd%E1)cJ2m^bllrE?haf1uI*o17-<b! z{SFHu2id5G-#($+RATm)r2hbCs(?lY(posld4R;W9jodzbo_+V`M{GOxNmPP<i?Ql zgvKal%Rqr~+ymDBxvzh)o?km0i5CUqEI~Q{=Ek1$yLz=!Oz7_i`{sKZhf|fO7E`IL zpx3Tl!&lbcx^wq;UtBM^;f#yilDeqWkurw6o-FClKXKw(m>29slhO#&wMVP-Gf!N3 zu(neAR#<=S4;otbh1rcK=1U3Vt#5sss$%_f&sP2{{wR5Yd}Gg?7()U3K#IUvULXRb z6xWHt2{En{O$%B85CA+MRBQB`g{Yv`R)x)yS_(pLs<qr8n_v3k^F8PJXP?@9=<?eB zT|1W-_jsDiHbr_g=|~^E@$Hp0Gkv^tmoTx-`jx_CI@RaCFQd1N-mOP~4Uih(t1rF% z7Y++>{8ewShC(4dGL?evA~kg`o};>!xOxszh!|hHYN9lD>{OIqkA<TgG5qGWsU5Xq zJX4CrEE$h+jYi9cHWm|+7^`O^t!TuEg;{Odax&G`Y`QtGD)LUXGfu3w3gQ)@e$hXt zZl`c$odb-HS$Z-QJ9j?HMWhvv72-z2Tsk{jFO8<`N+Kd+tBy3ITD-iNFXX1;;oPox zEL9N9i0+I0!p*;&IN4gxnv-jtJfY--XHo6)Z0ZHFpPVJ<duO7c!F`}>-DxC@t_Sfu zq7%w>dX;Ee5al-fYCI`S>NAk4?AW_!Wod3^YNFMwS5^P~NQf-Jf)(`758&&w%@99A zR>L^(HwT(3@H3kNe9tD3pxl>}%1<3&2ekXHv0ZClo|)G{mfb>l&#sAmAz`K8daVP3 zO^hB}jT*6-x$os&U1ry#DV&1u-dU`565;H78un{{u-uwUMNB(NM|O{QW16<KbKky$ z^G1~NSTdil6eD&fGt)q`oYqGtSHrP5*NnXtk+VZZxYmhg=c^oA$-k=9N=svhcDU8C zg}oI*C|P^`1Nc17dT?Fy;QG;?Er8-4V6+I}fi(%>NvZarlQNS2gG__MkJ}#5mmW+( zzf@#n`-EQM_Avl<b})l^W{Tf#`M#qoOOxZVh(d-KK2%TMl8lmDiDKV47)ZEaul`I@ zeb^FkuG{hO*~K6m;d2e1*AnAn#u$V?`9#!iH!ZlR#F!rm)mw~3reww3bt-C)S5Y9s z84&MJ=PSA8e9{8M=RWp%+Y8wg$428ZoV7KvFmH~Xh|4#9O$b8_HmSv#VE6<Wt@UF1 z#Hv}*XABGh0=x;tcf`W^Q+p3;1r6|158eEAikb{3PaNshV+8tjI4V=2sw5|Xl3D~2 zaGmrU;cpT^0(c`x-^K87cdk-OCCW-0#Q#AxHXr-D7S_Nw{k^Jb$gAGYyKnQP_v7Rh z^6z`mRj|Z^AdTw<Le1fx_G3ha@eLJR)-|cC_R=OnbCa0h{oWutA*hoLVp)Enjo+2P zkJkHJPW)oH@F5Zk4L7|1UC$U9o-r~!qodBal>$k;{jE>l@SLejXHFe#bw(-+%X6Ku z3K}*V)u2V-U79-%o4yC?Sq=HEmA0%hMWz#U6opj2y_$7f7eYGg8+bumj;*jF{cbGO zvQuTXj~#ZcW%jlUt!+IZ9~Pkf>(5L~@5{gRWjwZH`7=+{iu04B-})0=;=mAN$Rh4^ zy(O0t^|9%MQ;cwPY}lqy{La{<h~lZ8Yn9lJl4m^Hk;yU6pqL85I}3YaF$z4?Y1}{T z52^hhlX3tM`pa*zlexz(oqtr55`Z#oV1K1b`CP-%@qv#4HR3BViA?WzFcfA9cGS}l zI&k8B`*uDxp01CUf8sNl5}{sY_kX9m;9rqRvX7jC(VpRep(F628L&c>aU!^CaKMDY zHi)JTGkdx|e5>-xrXS9_^O>VdcRdFgoFWK_-Rle;RSR{;86*J-m*4nA#E-)TE0JhA zY8$tm;qL2H2s;nt_|f&XojYba?N*~!E+*Z0R1;(pCi^<Bxs{5VA2{^egGyE5Zr5^k zC%I)H4kc@SP=+FtzFfuo9-gI4+k55E8H_x2Hk}MVICA)jhw{RzzN6Neuso-FpyF;C zomSa4?L-_x?Pe+Eq%<89rHpIeS7gc1S}R-Ec2LxoFC2<HSHIkquy=7+#LlMHLkt!R z#{=zlWtNxiWc}>1s>4Ss**(Xm#{OF>#}f~QAgnbAcvXAz@D5*=&h<L!C<2)W%5W>= zOURZkqZj+-vz@82cB^Z1k@VXS3%RXEZy3vWoUj~bVYbXP#q3^Y3JjIG8_frqJBXB~ z#-}>#isZSbhvYPiKeAJXMwccksZx@TrRi0Q@VuY0TlGuw44Gdj73Rk%=elk?rOJHv zXZ5)Q8no)gnOq{5kL;81@v!y(hn-VUfd7e?@O`90(!GQZNMIcX+8!Srug_8+mV-Q~ zBBdY@A;VM#5C4Kfo->AQX%cts5(Y;@gu|_|rXdAo;svC5M%F17Q`wxPHL3{DO*)(- zJw^Sxtv|;Kz7;n}lz2HS6K;JZ<atyFju7GvN9U;Wocf`wEvXDNYzqFp-^&aQxxk9l z%`!#gCM);%&cSo==lCH02uYCEXN|yp`y2ovuE($bn}~AI{6{-M0*4li5Vt-41ILp2 z0{>{3GZZ|eA--EC@JEWtTAUq`6sZ#uRM5AI2mu&_KZpO~eQTcdWzwsp|9$GO`nu0n zK)C&Qlky!L?+e4!uRW;t0XsI4cOzP(2mqAol<5Lg=<sP~;WO!}h_P!KfC6J2HA*!O z2zZW#2|x+{7T@ksR#`nM7yY&YgV+-?^uHWPE)$O#l9Zv${oD2L_Vyhb+p;uxHO=iS zE7HBc8bYc^!#XTAc2A{DrsL;q8tI;}U@cR&^|-DXG{%d~kwhUR2#~+V1bm$rb&HTp zFNxqksS_Xqhk@uCi9m$jS#vC4N3lLpqk5@1R?sj}F4U(@s1+(59SK1sSi=AD|3Q=u zGyf=2HZXAx-{E1XMMitAt$=tx4z8{}f`{*nhQmK@G#uwtID8~@uW&d)NWmBIo%rJ< zPMTzQAHX3BK;UWgxhGJT+YUyQ3N(E@KScrH(evj&*J^D6`IZjwK2zO!46Y<9iAr31 zgi$67lmqN}^^PO>jm1JT71u5S#5vdL^YXUOgKmv~h2Kv05b@cWFigF1^KU=3t6u&? zcb!1O1Y7y<(%BonFl-Wy3^dQz-Z2j!fM@*jC_DA+sMiMCe&e>?)M=+nk(^|};I-d` z+k5x<&&MHzK8Qa@Qlvz*SBsf2#i>!4=?s!fw)CJ^F8SFa;>9a)+JF8gKF)bq=BO0- z9bn+iS8l$CGNkYq++2Ei|EW*^@~_~}0)oa~JByM7Py;!Bz0hC#3HbE=o4*3YkBEJq zz5{=ZB*_eUh#22@aJHQ@fll=?xm8#>d{8<MHlEz}5OjY`2^p1(m<xiCL)dq_9>~R3 z!&Wr`{=?o=7v53GIwODbEsuUMX>fP_Bz$^zPgtuf$0lQC0V}P}p*`tzJDmxMn?GBj zp8{lzXKbOjP`J7+LIQ3%=HRzx_N>mW?cDvTA3b-hI^|ilFFp9?FF$x%LVfzpBQH(r z`Kzr=$1ctuT02=c;zDO2(W0DkgMKqNsTLEr9_j0C{Je*q-5!lC3Yb!X-0fsD*m~W; z=B)6upaIaf2Gt9E+VZfOOu94p8;(R8iC?N!9&%jDt@a3H;mL&sqmyP#hNAb)u9|Vi zj8xIq8C(}S%ViBrnnras#`0)vHE{S4#h<yl!J~ew$3Z+57*KpxP&C-hpZwadrG%1m zgwR9y&7?_Al9xS9Up%$kN=Fr@J$%hohX$1tw{<EC7+3apgI0qyz`$-$GyDeAgZflo zWhXm>_;blWml_+SncSJgmghA;gTJq{&~N>M#w4{+V`EI8QCydXG&Af77cAk1KWNeS zzK=4&oph*8g&uXHh~5#drxH}dHuCi4WEWAt`TIA2pP4n}T1fXB?t^gu(@(`X*A1XR zDbsWbe{76!jRHkm)Gdz4O#WR=FlA1hIp|Upze&U1{7{Th7NXzE1UIPWeYOR1jeRSX zP_yX|Ql?hWe<3v{g<FXl6et)@iCW$S3WI)||HYF`7d@(J-c>RMO`nx4x_1L^f7!h! z-665?znTMi1nS_OUAFy!m*08E=v~EF(DyYEU3d1=uED(qiLdGieM0p&Qu3L2P=M{O zbdcZh$t;YnZ>Mjs%-bR&U-T*2tw!ks!`uV|>@1aUoL*l?`n6w8pL{X5J~~p6oF}Z1 zhG$g{;@5Bf8Z%LYUGl<l6MP%;dxjYXH<DhN5$uB>5S-DM-U<jzYc~%&6g7s%e`h|P zIg>!NZ~qDx>wSxp`~e~KJ3YC1%Db=KMVQ)pB1(0u{<O~zNq2kUS9KDHwKQRr_f|sj zdHB`V<bI18ER&X@#F+^ujJ(*0P<?u}w`X>r$&F;1Q4rzDol|2OFyneP&`U2&j6a*O zeRS_!Jsg66x4cv{83LD9ZhV&nu-^xjgnfCs)0;1c1OjDdScG>jU1MX1OCPyiN5Rw% zLdka%LKi(;ULu?1y`BVl>$4Bte>6`$dFZRvp%HzTW?eJeyLI`=XaBX;ywi1VA#Evx z)t)6Zuh_<eT%d50s>Y~U@lUj^>>D1*XvWcv5Dyo=>V6AC*3#mjEv(ypsq|zz<@cUn z-D#=&Vsf^6zl~_@8{4}ft#BW3+GOGR=d!In1MPem3I#x*g3fkATF9>dp>Kl&6e3~Z z(dmhh^i<V#K+splV&P12<%kYqcH+(+^|Mx1N&p@%?v*V$vfrtgv(pT=mq`!!?CV~9 z3m`yfX10{dN4(l{3XH$Fw8%m;-CWYK(get_5kkMt!@~<6?R6DTmA45CztPd4q-TH- zpOXf#o13?1-v~7`J~(gf1UXO(5!{9)U%P=GkQjtQ;WxKzU50F(M<$kO0}N|cgNL4B z+ffiSrJhb^fN_i6SE3?4B|Jpwf+ut*TsggG!w9fpr=C0a=7F4{oetXTweeCT@1&4f zj?MwJg-m@Rx1Od5@PRv<u>H)%#}EXLzZi*~HxQX&MI>s@{9C(|7Hq*^qA#hQA%tG= zIQp6g)oDv{bVa!Zw{C&*NHKs*pA<U^$^pc0Nf@aTimq=O-DbnzmMFq~C!QFkVJ7+I z!zs=U(3zVS9Fa~pERo6?`kAMH;K`3f*=+vEbfj7u*C^9moh!}QY*1wVbu7N#PUa1Y zAA0ap55=!K7Zz=Xz_=;Cg=-vz_ogXqq=2#(@2<8=^~cUUe*8!@v3_LY_^jQg09>;s z9Ev<7&Ya^M)M|<L-VYy{*|!qOJfS+Vc)s3qF!m*MVu5>gVu7*ktyl<<capzm-}a@S zGtdTI_Ep|FU%UC*Yj-{CPr^y~5PptiNUK+OfneHyi~$8T2tx<Vj%t!*Aj5))Kw$m^ zDOq1f&%;=uHdfS1+Vt|sf+b{#QsYV?0sp5`sLbk!_MSthms&<dYMR`0Q71rt15UuV zc;D|T58VL*uuhQR)59m|_&$0hEIDz(WkUyfCfE&A@k_%5EEQ@iBbC~6Ybj*K)t91E z$&W7JWYhb~ww{RkUr3OA9!}zCy>Bd%ooZjnLJA44juuR&?psnp2?KTO9Q=DPmw?R8 zw3?-z<(+O^lcWem?N%r>XKw|B4~p^^8HWo4P>nO_H3q=K@TGhL!uK2r;22#(4G*24 zZz@o7M5A{>e}f@{<cHw|zU<v~icFLF-po`I6{tBVSgV^>&}QI+JCg?D-ESd1otgFl zpMsQj7r?8V@?k%7uK~y^q*QC(U3f_GZ{d&cI5NP~RP)gJIS)_4HQ)81!XF`ozCx}N zN~Grla)q|3(doGvx9t79ia&f+{q*O-*xlyMsCoS#Mda$|sy|PB76|m`sImVWonnjN z009610EPgb00aO400002044x70CxZY0Fw)s000000000M02Tli02Tm$0OSFw0(}Gg z1T_S61g!;31)T-n1`Gy025<)02UZAU2|@}Y3fc>(3_uLn4U!G%4p9z}4^j{+5ZMuw z5;+pa6RZ@26;T#E7QPo!7(W=$8LJvz8p|7a98?`h9t<ANA9f%BAl@O@BA6pDBYPvq zBnTuiB;O@_CG#e?Cl@F7DAFm1Di$i5D~T)&EZHq}EyXTxF7_{LFZnQiF+4G=GLbV# zG%YmOH7+$SHXSxkHm^4PI4wBdIoCRaJ4-vgJm5X?K4d<nKIlIgKU6=FKiEJ5Kw?0) zK^8%ILAF9jLaRe#L*_(MM9f8yMmt8$M)yY|M`A~tNES$YNRLRPNU;C_0096100961 zTOF(`Uk^O>01E@?00000*s_@a00000*s_@a|K<M%1o{H@00RIB00IC200000c-k$H z1E}9|6h}Yzdw*%RZQHhO+jf|3<k@jH!<3u7ID27we&>(sbWNqIfh1XP30<~EYr|~a z^s`zHCw+;w+a$fQS2_yiLSH?=w+G)4JE#%Jh4?DMELa8ulX^2)0_$Ki%z+iLd>BHH z<=8Kg$9;mjd409>mguw8F8qNP*ei9+SE-%7RHHCa&9U2s>uMLpa#z7n9rl7rIg;2G z4fj$y<MZ{Jp3kFAdWLKd55ql;VeT-Sl$9R(S^;KHh*L<@VRBNRV!N__Asb_NCBF;# zAF#Vxbq(SlU6|2@*ks6$f1>>*=aUB8Y7OOYUGQ~rA?rf}V&B9cEGNH}Pu3LlJYLww zkvG+|)Sdf}hCNw-xP1UUyTxWki**Tmf~{sYyFwXae~?R{7L+3|#V-ZLdPA|;U)XO* zte;oqpVF|{kLWWN-#g?<<SXQ&*sak)t)|Cqc*EYm;HIyWtd^unQZ{)Kb9n#&c-muN zWME+W_MeR*ie=var~fNhegQ>LK^p+I=Lg#Wc-muNVtmZBhk=!WfvF2fGcfc(XvPN& zhKx)MNT7j%;k^LEo3}82K$>d`gCc{%`~S=e|1B7zn7%RmQea@{k7EYvKFqWNs6>H* z5ddXY6AAzTc-pLtGhk&w6o&tqi?!|Nc5T+%Evntv=!@8>W81d+xQp$iePuK^Cw9{1 zm>&RX#~*_M0Q(CGY^#^q2LM0u0}3v}h|l<n|FDNI0+EGW6rl=DXu}XDv4AD^lKtWD zc{}gsBYa$nlai!VDMQMVa-{;PSa)0ZK=;H-L;!R!Z?L|@7LKQ~es8eacqbp?V^Zt^ zR=!lEyQRCYdu-K+XqIMZnx<%yCTNsesEO*RoC+wJqA8U8$(vr&t+f|gj+U*(YjI*y zjENC3BnCyl=o7tDUQ_N<uGb^MaE8KE>;V}Ez>gjPlB}pWcmBe~OP8-)y>|V^&0Dwc z+`V`I!NW(7pWrE;>GTGp$!uAlx`w8fwvMizzJZ~Uv5BdfxrL>bHG_?<9fQ4tqm#3X ztDC!rr<b>nub+QFU{G*KXjpgz(1J+9j*Ml929k*&W(-4o0*YKsQZj-Q1;nXowRMe6 z^$i5fOvxx<$j!@-D`dz70D(4zWB>pFc-pL0<(lh83>}A-;WD@&-?`at<FPL@+i}v% zi{ED#?WN!4_8I&%a?1buXgzYWOr?crMx&9Aj-&}IL#&qLC?R_JyPBMT$&JO2W4>wg zY?5wJz8rI<nf)=WhSln-Ua=jARl+LN?Vhq!m<AqOhAG|fxMFCH_|G}6&3xROwMN5e zHCp&2cC=&XG4b47?C{gXCcZ1HyGcSt-zZz-S*Qj(;;V9g6_#)o6F}q{@#tKfLPK&k zDy!SFx}Dl-k|Z`;ZjxwLbMbnTcw99EKec8CK=p7g=DH5Jp##8A*h)RF84X;tR@7Gl zl9PUL`NVm&%GH(weMmXw@LgQ3H<8wSoX*+VLK5o)r%xAS9NCi8AhO4G!$YClQ<Xk7 z4QzFwL9q@puB>dZwF>`SZ+SdqNI;H-tH0OO3W*1PnkHhBwo8!1rZ+UE!YF7tTV#)z z-Q<t<y;!b>9vP<;<vOFa0aMBrHkoXI+(gOMrq0^^5Mzg2=gYXIaO<E;<EBigH#S<0 zV#n4_(sDeWFh!*j@mkjQc+$X-h{wYhavcjDBs?Lz1?(oU^LPqwrb|i)46Gt9PlhSw zDe)vo^LX04T#Ad@T06PS6YKg%k58DF^YP2YzS4G}KUL~annF#5%W*L^h5BUyPr1^h zXx*R~myZd2uys}w@|cSYArILGc}`{6mZNcPb8Yv}aRvXSCP5}$;O|1ayHtl1uu$rh z23sMk$34rk%37T=s!)|^Dds61P{d<8;86rl14<G8m+5IsO{yTs)8b^q<?mek5@<S& z+)lY3pD~3c@+^23dCnA7k>^cO6M4ZDb&(fM(GY2yVo2mWrWh9at|>-DerU*N>obpA z<>D)k-E#4@$L|@%6Yjsj_&zYc2j2vYA{}59c?lRrYG4$385l)2fl=fPFpB&D7)8zk zqex@uVcBY)LD=b(LV*xUIUw0czxjqempx3<6+_geLn@)OyIP*HBQ4NM9$(!mk9C%> zw2HcQCW@~nWrEjAW)3~RZs=Bt_Xg~&Xnzn)7=j<HS7(1#^3#5-?-tjsGsx&B@(199 z@v;iDyB^;%{<()9-#*S1O}Gm4J1B`d+a#a5GQ6Pjd7gK57t<cEAb++n(YGz@%xT2G zYk&#^g1^#)M?!Z!_chVO9KP;tACuqr!8IL-6>%!_{&YV6sX|q<f2zz>FD8M!fx{sN zthCl0OdqPb2Q7h({$pPW(>2Z2Fk738vB_+#Q+b2--pMo}^o;JHh8ht%$P1#3lF~sz zv_#Q3Ng=t8hN|!L3XhUevjl<9x&AA+9SbGDFB}jw^_c+&`XQ)!AZp~Q1DthqSAt2k z9*Vw9V8EzaihX*h9UGcWC7f+j-e_X?eCM}szfcEt@SrkU+W64`LAY6(RKA|~rnp(k z$3~Mctab1(KTQ0>w@zaKpKPh;cB-H5wm#72nc=(cfldR%_uL$TrCES>zYWUbvk%j7 ztHZ$VRtsvhy07mJ+_mv&;0^y5t=Rp)x=-L|cL7V|3E5F^CmCRjgpAGnJCeVn2L$N5 zyHoBW<um=^>?xVUQ$F9sEPix|{<$fXb@~+F!uo~bI}k60wFtaYCkKi_*jGl{kzZo@ z+Vt?wVDSbEOBQcU&nh*4^;p+t4g)|GgpZ+*iv$fEb1XBnWo9l*(4co#kj?Y=Cl)&r z`b!4$Cy~VvISi4<5CzPaL=i)jFhm(cR4_jhRSZ$X5OoaE!2C-zF+>YPv@t{n^DWWE z5Iqdh#}KaNJ*S6x!0_IshFTv|BfXwve99PG-b<<{4AoOg^^8(Fx4hSsE*MIel+qQY zbZvQWDcvxXZndC0E$H3~A8)RbkEd_bdvLw$C+C#BQ|vP<aZO_Yc-mv|-obDpC}JZc zV_)P3#+@7t91O_?8yVP~Hc2otXn`1;xj7g)AS_WfFpJ%37c&C`BLgRp&Ed3*!2!Z% zW^mc4!q^cQvB9AuVk2{k%SJU2Zx=@c10#z=M{-C6NJ%6RZ06-)<k8x}_`h`nOYcS| zpgz_V7XWuzB+CE*c-mvY1l2%j!eGL*j)8$`8{?7xKmOY>i2~XC|G$Uw%|Uzs1JDjf zc-l<NAqc`y6oujc-n%z&va!J&xNb3uz1VEaViSAs&E7>Y+A-RLJ=uG*89d>g^l`!u z4Ax-$H-T(aOtLfQAO|bXa>0U&oT$0VW%k#$;UPDS_L1AH-%;6@z=|1g^N@|+@3J%a zQx5k2mJ8OgloMm@<TCqfKgCgQ*ru1<X8n#n{aU4p<;H;^+Gl-5-<IStEQc8l;pCQ@ z$P;T4SyD-3`tZH)b1TeLf@!xNH2B|Ql8!Rl2_1h9zHqHl`CF=0suf-`x$VTf8(0$u z#^?1oP~Kf|^~V_ehc`nV^L(J&raRiyd*<l4p>4d7L~UMoI<mn^61$Zo>lm)73QzDg zs5C|upa+u&Cd+LSb469uk4K92AFP%?PYnhG)zvOM(?bRKnJ+Ku;DUILlrAy_+4lz4 zg}h^Oll4uT$4zllcg>A<l;`b*HZQ*ICZ}$oGs8%r6%*%R_2Rb4*oB67*X!I$O<aKM z1;FRaj;pGwAa>1V-PbnKCy`+(MDxo0HfuNGh0diD<#HZmHg&nmZ8Wa9+*+wW{^fea zv`7i1G@^_wbtoi9OrI&BFF0#d!S$C)5>@dqEs>#IurYq^0%?(naT@ZGS(jLm_pEZ2 zeq1U4L2eTmEvoZCX=+2U3;rhJJ;X<JObx^qm<4D@)D}NS8nFdu1=*OKoEbcgI4P-n zg7`tD(v{W?rB@&ifXc=b5pRZ<lIO=OC+oj_wxTmd1GyAtq{%`54Cj#4Mej58<pXzc z+wuGYrEg+xIs4=RX{9cv9UyX<Q=bg*e6=a2CTXzEBim8X@sJJiE5F6tMRba20Awb7 zK@?k&spS`5J;ZHsXQ{^sd!6byTa*g+rgHt3f=_&J$5m4DNM#TDGHL2d4-wuccq4aX zB3c~hX<K4P6XYT9aHWXLxN-_)YbiOzO5CHX8JDRAe*G<+hyKO?0hgs?xc~qFc-m~i zgLNbT6ac{aYvXL&w`be7)yi2>1HogS<8%_UlB9vmNB|4pG%V#OLPAnXT1HM@K~YIr zMO97RGRv*7(kiR1vDP~4ZLrZMTWqu44m<6#+a7!Ev)=&+9dg(aM>QOC+zBV0a@rYZ zopas=7hQJ6RoC5c(=E5%ao0WfJ@C*Yk3I3!Gta&7(krjM@zy)<eefj`erWd1j8<*_ z=+tFYkKRZcGaso)N5-Tn)5gt4R(IqgAB8AJDauiast$en4H-6IFltec#R;*O=4Bcf zI2wRyBV%(2?Q9999ig-nt9xQ@ZX#<)MruhSn@?hHQc5CIL?%-}CTnnJdTt_nXhCsi nPJSM9Kt?8WKyfBWo2#3f3zT++(ry5)(MQDq00962|Nj6FC?2)_ diff --git a/docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 b/docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 deleted file mode 100644 index d0178f421501dbac3424821b480c7e58b1dd9b48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21944 zcmV)4K+3;&Pew8T0RR9109CjE4gdfE0IpO3099T90RR9100000000000000000000 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 PQtWEZ=5h_SOlTAUVvCDK diff --git a/docs/katex/fonts/KaTeX_Main-Italic.ttf b/docs/katex/fonts/KaTeX_Main-Italic.ttf deleted file mode 100644 index f2c3ebaec0e7306f4ea36c98f43e301c0a3308b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47640 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 XQglzIQbW0WGIxjiEcVqP&vyPh#XzB! diff --git a/docs/katex/fonts/KaTeX_Main-Italic.woff b/docs/katex/fonts/KaTeX_Main-Italic.woff deleted file mode 100644 index 1184295def56183abcff437cd382b3b295a8bac0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26228 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 X(ymb24FE{wN(KM`00962|Nj6Ffm3-< diff --git a/docs/katex/fonts/KaTeX_Main-Italic.woff2 b/docs/katex/fonts/KaTeX_Main-Italic.woff2 deleted file mode 100644 index aa05e142c4293315104e02adac9ed65dff454deb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22748 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 diff --git a/docs/katex/fonts/KaTeX_Main-Regular.ttf b/docs/katex/fonts/KaTeX_Main-Regular.ttf deleted file mode 100644 index 8acb365453b7590425ad0fe65c41a5488d3d64e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 69520 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%0ARlyh{tY7?GU*2VZ9W&j)?g>A@b9!9`J|#t?B! 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%0Dd|4rLbv|Lm^2UjLSuRddForu%~Gk{yjR?}w? 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 diff --git a/docs/katex/fonts/KaTeX_Main-Regular.woff b/docs/katex/fonts/KaTeX_Main-Regular.woff deleted file mode 100644 index 9f8228fc38b5d404b5573cad5b33f28dbfb47cf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38112 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 I0ssF14_>G65dZ)H diff --git a/docs/katex/fonts/KaTeX_Main-Regular.woff2 b/docs/katex/fonts/KaTeX_Main-Regular.woff2 deleted file mode 100644 index e3f71eb7e9c0568f6144e45c59b3000ed3dda7d4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32464 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 diff --git a/docs/katex/fonts/KaTeX_Math-BoldItalic.ttf b/docs/katex/fonts/KaTeX_Math-BoldItalic.ttf deleted file mode 100644 index a645df64e5114034e6596c79103b380dfcbc8061..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39308 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 KZW7Lh*Zl{xmaf16 diff --git a/docs/katex/fonts/KaTeX_Math-BoldItalic.woff b/docs/katex/fonts/KaTeX_Math-BoldItalic.woff deleted file mode 100644 index 87d4f223eaad873324b3e0d42a9a0cf211929ed6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22324 zcmY(Kbxd7NwDu3~4#nNwtvDQ<;#S<fP~07gySr;~cXxMpcRRS=zWKi7=C0(I*=yx_ zW+(fPnPkmuR|N?P02tt3Xm9|K{~OaH|6l&!^ZyM66=pU7KsNSYRrwzV5;Z?djDH#a z{#QHrmu~_9U}N){`3lA^P9*;%8~<to|F9sAhoUq4ZT<@YkQ)F1pdtYPgp_s6^&4|T z$A4Z8hJPB^{|~CUjl0>ud4K?b<R}2(1CF)IuVrCsXaWF8zyH&4{RbAi(}~5u;=lL) z^8<+gK>@P`!C~>s$qfLI;rQq0{YN5{BQm><o$)`NG|NBD#D8efy2<=9bo=L%M*WwQ z{0CSFqP(r)FH-<Og#Z8mp8)`1uwTa0G=AGTIspJ`7ysrG{>OS}2u0*?2h)FEWyOEK zuzw`^KRKqmtp#a8X=?wR$x8csJt#n7K_E&)5u{k4>c^z`grwHS^lSOh^?xJp%bQ!G zee)xtJ1ynf^LS!Hz)_i`6rx8#5gdnRW}A)|d{P}hPNHxl^x!<lrCqON;Oo<wuzU7k z39y3q!9x&&@Y``8-%MU#j|dD2y>SCN{}hQ5M=XfT6*d<(<0l6759Wt>qEHP((AXVn zUn@JzWAo8*`C+ZxccNMJ=zS%P!M{;Dj$Ms4Go{Vnv>q%L-Wrt@D2;1X$<q{-j~t7< z<c~TT5)wIjkG#q@G8Ngf+1$fg;>$0Jb`Uu;c#D)D%uauA?0!$26o>kJR$#-D<FSXa zW?wANaU=}WqyJ_*s2p9ua)|#Jd?Sbn^6Xo)4@wWMHjmz!cF@>)K~NB=EQl*5lSj{J zNwK^#<`e1w^`BtxwuoVdi4y{ED{?y-r$XCrOASJUsw0UeNquHu$gdy_pdk#fPtJ3} zLUtK8v|TvCmtuJ;U2n1gaN)AfDkOj=u-ckD!oAEj#ISG^79t1^rLdY!*{vic%0LJB zodIL(VdN`q9Y5#^HER09#*V-7i%X&$-*f0w#-POljo4NP8L8C-%@r>`8IvZCJ*9c{ znU0Z0qM)u7xa_XDe9PSm7PVNgyI?gmm=FeARNobRZCp;o#2TB^%qTb$N+L!_o<sPr z-Y0WjTCj5p5@2jM@d){Rx0acnkDelqbFxx<))k(QW7=$_jqnP96NkGL<iI%hCKeCN z$XFI0J~p)pVqVI)x9_vvnvpyBcBz^NV*^t;6$5BO_hNCe;P??}fS{$1Zu69rH$%cM z-+lZs=+#`fJ~}ca@_ictYvz&OVEU{EGB+ENZUmy33vrTwCZOLbl7U=<NND=Xgv(00 z7td-j+9;RQPmi5Hl84DeaoT7Z8dVJK5ecJ#i^`g1HdyCjJnj$rbV;(6!NsIK^p2E_ z3Sljzlh)9(L!#co22+Kr(N>)NMvU$u3F8BqKv4|RNwbp733J;YrsVwu^}Qz6<7rh) zO=6-7tb@wLzK(6~Z|o5-r-R!c3(pN}<x>kdvrjP3Q{Ggt(!Z(k_!lR5+`P}EPPY=n ziN-0x2Q94rVxbRkx=wX$$NC=5Y=KhNs=Yb_^IkR}$pEWN&lK}elsteGJ^_~K0S|%L zqrf%nc$@+*Cs`plivUDJPt+PLV(8NAGPOL&V2H|&(~l6@O<@)q+cFn5*HL;4340XL zw_8P}ucK?;=`MW$>w7{DThsBIZ?ncIad&P0bdH{GhA&&|RyY-YTJE%WOV9&5ch+st zYsWSY`bzc2w1CS*NDBy?&sXH&=p!sxo4wLqIAToi72XZL%Z2xwJ?w8}=?n4G4Pgxs z6Az3Cz(2QLc*Cm;HadPR&Q-*U@*$!}gjeSmJ4zq@H&J4%OF`r+Z6~CFx38SHI8xX^ zyX)XrR}EbV$NRGN<*IC<^;CwWvxuU5v~IkzrlW$&7q2OLE|hjSh&sk@`C2U}(>{3= z(CLpW)y$5T(I<2popPdg*$qWgeNk!7S>v+XLE^A^&ZGGOjx6NjRJmeEYOc=QD&18L z8=x|7N_%bScnnKiBy_6W+gU<a`E~8MvH3Bb8kJP5t#VywfB(Vz^|7mTCSQQ6Fxf5! zd89pP-!-_v)QG0GMUwne9af3gbpB7@A>Y?fgHsVRK9Lf^UwswIs|hvRFaSm=WjTs~ zQz(dOSeJ_$DT8}gCB-{=a(H&O6J6ge6{?K?`P!p&9Ol$f$+`q7TEz*`8+7er9Mil- ze{c8qyPoGphs&W&y)x}LDkST!ub4(;db%oA^ON<*T9wiiL-adSka;Lx`?Ma052o?B z_t1&k+1gJ6{K@H{67r2G$ri`d7UT>Z6?@-Kj$+Zw{Vwl`V^~h^*Rh59+fX-!x`5UZ z4@DAlyAFRgH~xGnx>nG(Xx!4S4aZ-|t~3DxWL9+B3P*+z4+h4qFBq5EOmvOi{gh2y z)s9Hz2dQ#@)m(}VXEXvJQVWvIZIkfSWh%3+-%<rq<YMaXQRm%dY4szBej+wroX62> zzqUUlJ!;L#$A|_Co(H{Z&on1|y{!6zm*5&1gnRbmp;uAiX9WyQP|<vRLc&-!vh=z} zCGz}Hb7E^v&mC=uefIQpfuciqKP8;ceW2jkUx?7tx9#LidwFhP<&YHe6oLyA`^&qz z1|6FH(@}68^MSq=dbWId%ywA=tL*`r`GC)#ff_2qIqdD05)>Z&FFvsQ<yPK9``^E% zn3Ufq<G<Ec=Uu}`Bg~(IzMVr%LS|j<PGq9m^@cEnVAI)8g6X@TZO|=yyU6tPs7-Xz z^0ERrSUfM$5uXXF5^dj<&hxn%Fy(s2YlkJSi(%%<*J|Rx{F-;7QgXVzL9a{uDO;r} zLS!1u`lBHWBmL%KFnPT?F3(D4F1U;fQOs$7ZuX)?zzf{$iaQHrwQAi#@E5umF2umv zq0TsM_H>@G*4DT<g_p!QH2l4ZTgq(46x(6t&7H9|r>J(l^B<<!^xIdis6?NM3cdX? zkpfOmXyQLo><cyY#Xy$KFpVerO~V@1SZ{=;-~+BMTWB?z(cm*8{g#9t$NWfB?zAHZ z`e0KL9gb5NT7A&|bleP3V7bWkDGo);NH=2MW*3;&_kP#=f~h}vi`t}fOi4P2bT#hW z_LFAK=j|m?b8_^5wws;1e}B^!8;qqFFMtVKxZ*)mTEVoq9`#%dr_tSpr4~+Y`+B<n zb@cMs8)Y|68H(nT<A?~0gW5ihCfq;?uXJ;t?R}58R#}!|XhanTY4fy)bs_j5Xi=5O zepvUkpxGRAh?S#S$Jgvh;>KFm7S)aVD;<0t3dOtZo^=<$ijXI0cNk^iTk(*B_-{Jq zK;xI%0~-T2A<X<{WCBDyFLVUENZo>J$D|wTh3<FkC5=EI>Lz_4J<^Zhi<^q^g%ib} zyE*7|!vPEDdXLM{VJLp><Ga+v)HBY&6-_eHr9qPgXG{o|k7M#mq_0d?pjE_HJo^Qd z4Z76|QBb=FGn|3m=cR_e&+QBS%MrfyA@`#OxK&}G91NQ2nv}z0(r{TJyhFPvooVjb z#N@XG()hE@b&$ZD9#zc(FS4icLvx`5eBx}Y5)tXf3rdGmSZ4Zkb+E96=;+B|--DbI z94KboLfU6%Mv6UYNYeGZz0s%3kC<6fEMAt$iMKvYz=Ag%Mu{eBJ8JtLX`~^%7LWoA zm7{UIfcqm)q153+xV@TnJ@-odWgpZ(kyKl=#hG$Cj8y-&`*Nr#ieZ3+YnN{zU^MIx z0Wo!TWo1hl#%d20L<28&X~8_m#Vff&M4=i?$_)v4GrU%;)$^gMRy$GxJ?*}%CDCRN zrOe3;OUlnIOX`mma^~%XAXipksTUB}7iPI>BJ`b`B<>PFKexw-No-yzc<YL?QbKMU zqF4%k_Csf3?=~JvEw+0i#4SBy1p4g8z)~3fijKtnRZ{dINpNVa3qfg3Mg@iYO9-d& zaWoX{{HO?lnp+)(kqh53X-<(1a{*m%!2CmkW8v2>6VB{6(5sK1X8#WQ54T{vU(SX7 z<O<aD#3<^%?8t<ZtkbC!GEOQ<4Q14lP}p6yFDGN+Wjn|N*IzzNbs47%tpeK<z?-OM z8iUy?l&QksdtndfvwCskD4VUL?_pB0->`2^c8#S+c{TiRPQ8n{_QN5ESjS$0QbV6l z-R?|JN2~{O7AC!5%$t4c3ykL!gSem9DQw+$<jM8{m+x;=zVNh^!)`#KTz1<Ygz?=$ zSTsI=sgzLYAX7@}7*<2v`4iUsSD)orVKa0F{Xos_^Y6Zo&$VJO>FkGniexAA=(an_ zZ^-%3{J3tCWlB#Y^v7eZpSgAY9g@m=bPCsMqZ^Wk4mx84ffyB%a^y{O%pXs!{6sw{ zuOueREGN+=A9Crwav{@^*^y=fX;m8sXt%f&qE*|~&na1gj&7%#4eZHOdnFP?lhB{D z#Jg|SD8zYtv}B?eaU?gs-KmyE_V)!sf3Ci`Qvz!tgl9NDCx{9<_F3R!oMYv|<Fas; zto*sS!#E-CNEGoP>2{FdAr-ja2tti@aCCw<>0k<a==@jS`yE!ib`E3S84xq|Uq6@i z-ws-2n7#HKx4n*@FCI4h!@?4VHBzP&%s=Sv42e+_h@Fq$FR9lC;|i?xy>Dqgmxo@; z`tnk?`MvgJoVQ`JS2E9Da7!*!`N5`L-Xq-)B45dsz*xoC9a2inO@FkPxJrQiP$ac^ za)5zTe*%I<6A3y29{anNGZsfXKz@(4e;ZHjL9^awNl#Ax;X)l*4-T{IZSCv2=2_<x z(kYHit9D<~K?`HSr{u)nZx5m>5bXs8=nMPXVN~a+CU<>H*{RF>shNt;%YYQ9(GP#9 zTz|4$ppuF?amNsB`O-4pOwD{6#6r4~DUG7i_I>CfPm=iz3SfTj-p{Jj5r%`YH`bb1 z8{;kIA?0*k2sNcN&_Tkz37%#K>J%KgVHZZo$TlL0Dz2~1<vL4hJC8vDsD9vfE(;WW z3MBOkG=*?;_wX#?Urs*W^|lJIAkEJwDG!#+E#_kx3fwMo2oWoJ{Xy4unE2Z(4_B<5 z9Ps&RCdgu5l`+7CHHrS==kj6e^>vy+8hy2p9rJvl+vss`Zpw2Nll3q@ar&@My+mz{ zG`JutA`$OkrdjqdR!Hpn$svG0(^_kfJxlD|I2I+>z+7m>JBOnFz?|bek31f7gDtPS zrulV;F*&X>M_6rHC`?a-r(m-^&!^9u32PC}WIlrXcQu{+!*hD!ozac3%Z>MR(d%0< zU$k7NXuNi*lv8Ut1cow;JGKt$1lIagE{a!rq3SQi@{?a9O*__lk`s@1q6t@IHcwYX z8_Jj4%C{aA2*0lf&9j5Fe;0WBfS$+Vyd&y8#|G86zF?2}7Ku@0oPDl;z+g08+^<}| zsV<?rqcnzsij<Suyq{pnZ%cdxyVaQMM$&X9nZ0joEyyr-;*<s{BB+9;qL%vxuTg?! zK2N&HTN-3B1$Eb=ou_5B%DJpwideCBy`053sT`EH`>P6WroWjdd_J_A#5&?;m5!h& z7m!mRNT(#IlIdjLN`LB@eEIk207)HQpCm&?-`8vHUwjJ2cVS7(O{`4eQIXRKk4xmt zZTlq|N~*}hNx+GOr;4dVdGxDEDxH@dgO?JFT>b4$-o=G4E(D8(c$;-K*hRkt&}Bac z;Bh@LV6`vVOCFodo+6Rk@tSUnA0y*4ANaqsZ#5~|!9Qxvvl;ntLN;k-i){(PdUULz zthejxQ?8aXb9myh=aiUxBmb&m%DhvO^{U`=m7?70BEacx5sD?$P$C^E!MwYm$-&A5 zQOje8%Ni2choDrxaIsf~PgV*NW;tk#XYLv*k3yl#!}`j+m}n93Hu2Dlfeh1gZ;HIr z;t-gjEH#JN*m`&!;$8KXm4oK4cD;9R_sKLzX{d3IP9N@16g?_y_wuVrYW#6_x_dcE zD8O(^nEyE5q>W!;qT7*?+gZuuDN{kRi5j_S7&SXaes8W5H<p}r(pd5#`<*bx^Ghi8 zF&kI?Gu1T@HJXsOW&7U1kO$+@<cybp_kR17U25Gm*2vv*on^$+r7-MAq^qZ#$FhgK zgWz<<VJd-*<DJ_On!_(7LTX~Y)p~A~aT7`D*t=F~=|fE&DgN-`7Cw2L5qlVEYWA|m zvLr<jzGp*aCv|p7Q=tmMCm%Fx)FT99Z^!}~Z{q@NU?{>u=BS1i<DoO$NBBPc9x@bu ze=D5i_4`M!!o(piMrTxnP0l-nXRq8aW6SL&b-l<RZ+4Obpn1{*4via@Ey+1tRs*ok zUcO}$<!4#K-N3<6ZPCZkP0q-GH(t~4@l)As%Jk{(^MD0jSN@ii!bS)gD()w#U#oG+ zN8J3MYt$HAXJ+sZ8Z*1=qpBK{)6F=ZU&K0?{5TO3fyn5zNBEPYA!<dLJY?C$wF+K+ zaN8Y3;cY^1#@8EVKDSSMaWhfO5fa!-OEK5<?4lG!#K{R>$Fx3&dH^uP(cHIm5m$(& zWN*q4*kqeHSJT4yOf(VgTr)s$RJIU)Gqz14*smv>70i$%W3zHOX9i7LgFrjC5hIjz zRr6gXUP=+^MX<F<q-0_q1)?^+pKE!>LlFXp8AU`B_e#4D6ILvwnEQQUOBez5S=x~> z?6}3WJV*Masul1t%h~+;=_lVIRf-(=|LxhQJPFC7QdKACu(RMe^WpVh9|S{K@KMxV zudq!XiVqq-CcbXRgM6+&#FfOT=HRHvTVI}^mrFl8n=ZD*cW8kpBHRZSv~qYiP19-< z8OTApF?6QYP8hNMT!zb8;o8Udi+s$PzTVqo^QTrt7aHUcF*0<crPT>59q}JRGLf8~ zQ}_K>Szxo6MYzz14vsr|C=NDHp1k%6DrdCX89m(-9y+J)#BaUV2`4mm@HO7VAF}}s z81gK{%B?yy4#RP5{EY%m<Vh|#$A?ZPG<@yXxU>jRZ;xW6?u@<#plyiwuF<@U5CYz_ ztI6L?PJ%C*F22*a<by6gF2~0dG7^#dD0SOk0wVFG8t@q_2TKfzz=hk9u&+Jl`pL@b z%<Q~gKJ-T+)Y#Wm{p9Yu4M(!RK#1ug$eylwmPf;zIfsT!io#`Z$mfY~tVrHMznaC= zSKNAZ>zSTw;8qAoHhf5Q7IlR#`*$F=J@%&%hy`137F73wWmZA+czWKh9c(e-%B>rR zMqy9Kk-uUx$M7#0u@AwtJ7<mH9^4vVT~oGeJn1bNSJ-t1RUU?_D#ba)lyl{pjPHmE zMaE&QIIq|^h%;+PE>zM==Fp~s?TBxc(G)c3+6d;d6SQUYd9X)ehi3)1jYg?gnQd)d zfLJJlwu#~Ew;ab^we}s+_rT6BNsB|*7N!#B>?o<6hk5Y(Yg<3xix5=TqMTM877DU< zg#A`WH#3L$%g2DrYArVT?tuFu{jDeRe4Hpa;CoTYG5$a-6dxZdxG0pLfZfWkevI+m z`ViOw4zhy;AOuVw{?Ba33yZr7<Ljwj?LDzAwG4morZ?8}cnLr*5;k>)0(SazMY1ku zM?0gdXU-YgRgB@C(&VWUZVcDNpvZQb%D_ra481iYG2F1w9XmU2#SetQRLtH1jEwc# zQqH9_$GE;S<3A#o4|<oY7xkx*7`Ey$-J!c9d(Zg2aSac?@=Of#%e-sn%=grS598VH z(0mc5;kDVl^^f-3w?&bBSx4~J)MXaO-DGirFDQG4*0(F4ywE-2(7S4G&a0Iq<bU#o zZo_+ELwj|@NMHvdW|qZCf^?O`x9-Qg$zuYs1Uf6qx#5gq-=3zXFhTve8vS%tG+5Up z^4XNUdbY{@?)3u<Zn~S~rq?*E#JZaav7xl>5e#A9y?;=+`H!r=9A|Y?7AuuaxN)*R zB#ff`6v(qGVi&)X*cT<DtmcQ3SqP6@e7Fvoiq^4-A(fm;3{(@Fn(Aho;$;(%&_X#j zk7w^rd*uP*R&BsZ^%A~%GsVSP<B4>EhWUIWH#%33e5zu>L{2|_XPn>BEN~dM?N)z~ z?>j@DJ4R|Tw3^I;oWsWONs6G%rvHcz8OU1HDO0uUfQg3P`*wChW#UWDac?Yc&I=Tx zA%&(j(pwfSvW<)znDNPH4#-cD4(}eMo*J6{i|@~=x29%CPttckVkETEm|TgW7@ebh z{+{i<YB095=lkN6VLuRP=UlsLB^iCkg|mb|6H$0JlLQLv^LzL#Jl;SMc3XO<SZMrQ zQ0V0aVRWv(bi3^S!RxBsJR_i2ny6?=m`bJrR;0p{j2e!T>CpZ<?{H&i_zdVQ3~8^; zwLF@?%s(?ZL@>WA`oP}L>j<)Bv7{${_K1L*uS6&RHmq8FdJMs1>Ad5uOUtjq<Ksh_ zv&ZGxExgp43HhE;8V1b-KM@Krz=bvOm&cs*{_z+9thV0x#zs3MB}hI@krWc^hThsR zlg)$`ADve~#qpBruBXwWOp8gU=W8(_H(!M6i-SE4IsbmPTI%+wF~$F9bORBJH}{O~ zgsu3o)|*_E?Wd{7>w5x+*M+EKy+9>M5xn1k&MugvKan}RhAi*vavJQ9sdgAD9tH6; zi6ZEZcG%174P57EwdHOvsF+}LO8a5s9nO$+`vc{M>m4^;0wK@`g60oIo(JSSUjfxm zXB<h{emf*78Uv1s-yBHuHu(%E1%5pEi&o*1NxkG6PzOi&S+YXt<uzJ4DsmaYnq2h8 z%GEZhNvOUT#(}w+OHYTgDKW%dxBxwQoZffUwW{?<Y?X0^qJ)yAfPtmft`M)v*3+#A zy6;h4zYK-}7!UyKo;+ApEij;DQap4#&mlS%-hpn3=Tqie8)@KkwA}6DiO=4cSXif4 zgD_qTR@|u;WeyiPyyV}Sa{1Euae|UJgESJc%2&L^vCl2@BS%D|+c(n#Z4v^R(u=<; z)x$#H3b<`IC|T{kz9SVQtPXgdYW62&2uUI}gnkb^&5cC!hoB%0pm6ACW`q?Bo++Jg zQ!C3ZJxn{{i8?d{m!?)NKBts>#L4Xa>8`qlukP)0HH%{Bad*2P(^Ua(cK7H@9mFZD zF7Lmk_mKy$1jbp%{*eA7&!wpDbJvmFlGi;mxWI+#mYvj3PqGBGN;hlv_1BFpZHogy zasuni<>pGX-RtXDmCKA63Dv`a=*LPTu8F*ul+Trqo_xYcab`2dNFkl=CP(OiLz%tR zoLo)%GdoU__iN&Y)k~5e2EGlng(CQ#HYS#h<_xxfHz?sus$RBmTtKJ5O_;zZfbtgn z)<u8ci)Ne~F@DWzH1R{XmAFH*Q)x`C!OQ$C#V{D`O<82APlUGbt}F>FAF18P8kJMN z+QL%Zx)F41lPN2g#PAc`0I@3?2~^duTT}1hQoEv<e<`xQ_ys!Th*duDUfq82xsf%+ zG4}ab!A16>(>N(MYURPU%0lFl2}Va1g6Y4>)3@{;s1(^N)5@dA`#9O;&2%_)z|9iB zM!es|G3($ArmSwNY{zB|rimZ;aIXNVtvQzZe2A~OIUu32?H4zSZSyf^i>S7N76mD6 zI|5mL_q<P?>^^y2{q-p1eHlqIBt7}<Tf;SPyAbZajtD)&#^4#88;AY0I9BV~b<WE2 zR1f@OV?2VFcBP?!nkx4JQ{cZSFhsg*X5Jv#we$Le8o*hn825)rK%M5qqoE!2n&Doc z7HKY}`tvl20T&$aWPUl~RFfAiv9V1iOOG}qW;d2W|GTL~&T9pDY+R6eimlJ%{N<8d zn-V+~e3l$pvY+w-S(ari8FL|9o`F==7ch%6^H~u8zysF*cvt93>tp;xEKmv-z)lhi zA^%;t2radqi)|O9E89`Q-z$ad?%^pxok-NfG<Wg*qio1WfTI1ji&2vxN^oV~v*>Kp zsQDQ7IxKr=c?WBEsAs>q*rDC?N&6{)Ai1>ecLzbIf1`S+a1R%|NLWa!xEn>A?oyIN zF|iKozT;%R^imBOH3Q*XZ0q<4Na3iN+>ngB%$X8h%cB!Ft_6Blj&Fb^sXzD(y}uX8 zt@Y~v0<6enl={kEg)K<62I)Js5(&36)jf+M(7<_y3IgG%7qGOaV9lEkm0<Q@-8R30 zM-nUb&~w8e2@fxQJ1WL2@?x{SfOf@-21ivK#yVCSksgXGJ!yFZ>oiEs!FFe8xI&U0 z%<wZlb<i=ry|@^>`u2xfcrFQZ98a*B1EkvB9<e*>xz#kaXS*cN$s<4PaM|2FlNPV` z&INQLA5bRj%~i~AKJR=>F(~gHbw%@e+V0?k(RXk!%=>ZQx~=syn^X>b`@SKT=9;DY zo5JY46sA*%u(R}?7E^Y`t~HwhKY88s;{XJDUi0=ooYD}=34&v#Sy5uBc>k-JLA|PC z%O+&rVOQFd=J&lU6k|O2ZAi~INpHMyY-ygQslocoj)b?6$w76}<OnHEWKPCANKyxb zVxl=-6t)Bh0y`TD^lELl;)uX$JBgc0Ich?MX_L>p>&3BEy)k)Sxh8BLFMdqKAg4KY zV^TO3PDMhB2C;AOpMxodt-4Xq9$mW*Al`dNNslckI$f9{Ne{u8(n2X!`%Q`zOdouE zs#ay5yoauJn>TgUhro^}kGi`WY5}Lyc?AKFhl{lRH<07}q!q92DV+y$_WXP5^{#p^ zTc_r5xBs5vhIz~%<O`)YIjKgJPTp{Ms7@5o2fAtQiuXW7aq~Zg9{9l?jhf%Zj8@C| z$*tJ3w%E>l4m=8tGh|X}9A*oR2t=3}Ld5Rgr7cG}dYnuo<<p<Ekxpa2QVl{IH1Z$i z9*Q%=vCbmV2sqU9p#V&{wC$)Tw3g_Cq*p5Hb;rhDRg(@KXvx!h@CQMM@@-s&M_VV6 zJwY6P`2+^(lc?ZsI={qH!3TevXS%d#z_J89QV|O)rjei~;R=gFh=Qf%;aWscKuOp? z2F5STbo+k&5>W-t$5{YX-YwEo54}3ui}qqR`+vV@*ltWjs!o+7?wgr*$6I4+@`RpJ zAfUnG%Y3Hc=X8)USS;PIJ%{cF)*c@t>Yy!PGIYfPZ#AD)e^UEEKfc$;2)Ug0?S2lu z-Whqek>Lr8=!#eO(EG<dPfnuzDBj*bZ9lwI()OxYn=)Zun!cn?V}Fz@h`HNL(r?VX z*Vr~W9GitjPBGf@LNF*vqy1ruVE8lLYJG6s9WHT5uImO41|&SF8m}uBFA(@Q729LH z$gKubHT}|qGFYQ@vC*Lwe5V$>{ve3b_xiJoKwduv7CRV@+<dI|cj-uB_>{VubXiCP zLVA|m#CB>F&c`1~iG<DmfP|PtVr@p=O}RSpuc$R?%`s{r*+r?bC!4I@${l?NyF>8r zoIHYUSa8IV*0lK&beqW{I`F~vlz#Lp-SHS9!#Xo7a_u_z%=_RvirwMkH*bgf4_J`5 z8V8RTnOVNw4@vHsvLKsB`rAYW>v$)RkqMt?(g~;Zvk<`(HWWiu=T`3Dt>$^(({URH z2CxZ8WOmUI^)6&aOkQ$PUBS!A$PpO0_F{*jndKh>^UJHt6Ci$GZ@4|&)n?q5R@4k{ zD_1B8#`}WK9FZhpY#QA6YH(xz9R5wwYhekM`TZhF<@>n^`*y#YqOMS*sA@-3qb^t< zrbGOJvaQ>1zLDd)>|DGIvux#Oi3%%-0DlUhq7LTo<Oi$HqI3nj{5_NH_qC2vhksrA z_L5pJL>-{*_tkUn+@4MIPuC<?TL}}m%Rbg!gcglH-JyJjQy5lB`6c3P2emqGFjvEg zj*>qpVn2+jj*UaHSh0$OjmKp;O3}{DKls@uUb&r%IAANU^;<+H=}T-Gl5;cV-2Flf zPFT?N1DTKK=S>$!%_@ovX8t0)TT&1xeY(F4DF5q%n85AuiX{S=vf)hhwsn8>x)Oka z)g}=wK?u$y^=)p1IqC_RA!+Drxy&ZdP$B^X|LqkddOr+gPQZ+8XmEa^8NdBl5rgv* zR?ljC*U?VP$g)*uizZ}K#dXf(Q%b()FdyLo5qTZru&^B?A^ooQV=L4ASnb~XHbb_4 zte<@jX{C~Bx!!I(l_^E}6qyTnqO^(&W&0QfdQuZdr=F)Rvgjs><6e4alpuhqsi<M2 zzdT(90Mos`0Htu%$xraq@|a8Y#rJlY+k5@p_PligO)&VpfVo|qKyz)1o;gAa(%6|_ zTuqFNVuEu?KGjO}SZvbl6=fDs>Ib#rxcEVD!P4)pWJSco-vS=-D|PZjNhaLL&SYg< z8t5#Geu<q{HE9&n)!L95;Cu9N?Vmb8@j3J<3%zBL79WS&?}Pzs9MnPz2hU|y7GG2= zaOQbU<BQe0_W7i>pP$8_@V~H{i(A6<2udk?OvG?hx_y*^sZu7-opJZ~z&JEgk4Z87 zu-bSyx;=+ue-qnU<C>;_Ek2X^rZn>nt0{yB)M#nMdrbPRr>BVvG$AjS=Yp)goh#t8 zCwheA>Zubc<2oKkTiPkM^}CyDRY_$c<?zpUU}|~qm6-<<`_~WRQj1Duelcut!*(<m zOgNg^Bl^iDqLU~^p7ls=Y1tt}t#CX=XYrVCDOxJn$@wm28PW?-)fKK^<X+&u;FtM< zy;aD5+%%8MbOEE|p@E^$S5kV7A>c#%qJGRI=k$&rr^P3glKt>v?KOPLiJ$Lo;FI)E zk)rZK%vqr{Nc=$;a%RPH1Ru9^hIuPiDr#=st$Gw>tBlkbdGJHa0~|2LokUESfN_Z; zf{5qcZdDt`?;Y#NKA#}J)0+9_?rN@traF8E+Oe7#YT(zokXWL@0LdVbl?L6<&6<_r zZR<HhC#p5F%LnxWZK5y%zykvrx{X*yN@W-pCL$2avLWa&q=o{Nt5UiN94zyc!a@ZW zyq9@%w`z<DczW2GJC8c^AIgm(p3}7<rYtJ`4YU88VX6DhR1Vt;@_yVr`aD5@zELsT z+(6({ufh!5`7Im+Nln)*<0Ak(xJ$(QQVkfD(IrQfasu}$E>HJ#*3DjJZ>!k;b*F37 zdri&ondRADC5w_OD~YucB<&{Ai`|FIm`*R0!$DcR+uD|LF)w$FGn^$+dbf-9%UZ%L zHdYc%3H}qDP)x(O06sC&VFfWC5-t4CCK#Mo5&_s~JPL`00=f<^&7x2K64A2%@sPYv zVc>q<NPjDjOUjqc-#R5k#ry-~%*mq&&cW}Unl>M7H&r|8ZACHjAE3>xWP0DT@%HxP zHn6nX-xLNX{(To-0=n&=zueCqlQ-Ujh6(C4A5;71vpRivm0PuJNrYUm`P~UV_Nrk- z0zOD(l|$Xs&yWkU0S>VgEk_VM8%DXi4;iotIB^}cl+3x^IZXE3=8e~R?$<GBP`xv) z>{S5f>6VciBLOl;^VCni1hk>hguQF4Lmy@~+}NpY`c{liy05((81s4#w(1G~1SX+H z*(RB3*E`KKOGuTY1{)#7C=P|QSC3KFJ*|5nxh&0<G1_@ae5&msdR(rtQvQ)qW^Y6q zl+ksuwXgZq38gW6D-`G4v+5k8QgJGu9*<QbQ?UK$=pVZC;NY=d8={58SD`Umx9YBY zxsk{#D$&(41d%}TJ}@z?dDtqTzC-1h%E@f+Vmj=0$66A^2>@rym6qo28*J&7P;=Z^ z+?g8WhOezG2}P<Kv?j#Rfi?Vy!w*59k6DCcc>?t;k4l}YRd<whb~@F~NrmCJ?LvK` z5`_fBEX4P}`!P87TP`0NIay|&D%ys0O7moyL(H5AriVcVTxRk`iRz=yd+AsRrwn$n zKjl0ZMFkG#@oF;y9SK`obz+<s+|~;Qq!*dS0?@OW8JCd4#RYS7EP*7Ns`%D%z(#6< z&U{ot82tC{f6H@`W;rJXB1K!qk0NY&=E8|nQe2hs=v+LjNdiHq7azC}6bLm4#1NKD zcC`;OUZ`1{1F7T}Mo1O{XiJpws&H(AHpvS-Bbz@_ba(6t)1*w>?$<CdA8*-lh2{D@ zZqJSy{5ikxp06GaDz`JN4%pNwatmaCOFww#pfo9Ca1sqo&LT}qymQ|IPnW6jcMIOG zzy_lANxhD2@xL8Zis_zSLAIk$VAU1-07SiQmEX!l%+Jaq*>odK>pl9+qM~XTq<Oo# z&=8h{d{TZGbAEJ0do~(el1FIv;>*)ODh_6~;JO`)JD(aYq#~sSr!^!np;r*ODEmoE zZ5a11<`jCy#I)gKs)EyL!Bj=hz#&M9iyB^$^s!QF%H>Go0N*8Qfp+60qTb@5$n+3! z!<`Th>wQU6`4)!otUV0dZ4lfEDe+F!FWt7^M+a9^N40w9<)<$D7{x95lDp}G4(mQ| z;V2)Ikci%1NRpORIULb~u>(01#Psz7;eZ`xa{t6MyG?$LIOo-|Q^$u^T3i|=YMPQE zL+Z3hI+rr^0tW_M4)BFgX<$U-8uxUvsr%zAIKFau*c$(0L7Q$ycv+p1wA>$|lJbio ziTM&}@if#O04cr-nRM^tZEOG~wFGH-IvopKy+PUh_g1|FJZKpjlmlfoEZ7KY6W#)4 zUt3jsSE`D3rKGD%IZX?rqrt=I<2;slR2N-@-cOI9*kkRENbU)VUzHP~$o{zlezqIp z4_I&n3ONm*<{s`P=<BgR&pNSnFeAV@t>sPJQ8t5O@$E!&E3w=ip#)*7Jz8}s9z8$; zu7s(Krd8tpee1qZS6MD%IE>^#!HDiqn?w=5ZZwDhiPC*ofGC>MQI7_V0l_!>nNGX! zWRGwpiSlIlKVQ-ZF3c1fSYua479IxajM8+%)dyFAc8@sykR@RRfS=~LX-1Dw4_VY* z)*sJ}Ij^3+Ijl0bF=%N4azF1c*HhX3BECM1tKVIP(L@}tAV=-rLq<eYP9oRGmr&Go z!-FlmF|@vADMC@mlsX~U11aTm2#^ATkxdO4&K7@l;7NKR>gGJ=kh*r2hje}On6Lc% zW{vOC%)O+M{3^g|5HE8A@6a>5va%Kp11I5_%mNK37nydm`m$H*$ECC1!(jI}{`yZR zyW1<i4`Kr-aM^e3^wf8bi)gd!afrXLDifcfTJ8a~Yi&M#&1g4VASA7m>5mQ<C!8?k zaKFU(rA8=Q7l+_`Mvi+phr%dW5ct73pH<@PdJB{b@%8$wmGf|aQ(6CTjBz@%+v54` z=P3>5QM^o9WPt;eZXrA-Bw|2Xz>|`--#Kp#B3qC9NlW*hz`91kh7oKbbs-}l3!U_c zh#fpkE$!*FCr>|*obTVa>^ZRN-?VTJ)K4SL3vwWq3&3TnHjesV=`|~uby|ow(CCoG zJjBh2c(EIJ%vdw(MM@?1gw*D*iu@R?X+I_xtxn1&h%w_f&T}y;x>W*{p*`g-3*M|9 zq{7$R<70yBP}%uH{CSIA#_>V?ceMNA3V?i@u?EiIi5@_IvZlmf&r0rs0)c3#(JDXy zV{<KV8jzyj70s!3r;;WPt~cQ-Yp<Zn{X8SG$njuFaX~D>9UElAG^)S`p>hUaUJO;0 zCrm<s1N9(_@(TF5XzQz%L!$aS&5zi{=aP8W`e3Y+IB)9(PjLUMcVAxE1YZMsb?IK) zuEXuE3}2(VMQLuyz}w!(#q8DS+~}82E4m45ocH~k_^ciQI2?QhOWjjK0nMI(w1L5- zVQs!OpJ>m9D?O;b4=$3RaXDwV>#0d@+&<WTD`mgnwAO2C8VfG2=*0IGA384_ejw1= zLB+_Mf%QZ*r5q~PibgKUmG0+VzmO;mHP(<^M9m+d|L0ANpQtp4sEJO$>Zdr~w@-1f z*O)JrNC!qTdpvvMY-*s-@%Xh@n$;{xU^neFY8qg-mjKxyO-hxt8yJGY)GcfpUgVbg zH&>jd`1Qpxkrgk=&jF*IqVB$*>2$lF4W$98N7xUriKz4>mE@BWUYA;|BWU@Cv9$7V z8j~A?f;e50v;7O3hLXHG+vIs9gI<R$f9x!n+KEEg9x_0x0-`l#$Iu-4Ayy(r$H&pJ z{_E&S$41N)MbqQJ%`oM<o4Bh`oc~b|h@qD~An{hT<M?##f<xHySCh!WBj)J|8!@uu z^Mg-A=B7pr4a@G|LU6g4(>(Kp9H}PUfS_q5pI9y<$yWpjQ^peA>Pwp80l~(QiUoUG z(u$&sb5qf7CybElMK-$IxR<F{Ua~1bNEWya=CgNvyLyHzsg}f`BNsh*`9`Fhe+@T+ zcrr93a`Hkwoz)xpHK5Rak8%02<P<<(^?Z&))LRAqe#_?z<b<AI{@X2TyLpg*^%w>i z5D6FOe8xylEKUff@SpZhE<^X-hv;c6J2DG3ES~ojfgOyes(+V%HJ+RP3`!qHk5heJ z4jV$tB@iv#nUc^G+2)0%!ob0RqR~U29qg@ficjc;sM^{i5rqc?iH49#->B;c6`>`7 z1PMDqihWP{_7uHFn^nt9mulrv(R(t!PO277SMvMoEkfK=42Zs?{Re9V-A!}a{UD>l zGPZIeL{_FR2n3$pztU0IYbn{hS<9T#QCf+Bqw}<r(bm-Ph`~WlJ1;LP(6usBpyJrb zf6eAxKs~Y+qlB83cBX{wju51kp!P3laQx2mK+UFHR>Yukx+tlsOT8>_kom4fD1o14 zJk`+1Dz^_`wyn7_OnRjfVtn3YmMCNqTA%NH(#P<O?8$r;RQQ4lPOlLPMgSABk&G8% z>WcUQ+bnTs4u?nrLKu|-vC|HI4vpvutJCG&X=i1cOC@SE4|@igG3w`D>D3&_LlA{{ z%Xq_y%}w<Z-pK4WWHtB1B$Bqq=?W%nn$ZCQ;dp<U^~+R;YpNU7XfdH1Ef2>yl$r6B zd~E4m@{XC(Xe&Rh@gWxdRDWyNK+_Rh$Qly7rh%hD{c-|dg?SxqTC4I4XGgdq1(uCE zXVL)tuh6ctLGmKvY#fozDNbbLFjmi)GP_Ar33CR{4lUx-WyUHkz^rUAypWPjWl$Wc ztzP&lNA}ZY$G@<wSu3q#Y-bzL1Ct?(?AOuCv~H*+_K8_>x%HNDg=wR>0OLVrb^^C! z<FpF}FKDyI=~(b!*o1uxq7MbJ5bJs)qkY#IxnpU2BC&5l4cTG8ZufcESS=gVrvIqv zL!UgU&^H4uPPTJ&Q<7~FjrU36M`XU?eigokpMNvDO<GVN;3~L#a8OkbG;tR%ckI;3 zI%Q&fl_jnSjybv-KnkNpf4iBGb7(IN!4yzW)<~udo(-~<R1x}3or~Ya*3ACJphir~ z&ZR2)Td7k~4r&KMJgNlNg_%DqRaW5%T>l+CGDUPJ@73Wwn7KSH;r1nSUa8iz9ENx4 zB5GJ~*1+XYAB2<6`7#(z?dxLFNL9<BBpO+D`?n|WlMG+bkugq`HsB|+-hj6?M-~R( z07;v}MO6#gahx>draR82XDlL?z}>+<_Vm>?(*)OfQ3uxK_=GiRGDZvfW3|0{B4qqc zdS{?*9Io&M?{Su1l#ao1Z>I6v=O4InC!dA%N(-xeGLVNirLy~d<@NXGG}s|!ZP{8) z7gm~O3OpAa%aw0~bCdJ-()o|Jj4IT>J@(0oRh-5`uEC4r(UED<J4F1=(LWNzhvBXe z)x$kn+LqcEaz+pTefuzu<PrA(Ea*Ao`FYW!vH|;pn?OE<W^Flbz1LN{NN-;+cP`OE z=~#-Ad~-~hB&+lnnn>o>hgQMw{_-gDCg7=3n|NlB#0P%8pDo)L)jl}2{Jb`_%F&$! z1T#mqgD*>}jV5XElHModHV%=!j`ofTr5tvHVs@#d(K5?=?2$jxCk&07g8wd#`F4G3 z)!s5HWciR5(<bM;qjE5I<{C<y)51wUV2;Q}(P&RAdS8v6yI6K&AwmZ3{#~{!PgMq} zb$QwUwz=+`vRcZQnWpn_W6$n(L*+n;peV&hmp<r8HFu~5<kV=#Y1%royW46EDVNQ% zMCt++$HeKpM#Nt=X5F0#b@6q0Ct1yxg^wU&P39rc$#JDslLt6JH5>%!sZiC4SXd86 zG5*D+l3FW=8xNAit!{VEk4IkJKNSZ1MO1^s$D(Co16=nxBpP%h#HKNE$IAD)?{7_T z`?2n=dK0G6n3n7Z;Bp=^F<eh~x&<9|;VVo1@1HvGYL`8<bf$y-|F%ive3H_BUeqe@ zO)MRG2uX9XY^4fihJW`bVF~zYOsPTteRo2uGGAG(KAXOd2Kije9`CF7(U!r<D>-el zF{O*uuil{pl*c&1x{K*c((t7<;b!Pw=Bnd!EL1Mj(!CV=*e2WRBt;6{SZNV%$reUE z^~nZCR~|FhKL3o1jZqW5#s7X7U`^D3sCbghBJxaf+S&K{mtHI&2r?8se1wo#<{*rY z)Di{U%_;Quna&HG7`&xnXG>HRJNS(A$Oj4RHe+Lz!M1<<+y3%t;7awwD~3+OPJxm1 zu}=OhcN2QD$IR3pn&K&3Ch?X$dTe#I*!Vny9uCnz82e3_r9$3C`S$406tCiIYXxk` zS!cFEENbSyq3F_aqT9DwaK_hZ{#ed^M|(-X=6RaY;U<s%od$}kfqmw5y7w&QmI1&} zbJ5<}z`>c{+S9$VbW`aM;^A1ksbALCo`THDF!A7X=<Pl=94<ypiO46u4t(ano>%@l z&79e>a4kk3IFoEMQl6>cD(5C=vB9EDjH@k`4)y;%T;rZ-+B4(WgqE|8)8U{LauCt; z=F~^9d_5d|i6`CXS7!^8$uzMf844J6Z|xfk8Ru)?tasT1MV)G1ggt)Z8c8^SxKSmF zT)%kVe>_)jUi-j*l;ZykkV8^Dpp3;d1%Kn?##2}Emj&;jm!LKY9^;M;pr~>y=d(PD z)lYrz2IYqR0X>(<6I~S$Hq>jDVUJga55*Tai~BgS`fBV?-)|B5%eQTYNtDlg?y4sl z8s~pMk2-3n`yj7F(|j2sju4TI!>l+`@G)v94iQ~|<z2m?hSvcnYt%TO4`-cpJ+LX4 zTdZ6xd#qsU#zp?IC`*iJQSN>kU1;Z|%88CbpS0&gi`j^vrFdDs8yZv)=R~tZLk2#w z#Bqf~fxe_agf4>Pypf%?w?w9pOMh~)qYBPqLay1TBD@0D8#)unVX&(bJ5V!D{|D9x zc#w;R(s>|$mh9)C+vW7SaKs8`9KL!2nTfguJo8AKIxtH8l#&?Sh<h@R5ewc8NF$Aj z2JjiX3S6`CXx%1DVg^U}XGsB^c$*rAg}|rG+vtHxzc?XDWFa{O>={pN4%rNyj{<Oc zd%L9NC7q&0@EaUcOz_lNalcv(B!+`TC*ZtOh}fKdhG{w-Y1fS@co%ESGprR?)rls9 zQ8CgJkb7uWb+oQ!v?i0Q@E8Q!aH1bpB55bI;DJ}|GP{pIp4MP|k78~odIkJKTte_O zoXtp*z(F-|I)(*HzIU^LPn6NR<G=Ij)|)J@46VwC;KA`cc!MFQg0f>{V!J6c%^0|P zO=Tf?IC7vS4VpK8ui~>4B}s*wV09dlW@g-lv#CNsollzmH{cQYJC->Q!o@W%n|-CP z$*s-_y|k}UJSK<2sc!t{!(vqxakOw@Atymil;R%B3YdkAwmUcDoAhW+m50W}48#!% zmj{xYvN`4G$Peq31=OlsMg(BI<xC6CRoG^?Ng%eZt@W(!zJET<_$J7IOU^e-{qsff zG}SFt?6^9i8Y%P%S$L>H)cMeR{5Wl;nD*soD)`}PeJqB<tkyoBv{v2~F7!Ut%zadU zdai+v^L!3FU_<A%(Rod0g=`h-#ygRfC*F&ri;sUTw}v(kp;juBpnpWvGuRn68gu_h zHke5^TrQuicK(OWb@j&cz0G;0d@So!`1n;)#^0z<eSaGyH^r5V-?m)4Y`i$=cQmaL znsY<<`eL|OtR|D%0~ecmoHR(!@8xVNH6xZd5;SI1)JegPCLdqxn0$CkObMq80{kp) zCb$Aqa1YHrxgmABX8#Nrb`0@^aII+KRv9TmPY<Z-Q%m~cS1w6VH(@CG#B{R%+ZM@@ z_5CPDh2JYZU+V#WLP4w5ay^-zmCqbUD^*)1W3kMNY?EnF*>B!OOyLjhP^Op2j;+e< z?laJymB6K>Zbi;R(*HYblA;f2j*=a#83LgmvV{%`-g{ntus*ASTr#S|2%3hb8C;k0 zuSD~QeVC)Y;P$S#R%9tA%{z}%EO*{V_3iThHjwZ>`AL?;rvMpthvf7#2N3B~FJZ#b zfc~8VQ=#eNq5uOJ>MmvI=JD0r0*4K$I>x6iEJ=9s%yt@XQpSBusr#82L>*}nnaAK^ z4t;phrdC6P30BR>!Y*bq^AHOH3ki_mB(tBpK7|Y0IuqdB480L1nYTaL?$3Ek)f-HO zN+MyDvirhm4r72n1TG!;e)s&liKr|HB$eCGai(+`&LU<-MWq5Gg9hLw5jlh!2BfXP zMA^^vV-;9;@j^)j!Z4X8DyBff!2>Pteb`t}H8TFHo-bPIHI$7Lk(BJ92)mK1JniQV z?L>!EN&D$*ZfDS)qc)}SV#q?g6du57e@AjMFE?cEK#*S_Biu5MYIbgIK(m;nHMv&K zO|wU|!8fBbq_6Irnmn=PEjN^kb?Jle0yP^wF-S}O%x3g}5xTmo475d3#YY|RC-d4t zh|$m_#eo2#T$fG2*&PrXr}A`u8K|bcRR))(Y5g?1J?i+afJMwx`=_0}dg_?k`N6_a zsc05lxsGCXl}d&u5#a)AvP}IZ?nCk}kq?PaO!>GQv@Pq8qY-d`?UR9?mSHFZ3!N)+ zy6hX6<jUNQz7#>LJ-hAt$yeeZUNhkCO7w~LnZJS`Dd@k2)1l?c0*#OPPBt>ehWZjk zqTZN5Qg#8bhm$5~jrYgIrRR=~giRV0=9^P37w}ho(oC}T@Q+4@b#KibjD~nU3EB1l zJ^cGW=Csv3r^~&jxv`dK`%-oMglhxYjWkBz|7omkA67{-hjSIh8<N~(ztgRMW|(b| zk4%ObI!;_D>#^V|3~i2dh}nuy7p({5U-uke^8gC@tyuK)?})r+{Kfnq9WHG<qv~2< z*+8ir&=1H9Sh8Nei%yv4HRRRX8*P>21xa)Vmz}7U1W=d^+-&y{#(Gi?Zs{wN;l(UH zw$A!XvAED$jJ5_)I1Y8u7fQKl(^f!Q@6w;AD`)*(Bq|G`#81dAlx9n!G0-*L#VaW^ zJ;qV%leyEWESE{<g$0v}Q`McXjLqwluKA``R#&=15f$9x|Ahb_*#T0ZgoEwzl+U4v zDOQ+xD^^(f`kJTqBX~OgP<r-mI3N6uiK^%n0!R8tWm*v8rW5v4`)WFLg!js#uE)i* zwmxUzRY1wb&V<_RS)59{g&G7}UeYRl0`JPCv+(e9G4sQhb5y-3R~wsBvAGxyeA8ks zTjkn4YjwQweHYblO5oaD?`0{N9Lri9>l;Jv$5ckB9XkBZ9^f)Ihe`A`QHu_hz5t;3 z$#z8p;E1h}B@ouQn<n}rmPXZ=4fXFf4Wx;p!hkqgvtQt>*2=-AS`vq#UiGrIyP<qf z!gT?-j6eR$*H+bL(hykX^PkU4u&wV(DoxXR1U8v^bgTfIq0&`*Hia5^j`6Zf4+^<* ziF{{8jsmpjE2`3C*J<x6)o7I-?CR0ULdO0Q^5%fB3kNHa7qE%Q0khi;d65m{&&}%q z=Jsu4xQ{tln2RrDIx53(SjuIzp?<qy$yoosP-vwTskTYsT<_~5xsKx!Z%$&TLAwNo zyuntVlB6HQnNpz$Xm}7+AL^GlsoxVH+mRv-Zb#bgN%G3>!;D*2h#EEo_Dk5%_(t-J z2m9%nfhOWHvHHn))Mf>Q!68mnc=GyOPU2R_HLdHZ?P+JUP2bq|JQRlzqNF&`!>kU_ z<sZzZ&NJ6Y`+$c9Csrd0EqL)_zw&0K?dXxfga~hRCW9mtIL8K43FDQJ9Wd}N{ma`# zOC;HelE0{hXm2_nH@4fJBMII%=20gX&HpE`7EkHpAFs=H#aQc80;kW`8Z-TcQ1Z<A zs+yM1oJ^%3|M*a*8xf9KoY}9RJ`0&*wB9+<-9K&J9%}c$#!tFGdlT)B+Us5+7vCY^ zPj5$sWXHuNKIF`F@9wvXyVYt($bEF8nOL-h%#W1a>zfyjbp4C)yT8No6)kn<23R&> z^@NiKxJG;SOvm!|8Ga8i&rgRXjN$9Ph1uDprBDe13f8QV;`&&3e9gtWA4lth6=Ucu zFg_STXvc8d<UPsR#$cx98Z0{;bYC6!q)jZipG3*tldRs0H=ne;g5s{qKI}d6Yyhy- z)gH9_pp=^pHe`HVo@#A6IG{K&&bq~@SyDsR{N<`VHxO;EP<4oiy?^0Bu=adC6gkz} zd|aggT@3^bHJDil#Fpaiy~fMambY2GhX2UhFLDySZ(yWBq5xQ00)mMjO(i{!0C>0Q zufR6PmCGzbXtH2=FNGU@@9z!V<p}NRav>b@Bzk^WESYR|jH3X@f6w-Yc04O@i8EhH zU}8PHpZxyzoJ2RT>zHTe4T>#+VreAfO8SL>o=#kP1xL5Gft%lZBT<cmqDzgmlY%ew zo0UVStjmF7Z1+Vw@O?joA#`#{vD3*{g~Mi=(l|JlHA8I!=gxM2DggA-VLEa46Hx{P zb`BtsPuMd2c%|9Oww5~_urfE55Pki3=kYJN{5*$#`?pT^i-ckD1NrJb!?$Q$9r0&x zWsHy|(j2c`+`N~Z(x?xyxc`ws`zuq+;L?qUge>ka+&$Ra7_2N!Pi0b$<qG&YIH!09 z$J^fFUD4QpGG4rE`mATp`S;}aOzaVgSkO$skgJ|{EG3NpgfYXhLeYw70Y_pi8dHOn z6K^%s2@|q&nb!z6RD;rwek@gMCpk+8BucEfOqEVn&3>#_N=s)hX0m}l&1lBI0a;1# z>x*+%#0mxpV9jtAx+Qm04~EQa<|SYd&{Qse;KLu^I*ihaPOz?}n=)RSe?EHZVlu1x zIR3xl=Aln`aSM23i+4sH-r;nb$7b_z7!hAMck;y6Mx$DtEpg_EY{0KnO{o5);)|Ag z#H+<Sj)w)ByW0*-7C-#8yW{ybudVyW?mZ*V>jlS{rg$U4@DGC|2V)ZXK~U<|6P>*7 z*@n91L?e}id39xPU3Ow1#Z1C22S}>(sc$X+emF2O`s|IGyqXH}V>So;0U}FGdRxFH zqLq`avf)_^J<v!8_1N-Yd+B}o>Z8{|<n?gz3dLH{x2eD8+SEhA!CldRMKvH|6ck~= z2<}K857~Gk%_PU;Q{{TL7UC?~L|%Bx$Kj379Kiu`!bUPw_5OEvZ*;_DyF2fdTH#V7 zli_Bjkjy%kts1}@>E2Ey9Y~$)#8Rj0EE?%A%x&u|5L*nXvXT$3M&-H7HJPX?w6k`4 z5Fn~y=%(YOBAld>;Y_|iU$4HJJ$5A|FUV1?xH=On2XoVpoQy~`7Y?3YjG2vgjHc!i z4#FNUzS-@*J?&;?hXc3DgE5B#!~p{r&?XAr@84ryOdOh2WJ*WH_*Y(j_SR$94)&H8 zXIiyNA|{VpZHDAQ40*@n$*3&<@N%i&)*KsEze+fkyvfR&Rh{)q`Mv_<=M9^HmrxN5 zkpN)xbgO<a^UPERJ2AMsuu9Bwt}(N%VkZtb7|fTm$*L8Twe9Adt}m7{&d&6_4)D}` zyW)Vd*U+$`!^^5H>6jCWF~&sR|9q{Gt!3uoI=%h`o-DkM7EgqNkKN>&Squarn>xLB z4G49LQ+wHbcFs^6OSjugl#@Tb(8)G6X|ZIk4-i2;SHFM5)$c{Lhc2Njzqz-H8H^zA zDI|KI68P<qp}L2K%d&)1d*HF-AG?*5ix=W{Js!(*mhj_v%Ns*I+I+x^+@MDdl;TKy z=hpEWCB(G`M-pEgmiGL{Ne})TVwsc^8f0nZ`sx;F=dXV^jS+=1O1yfg?I?eg!BYHp zKTI=JV@y)7^495CEkxp(rIQ3Fo@lh<!NoM-U&ms=Ik=pBi8ni!g3{wVr{qTi<*2EG zIKfx11V5w1jG%e*O$M(<n(<iRoxl3d4`Eu%bS?+ujd+FuH8*!JH{xQS92VIg`4cxD zoOOA+H`q3Si8Xl&CRh=ZE({ZPo2wG>7o$c!V2c+$2nqLoJI4OTU~%64fS${hMqLxB zkZnz_;2$l0_IWoM5{`GC7Nv=${QdmhP<cP1AWvg%J~K$Cw<=+c0qk6H64SY<Ey<d0 zN5}afJPWs2zL9_Dm#3zuW^#6%y!{_>FG}k9VyS&#5X}lpvP9v8nhH72nU7g&KR$yw z;72SLuO421QVv9qQba5l(v??c*YkFHdfw3q-r3NNTp?^-?CBDb><Gf>@ZB{pV_ZdN z(I<!AUflu49RMuK)T0+<+&eBJuiP&7kaO-aHhDNQCSHNh0`HdwdrS6j3I9dD60H#7 zYPmX9c)C{eW>(CR`i~o^zALI!JaqHjYYoR+_WZe)G2cfuYVzH3C|J?Ao=)2)r?QY{ zAXuU@Nu;y2&2t8eIl;=g-idHc**SCR!bh%lwR5%NQYBiI;h4x`ae(o@G^ZwHnPQMx zEEdR#*j67_$4I<n6qa6iVd_@5pn!H^@AXR;y4rfN(u;j+mRs9yb}P@M>S7APok#yu zS6&xTdoWc5g5khlMt5VPX78i<OgLV4%2!%p#{6Mk;j|rl6wZv5c+{5}0&(~RoT^~4 zUwoaYI6oJaC7qIHq|$rz{b8wFdH+7tf<0NLcBiMhQ;pXi!_kEF$I90~0W7Oav?fuT z89Q5M$T+((vmybLjhF&}1A8U|;E|*ZI4*S1{fm4O-SmGeyEOc*>@)qIm3RNWiGTm5 z_-jZ9QxBr+3A*85E{ULXsRSMSH9#<=5oiAw&~hak000000RR91WdMf&0{{R300062 z9sn)?cK`qYi3^ti00000000&M7629i7688ibOJ&G%>%RqhXqpxJqE1@G6)|Ct_g<< zL<*`4QVb*w9}Vyhvkzhr01(*`$P#%I9uvb9SroYybrs<j!56d`Zy5m@s2Y(Q5gXhb zdmPgpfgTDTgC7|mqabA<?;*}2iX(0$P9#nw?j=Pg9wxphI4Jokttxjbb1Y^pJ}uTR zjV~rIr7&bM4>6iD2QpYQDl@S(O*G0ix;7Fvk~a1?qc{OLWjOIUnL2Je4?C4SUOfpt zt3F{q%Rb*e^FILq0RR910RR9$Im$*~4?Oh%3j^o?0002kvY7(_0002kvY7+_>HjDM z!vfU+0{{pB0ssI20001Z+8vJr%oR`^#eetwU!1b-y6<J%u6fxU8#2ab#VOmy+>q^> z)ez?V`Rv<vbnype3<OAXz@At`DOQqW0K?*<wGUZ0tXmi!pUA124%$MWcuv+Ya$&RY z?iy1VqW9J{42kt*-C>+|(IPro&oZ_!g)wo99y&#@sKaD!vu{92iC$W1--kXL&UkeO z9j25H(kgwf{-wqCi)mY^Pb)n&X3$d$s=uj&|1@@P%7}D~=DJ`%j8-wux}U7M)=U@5 zKzqq?k5RFTq0xh4%wTMIm)OJ<9ix+~X|HFVbpuQE-}MjB&j0JEp?zbzr3(6MzUvL7 zS2|1&c|QN^r3ZA;1bX<sW$j!St*hyh&eK^}>8b|KTm0__L-dMH`eXmfxNSUPP`t+P znU%hHrrnT=z-p)gcA!Oe0001Z+GAj3U|_oXUxOivrR@L9{{}3Ffg-3N6#%Fh2N(c& z+GAj1VrJUIz{<eD)CHs&7<wQy;{ygmMkWR%(7?d(UV!1vTNpne%{7HVkwM}8e`baM z77S5LR~c?7FfjDTF#~lUW?BJMqQJli01p`v=l}qC+Dyu|5duII1kh!cp$Rmi585E3 z4Qe35-8G~D5}XWgfE*`V=5RUr`T+3#9kARZ&<CHD16DNe*$dCTvfZN}K@=Hfk{RY% zWQBFMIN^+I9(dUqV}QH!A#it*KUZUu?Ymn&-SrMPre``Psk$mEsi3^F$|$XfLb|Q< zPyU}AfmEO?hy;vmKn!Cu0SP8_1t2dH04%I*>>Qk2+&sK|`~reP!Xlz#;u4Zl(lW9T zc{zCnMI~hwRW)@DO)YI5T|IpRLnC7oQ!{f0ia;<BJ0lBNkYx-2C7(hq0001Z+N@RO zn&Ze0Er*(67<@sVn)L0Pls(LB$I0+CexGa6Zu-5v`wV^>Im`d{(e}tmbN6yXRHaf$ zM@LeHl_6HENo<K;{iLQBUvXpky@YSNJa5x2%2yMvG_#*a)u>uu*K4llv9hcp-RUbw zMQP}>W0=w{pDTtoh<{n&+T45nd1pL|*W;!46Hj|?o)9lABpyGtF7aJi-L;mAfl;=> z^H2?U#8>6~DlFkFA%Ms;;_-zfg@)v8TvoSbbvt!aYpu&p+gi<PA=$Ln=c*z2sWmeI zsz(b6*LBDZ9Rj{(C-u2zG;q;IQC|y5P6ol{lPhVw&efI&eMC9s@LgQ3H<8w2k}kN} zl1;S5>C@!|M=p}uj_h;Y@JQ6|tIB|y2DUoX#7c)5SJt-JSx0=Xw|pKkBp@fE^`B~L zjl=^#O|6)uof71z>5ojPC=OfR4%uU7Kl$UO7vRv)Bjc3fTxYZ~WJ<ZhCX)-0+bFr( z)LCZ`V&a(Vd>OYCZXI=L(v%7HC&sIB;<?(hEzjpEQ&cK3Z)6>xrwt5=crtn^*Rjx{ z<tf=MVK;@H&ogi{TT((`U>$LJI!Y-|iKjuD&$H&$N>bD|I`%S8ZR&4)K4D%hCa;zU zO4oz_RH;8{3N;h0CdJH5#7-9SOk0{1ts54T@-c-EcFt--9t%k!<RRNI&#4UC@-(h( zuie2puHe7aSY*-z{vNdZOLa^E3#CqJuoba-+;<$Otko%_3RQ_$5}wf^#XO-y9@pr% zkWz&Ies<PT(<%(}v^d#l^OtS+5@<S&+)lN9K4S_;<XP}6@|-EEBF~$mCh~$Q>LM?i zq9M{X#fZp%nPODrzfCbF^8bbcc0TmERW3gDxm_+k^Z7qU@l^XSVEivI{s+DZ7)5%( zDDo08iqya;@-i@rYyzXmIbam|KVTF&4~!y>p@(Iw`37NUQ;IaUk(2|Hjr3b!*l)9s zNxEW)dUQ-Bbaq$EGxnPnXeFPo?v%$l%U4=O-8mB{S8bW#wUU`*kFOiLRpPw?J10II z1QUkfN9)zu@09#>9_zcsb>|E+x{3U$cMM)uQFhnoTP8U7(C6E~XUa~r4)Z%Gi8|Y) zfO@R7boPrp@9Cb-C|N`P>|mmAJI<NYh=11r6$S)<r3sHk?ae&UL=SWLy0?2w!N3RC zbSPHDsm%M+#pK%xRmuIfGFQE5LwN&7Bg%DYt-F{$RPz8@0vm(Jz7nMynyXQ^v5;Vs zxmc(21|7VUX+r2Z-9-&GB6N`#L>VQeqk?FOqH&T!avcp-Kjal2C8K5u0-p<mS8g{J zN`7BBAZY4yLk{#qQ1d|4$W@0p>*}5alWIK_eVM?JQMHl;^iX>)G}}rz+oinG#O}r3 zZ{I<oj_TlHWwf;MqalK5yELhMJs(VQyOfWOra)Nh;$ePhgCcNFV*sD*s2BFCpYFFl z(&m}ryX_;LhKBF8a|D)V0oubhC`-TrOrxC+1G_sdsL|?yzB_c+!J~mU{4ca(@1N>E zfuG$4ER82*PrW@m#25=1+xd4Te@_ny(0BKz+(XJ|2E*A`GKZ%EzKL0UevJNwDU@^i z6yL)7rQtgeuY|Q2yizv@ieG!I2jmC>Ab_GV?vY|T7l`e(%~{*W8eDMukBYCW{+Rw| z|ApJp{8L86*7uAesyGBOu@4VGTyY2zG#`p24M@>|G!4kmyeP6XAV&l8G@wB9rYO>Y z5)CNRfC|m0qDlj5G@woc8Z@tpCJkuOfHn;<#JmkpbBDovRCQfFsd}6lhG!l6V%~-I zfWfVY$gM}nO~+#1hv|gDO{d6BXUI+GVm^fFg27Ff$W2$sP1oXFo*V@~Pp{YJg<&3F z#dpCs<z^2>c1>n@+GFtE!EhodVk0ACU*rbHog54t49Nu>8Q7dQ>q|2+Xn|N;l{ok~ zm^dJ89X1IzFq^|^H^%|y0}M<IoIoCz({2U_AO!Q68C*81Fm?n+Y;fp^*vOpXvQZ7h z+r`nqz{ujzksJ~MQW6OSTU9s&IGA{}b};^L-N4ejkqM}iHN^!026-oF0001Z+GD^4 znt{-S!Gvia0|V1>#y$Uk{5N9~1+w@5e-Gpz0rGW$e1y7nK)Icahk@dDFm*uw5tu#z zP|6(10001Z+DyzL2*OYlh2j6+yEkyMvB4a;ZZV3z*lf#U6MOH?-bFClG1`MY*?Y1X zJmH=6al#J_)?oZMfoxPvvNPu(2P@8U!GepNsJY5z_Sd%IAvcWnk=v}_QQ4QkiWzY8 zkd5B&vNQNo4)*?*3)Zoe6JzY;GW%;k#Zhk9rkC7i{f<8UTBWM()?pyH&;E*iyW}Wv zxy%?Sw{JbFt;P!2K9)ise&6p{E;Aj|YBD>skblcbI>~f9viv#t!nI1}Z>d(PR(M4j z-Hv%b&?*j;&zo@|yu0M;kDUC6w<8(zVjx4+pKQwmb#mH}Hd;!0A#YmQ)4^I2+qEQX zE3SzOPw_RWl(P!bgW!?Mj80;%h>G~}NU{Ed)$-?=(P$+4(Cf&tfcxB+7j<xv*aajD zq)7I?k+u=<DWkHX3OR3zo3gKNv?Dz4AkwDzZ9h4;Bbg}%fL2s&kTr-ym9dQ!?{C(* zm8!TP*9(HrmmOC{RY2@Im*r6Eo;>R*c7;e@Tij)7r@YA8bgEo7L8en{gwegc;&N-P z{`fD~Bc^3aD5VKyq^V62Sz`K30e!)@MipFtspO(69;OviWE?lfX&+1tB*s^f4`kED zg1l#ytMuba`46ydaHLaR1WQr~#Xj=45g#Bvp;Kxgw%{#6y9WmRaF&QQa@K&xWZgG| zr*clh)jdW0pi=2d>!#AnnMYt{`9z#I!%Mj5$15l6zhbtz&J2y*C8&Q<UkBeY&PDHY z@cFnM+;;3<u;eYyO=q7xFtw|TX)o`~&1pyqe7@QgQ<EgJ)g#*x(D8sq&@#93_7R;U z8iAR){DM(zMS*25yatFv=gv-3x$I4<<J+P%=iXAT-%`Nb@9nrs!ac6C2Yne;4e@B; zlFsl3c4H#aIWE#}9s`>q54gpZA}-@HP{Y=6wupsu?_JHfOfBTs-;(ptzxY2mTVvsP z+HAqq0fAuv0KoHGMYm7ao9=FpCNVe*xDx^2kKms-Vj^(~NhxU=Svh$HMI~hwRY6T% zLsLszM^{hZz|hFp#MI2(!qUpx#@5c>!O_Xt#nsK-!_(_W5L_ET2mk>9yS%%*E4DBN z1ubKe=;%hHr*B|rWNcy@wVAnvrIodft)0C?l#Wi$QMtIfxqEnedHeYK`AeiSxuUIk z8j>xDp=eb{%lV#v+nz?%xovCpFM7c>&S7H(hldx3<c^mmC*MX7oIdkXD4zU7@($z+ z(vq{EriY@g(7OTj-UH!9V>lMJnxwu{>0;nahcdj`1ais24a87eGv`giX%S{uW3m9N zOOTzN+L@a6ZqE_%3JrW@V_ezTmC}CelDgC>r+(-WwuvL^BS)Ub$?aaBIel|`dmZ&n S)VDhlcw(sl0RRF2{{RnpDA*?e diff --git a/docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 b/docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 deleted file mode 100644 index 83b49962aa8353a7eb7cdea57fe843af4dc06f88..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19720 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 bXssLhNj(c?!93G5;NEIzo9EzaliuzI?ubzb diff --git a/docs/katex/fonts/KaTeX_Math-Italic.ttf b/docs/katex/fonts/KaTeX_Math-Italic.ttf deleted file mode 100644 index 9c38359cca652bf7fc7e7f9581df11e3267a375a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40992 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 AJpcdz diff --git a/docs/katex/fonts/KaTeX_Math-Italic.woff b/docs/katex/fonts/KaTeX_Math-Italic.woff deleted file mode 100644 index 959746ef56fb302f4f8381cd199acb54ab7db0ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22844 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 Ocw(sl0RRF2{{RmnPtGy` diff --git a/docs/katex/fonts/KaTeX_Math-Italic.woff2 b/docs/katex/fonts/KaTeX_Math-Italic.woff2 deleted file mode 100644 index e3ea522a6a2da7b5bfcde8aa4cc4825593e3f857..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20096 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 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Bold.ttf b/docs/katex/fonts/KaTeX_SansSerif-Bold.ttf deleted file mode 100644 index ff108512453a58cdbfa2549249ee5f2562640f08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33688 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 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Bold.woff b/docs/katex/fonts/KaTeX_SansSerif-Bold.woff deleted file mode 100644 index f0d6ea739b8405ce37eab96c71feb4b0091503f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18516 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 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 b/docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 deleted file mode 100644 index 4cf8f146967e1243ebfd1eab7ff9c596be939f2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15732 zcmV-)J&VG3Pew8T0RR9106la74gdfE0E83(06iH10RR9100000000000000000000 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 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Italic.ttf b/docs/katex/fonts/KaTeX_SansSerif-Italic.ttf deleted file mode 100644 index 3dd767131a57981bb2e065f4a34010ff3f3ad45f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30960 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 W^w{Xw*!uc)w0<2USaW!<9se6ZpCuvy diff --git a/docs/katex/fonts/KaTeX_SansSerif-Italic.woff b/docs/katex/fonts/KaTeX_SansSerif-Italic.woff deleted file mode 100644 index 9da0dfe39632bb169458e6ed72c84803a6c058c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17572 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 Z)y>TXO1na7HvshXLi7Lu00IC101r~0ithjb diff --git a/docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 b/docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 deleted file mode 100644 index ce19ae03d50fade531801d77634f35ed06f90681..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15024 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 G0000sx++@$ diff --git a/docs/katex/fonts/KaTeX_SansSerif-Regular.ttf b/docs/katex/fonts/KaTeX_SansSerif-Regular.ttf deleted file mode 100644 index f117cd619e99bf1d030446c40c725a1e79c57b71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29812 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 X9Hqzl2L}4G*Z%BvfN;&>#diKLsZO8M diff --git a/docs/katex/fonts/KaTeX_SansSerif-Regular.woff b/docs/katex/fonts/KaTeX_SansSerif-Regular.woff deleted file mode 100644 index 6ed98780a7a46c0c459d2f455fff7ee01954d3f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16228 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%0AmJ9j#Q5RPd_e^lGxNtoUKL<R4&P{=H(Q<2H zi#&|%>~XB5B#oqTlN5~XRAg~f#0gY{39KB<JnV5i4Y?f56nrt9gd_=^xg03}CKo3s Y!6e!R&Da4C4}uRrdnusrZvevo2MbI9v;Y7A diff --git a/docs/katex/fonts/KaTeX_SansSerif-Regular.woff2 b/docs/katex/fonts/KaTeX_SansSerif-Regular.woff2 deleted file mode 100644 index 27611491a1fcae82b54021dcddf3de913573b065..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13708 zcmV;7HFL^$Pew8T0RR9105yyN4gdfE0CeC005viI0RR9100000000000000000000 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 diff --git a/docs/katex/fonts/KaTeX_Script-Regular.ttf b/docs/katex/fonts/KaTeX_Script-Regular.ttf deleted file mode 100644 index e6f34542e296e006bd7f5b313ec59b1e42f12d8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24620 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 diff --git a/docs/katex/fonts/KaTeX_Script-Regular.woff b/docs/katex/fonts/KaTeX_Script-Regular.woff deleted file mode 100644 index 4a48e65f0de679fce0ca17c32f8b52bd3de33fca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13428 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 diff --git a/docs/katex/fonts/KaTeX_Script-Regular.woff2 b/docs/katex/fonts/KaTeX_Script-Regular.woff2 deleted file mode 100644 index b0aed195ca3be06a66c66919dfd66564882bdd81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12064 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 KZqM2T0002O@mFpD diff --git a/docs/katex/fonts/KaTeX_Size1-Regular.ttf b/docs/katex/fonts/KaTeX_Size1-Regular.ttf deleted file mode 100644 index 37faa0f9fe41ddb1c9a15725f8ad4856599193a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12916 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 diff --git a/docs/katex/fonts/KaTeX_Size1-Regular.woff b/docs/katex/fonts/KaTeX_Size1-Regular.woff deleted file mode 100644 index 0832f7a468852ced3080993f4a826e8ef608befe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6696 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 diff --git a/docs/katex/fonts/KaTeX_Size1-Regular.woff2 b/docs/katex/fonts/KaTeX_Size1-Regular.woff2 deleted file mode 100644 index 483e7b66e0f49b65dcc2110d44886220bb2c3e51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5592 zcmV;}6({O<Pew8T0RR9102SB(4gdfE05YHe02P4%0RR9100000000000000000000 z00006U;u;~2n-3A7ZC^wu~?=S0X7081A$BnXaEEt1&wD1gB%Qh8-gn%5w?T0k`Ylk zxf%TB@y2lEq{%9YGW@!hwf>3Nf5!6pP%fLTiz2g~4m^K^#y-jYgSPeW?Jf&Tl!8$U z7+461icOn$_MLG7Mq<@vdKo`kFFL<7=l#f#ARD&1{a#eJrMkBbS0%z=L3kX_;ZVe5 zRP_i4@c(G*XGIB~=7tBpGl+fBtf7*inYE&(sMA#^gZDqcIPCntFC3QpSIDcvdz~RQ zk?TxIQkT=rp?@c`BZR6+5}r)-aIaXUTORYu^yWTt+f?jOR8)y$)5HJiEZaXL>+O1i zz98zL3-(NPsx-}bWGNbr%?GlayqzVMeWU}j1NMf9JNpQ|8^)qT=(tZ&Ls6!@cSV(= zE7O!Ii+l{L-}W;Jb+?G;ZX~7W)wXb(Ak9)pfqVSA6Mq&!#4_9n96&r1CjemN_o49v z42gi}{ucrKAcb$ggEvro@2m$4_IHxhq*-;Q6lbvj8)lKbQ?n2Nxf8))2(T;g^w&%I z6F?dkB@ioGf|=%;?|I@9@q{E<rD~IEi|VfGzFMXZRBO}`YMuJLMxeP9^;HZJL&eZP zOil{0v=Y>)I3S@)c8gV?(eSqTG6vwkKeOBukG*Y!^}PS({Ac`6^ua$*7e8I})c(}= zlshpw@x<>FKTWtNf}d2rZ+#T|DCSYLG!f8S6Xrou5aXZ-K$YBx%3V;jp6kvnGHiDW zpTJJ@??bSS0vty|Cnu`Ir?jXcPC!K*C+X=w5Z^w9ete3Uc4*VQQmKd#+WqGR1Mj)b zocBN#V7E~Zh&ePMV4GC~0WHLtS5W>_uXV^io{!&rj^4a0m7@4n^jMM;W}wfUCROU@ zZh>}WfYgWewq5|vc(hVk)AfMQvtqL>HXp#rketj3Ne9~F7@%28zT5^98VgFhh2Zd{ zkCXsiM(DgxVADXsym>7u=M}^dv4Z_LfO~I-OuX(;g$!?W-KxIZ3(CKa`IYP5q-e>N zwY-g+?YuJ_SX=`KaCxH#B1S-4+$c=|bafD~Rpsw}COAn07qYg7fb)M;@Dc#7B`4ZU z9&MM!L(d%p$Kkq<Ixq`nA<9>lMKgU#P?|L^nx_^COG8jn2Vr+u(liC6S~t>lUIcPd zgfC+#wFe>d>}~1=BhneYGoP;e1`#UJa#G)o8pHq_2_c{C!kr!o2@!}&k%*dmB@ois zELZ=i;9WFN#LOygacTssCdJctu~MH=x;VS}*M3+srR{Zm`u37?eph_PmZO5?_}-GR zLhu=oShW^FXsE&~68XWU2~uC#Srn5{qg+`d{n^CZya`1e8xUct-@OSTr-))$Et0b{ zlgU+9fD<+X31s!?@b+Q*V67s~?;;Wj8Y7SZUjTPd2A{vBi*+z_3&maKy#e&!a2Ha8 zFDm%lXi#vBpu`0DVG{f?1p$}?ftZFM%!OdggAmMkqoEcos}ci0B^Lfq=uoUtGgna< z3Pmj2f(jF$#w2Jk1>u+j5txQZ%!Me-11)B(xCndDKzx2i+GuEfG0Y3)Y2w=MN23Gr zMtstc0BNd%v{<)g<Jp;KbevCTLZ7Ea<C27=a-3ouZzWF?h6I(SYc)dm-R6n@OG$Ta zE@)PiCbxj{7rgy=MhCW4jgDS#FhP5oy6pgV_yIK}p*^$Lkjz6lb%f{qDX2uSyrj0d zfTqE_9+x#!BR!qs%#Dx3k<ceQs5p7O!3;nNrKe6?9fmiSMJLi;G20H1I$o`+7AOhK zu5b@~zvXrONt`#kI@aSi+ov!U_VTEr5pryjVMz8VJ5NWEQNISkyf8?1i^Cz<flD$! zi;51bGYQJuq}y@^j}r*}>c%cLYj4|zn}cUA;b=#TD|exMU_z}4YSt&a`Ak~rfwU|r zRVQ$KDjeI}8Y}60fhR%IglT0(vopxLCq`nI1?92To_Pm>UPW+XpQ2T)c)SQlGFGyY zjRM(H&#_EL;>?kq=jo!pnmO&sf~ifOGkFsgZ)&T~ksITN>;t_T!Xi|f_1>)m%qHDy z8<1*+Ran(rS~2hPpxC0DN!S|=d!rVmX-^Q_k}Dm9$~!LTP?PJ$v<aCXY?`_2o^3f_ z_6a_@YCEH1rUAPqcstP9BEF-XsG^sqdl-(^3BA#j9Fp9wyg>!*rbsHy`2$kEP_Q&m zj!ms5Me|Hox^PgJDGGXdQwBXgfTN+Q7@>$Dpkj<r5(03dQjAbW5KuWrs0aaA(KL)O zogkn}j8GK<u%l{>P(u*V42&={1mHrmFv4ttfZQ0t69TZIT8vOf5KukF&W5<rw0PT% ze^aAbo6>}`p*ao84YEK>B3%et1L@EfNEhBMdqR6d26O~6pfivGU4abfZpeh5KqmAC zGNCV!IZc1ueug4eQA@GKj`A8t$?)Q#rDav|fbd@p004<UsBtk4z<mH<p5SdiQh*mg z7y=LT6M!hb5yXj;@!&B;;^@rm$CIUel~P{Xwxq~UOGUpz5fc*;X$}cMifeC8GHF<3 zf{qTGR?Y?rSOK5mvSN``tcwn&HX{>o1k^o=a0$h#5n~G^$%2*P?>m8t`T|)MNT<+o zUuud{rX0q3?R$q43(PZOSZckT#$bS2VeqLOI4pgE!^XO)ef~Y-RtVzOiwM%Ai^*#` zM+z22!UON`V6^&_<F3!cAu7IvSV0BjwH>a;J26gNf-wb>9Lw68_>QdWdrlCVPukti z0!dfrqb&HICpa0EPg>n6lpv=T5OqP?q!AJ%N33f*U;CI&q2jL3-2r<$IHAVc0T*M= ziphK0>yjObQp8beS)u<zg!*xxoqj@W|E6t?Vd+0}JWlBGw{DNJoX#(gS|E23k|EuD z$M`#=p;-&UuIsRuFPC#zEYDS%<5DbV1*dsP0V^UFpb}7@A9BB-gahug4PZvYp_wjl zTMJJ2q98p(TC4!5#12G;?3+krq|7PE?3{3_lkN{4KRAV#FbKJi-B}LEwz1w6NuOW+ zOm7D5X_|-MZVoXhL+rcomgQg?xG1(ELhv<!j-6^tUH&SrRIWBPsQiB?=eSBGTzz;o zMZJ#{Z{LY_lr^ybQ%e>~oN)h+k6jnMF6%h=SC%JwzICyRBBmB3c}`z)hm&?-X;#gM zK`bhe>3ItEkLS3A+Fy358B~aDQPx&CZ;;{;#dYS%ZXkOaLx{B0;)L()axnJ9>Hsfk zXP?C3<Bx&drT0M9$2raw+ky1ZRhfwY3c6+k552h!>EUajQg)z!59Rg^Tk6t!`EQbY zHpX38+PYzz)4|7ov3Azm>ikx&{sr$7LiWnT)j+n*gfZI)G1<CJ>)PsD(Hw;|G3ZzZ zWIhhMBTp5*88CB&t3bj}`cN^=wO;bIIB92VVQ9<3k#3(x_36*ZsyS*eIqzHF@P4D0 zIWLLGstz_HO15TtfhbRhs|1}=6F0lD!&9V8M&%)CvB640<5hLYijRw2_~xxlkg0P9 zLKh9D(L7q)dxi1Sn5zb~gmJQ%bsf{Ai-<7UQ8x<fW3JU&qKsgWMsR$wzMq{;PMtcf z2J9M9y{;M!?!I4(Ew#0ptX7F}Vu~3vrq@?^=k!AtkRr?Qu1H5k;e{&p{!=A1r$~>~ zvqn-kb8s(XypPw;R~H|r^FW@{7o^!?(peZYlFEz}v7o{nKdEm@WtxYN^Kc0-2i*Ie zm}d{Y=#%vEV=cX?Ax5m5<#VRaJ(?);@y=4F?uF4KA#`VZ(r0i%^7y)H0B!)u9o}Ag z0Kjy6WxfK0fFx!Y(tB76A=1p9zdF1#T>NU__LV$LWgj6Sm-@-?MihX6F7R8gvA11O zXE2T#c{~2TI7i>*4z3`n-S6$b!S^ojrpSt5g{pPX`oAv^Cv>|-qF%1=?sv`)4O6+> zm)9ffOlu1Dam^JlH~2(g`66?T2pA51b;f&Vv^!_-)Y6QTe}fU#m~V&R-io+wG8*e* zv}b5P9rTlF6(VuICphz?E_mKQ3R?Dnt2^Iq*`Fj7nGS6E?v4&2O?h4%<qt2+|HR!M zqCkSs(}cJ}Ebl&E5dYSc=l5`?e_v7HzyGu9u#mt4&YtI|yj2i?yjxDwpOjcQ9FZRQ zP<_GwmX~s(M<(w%!OKqF<?~%1W%^%5T5yF}gc|4liNs3I?me7>z>w;&&-T}ZW^4FJ z8D@s+J*DW8NoeI>n(oKN3kk&NfZQk?nW9o`FIzmr1UB9I<Yz6$ERiLNe#35I(rt-5 zPrgl%uUs9s8Z%_&E4trEwD5`7eVq1HdD#8hjn%Q$`)GZ<oT<631^vqtta1B2;e~nE z|BU_f`c8XKyFbplB$iH@4PU%yF4u=a&vPwIHW$74Z1u{oFT}EowmMZ7(+)UzD0bWN zpW-aeoTIJBx5avQ*peLV)<taWg|An(o73=(mu;RyaBzpk<W!w5q3A{Bznz1%CD8_U z&%Odutj*Gjo#fw9o-JeW9?~2e`L}ZeYg6~CR-sBOm6CNg7#-VEyZfIho$-z8jn}p9 zyW96FeDziCX`j?K(03)PP;Ebfl}T;y;=_p#TgZ<(!^~CBozZ@6eq;PKcwJS(B{g_> zyvAGO#iRcp`JXSP=|8dIyT<Pz>_-*QDwL{dr7Lx4kKMKw8k?iuf4%?UKOqe6DoCR@ z)n&0$(TzH}Ovh(bv2>a}%brHZs+jwP31)v(GWmPUE8P@x!Z&m-^lVi4hT$JSpDXhZ zO2oHBCnbvmu9^Hd<mfg$BtGPP{6wcbF7V&mRL}WBpo~jAA*^SXi;hc{_`J16=<?p= z?hVbII1r?yRZkcQ+6O0wQzlT#-*4Qq$M7G&4Ia;4w)uNq&y}Bm-}!&V=e1Vj1+Yz( zxr1ueU_fapYyE`OGZd3QpE5UjEVGf@Kr|~jf=WGo2!C4Xsjs(~{}}#iLPTKWct)W~ zmteQiWDsS)6c`)ln(U>?Ku(TiB%DM4KGT%PcmL_rETaiOUvjJb<pAYbpBHWveO~lg z03{ogE6>U`4b2H78)bExX=mx=^YSLS@}5SvB{JikRzu43(Zm19&no4EGAdw^F4O#J z^0}iBzgC}meCu1`&80tR`!IlB^ZpM(VIztR3s<n@&^rr+76zDVSNt_kfHS9}k{RM! zG87|Wq3?O|owi_6$#8l3p#ACE!g{_X7;JgxC%dE}Z-(2!IuKVb9G)qZ`3#nq4~t5J z#WJ^o4J?OdhAS%b^K1Qv>s#vlI^iccOxy7@S?eF#+bd_RmOtLpu0$xyzpbS|HLcWI zmP?zfW2<SuvQi%zcR}dFFhWx`u1qGXQ7%?52;pt?{F=%(n!0b1`Hbe1u2LhCalScp z@3l(xR7qF7vd`!AdQ(fpRKZMsRK2P!!o6veJ7T)pU0bf^a`UDLcartBs)|UZKg18c z`TC<zcFPvUD*IL_PhXU6WE_liRaNbb<`KW&qEn9bhrBuJJ9qXsTK28b@GBbi8;&>l z=IZ|L(lI2ZO-z+#t5<Q5>hpH)<5~Lps5tHxZ%L|fe@s=;P;PbhhN4pDdONj0!7W>) zS8hp6rX51ReD%_)>Elb5F2%5<XS~Yx(e^(K@r^htg?)^J`OxIu@#`8?e=l<^u$Q7< z!7R}|mXjmFWA70f^o9@ZT#4h7dic;^yc+o7P-<3QYhQ(Y$>zMQ)ZwzZLH^a-EGemJ zM0~AOTpLfMrKVW6RrB;4$KP~p8mw15uZZ8aOB(u);>eL0ycoqun5tcImxlVcO5U!G zhDER$PRid)1fMKDNK**JCU(D+#7B`41_=V;6NE%1LD3QsF{DBkO*qJbB?2TuRAeL= zNhl?xUQ{~@a|IV<dp3}Z>FiKpa(O)m!aaQb%@~n|OXu}AyY(bi3?zUcVk}pqKX+23 z&o<Hd4%;$};XX(TRV+elc5+gJE?U*coY7^4_Do~%!GaVuZY)7;)9jn@loIf~iaxp? z&X#2S#uJcV0sy?fZx`dO_P-ndzsT_?O96m~05hpfRSDJE^{&Rrf#RHk-4p!{yC7b| z-_7_AJL#O7ut7?C0j~9Xl#v51E*mR}#pIuH96<2jjeR~O`G~|G=-MH}htyG{kWw?t zf;sagbfzh=X*IhE-`7#1wLa;hN~_CM{924F``g7HG;>cJJtaFG%%{zB;{e<s)C=6$ zsC&Q(0<jpWK>%!J2UJbUB&GsN43UNeN;Cr342`5wr%`;Y&^SbPmD6fXloH`pO)M~t zmrKnkGK+*@mO0}^{KeOhGPY?1alECGTya#RM8>4X5txZ6r+rNnjbyeaX3D)%F7>f& zzJK$Ou8Y35-Hx|!jqsxlJEh1sSQVW_45q2fY(cZ9<4(5)1)lPT%Gvh|iW#0w#_ozl z>DTOOYG92zJ)6dkjKzngWQOhXebMXmIuqJ+j|*K^o5Am5Y%;giGsKp={8`a!Fc#o# zqo^l{ERv4n^B~Qenb^vh-T3_Yb2>hsHKuEsUt?(UUgu+kxn+5U&qmNE8Sh1Au03hc z5q(h;BEh~xvE|vOwwAD-zBImltMS7h^c7x9wd+{qDje|+fnBFA!+Nx6(yR|lt4=hQ zk)B?vcmX%S<Sd5<y>obnm@QM6Py?@Hy>VE1?)B5*8`#Fcgv`9aD?H%Hn)RrM?smfp zBviilO0$(|F{j(0R6|--KQE6SE}c5`^?8pj8cYct^6Qd@g&HD0K08M9X`<+%Lg~!u z>Dm{hiP8%yE@p7K41=w1opzp-ZPv>9kQ~|iKO{>u`Na&!+D2D+q6HD~ILHlZ!6#Va zL@!-@fHl=)4{BW2R37bT>TuX4#Q8dJRw{b|c$t72`QU|mV(dun<)V&0)C=`N{h}U+ zV3COikZhj9<QD0gv!H>>LLi7Me%N4OZ3AIR^W7|ufhbxomryNlIZKW37|vOhh<f{^ z;U5#tyeshplIvA>d{pLrS?EwCyktM|6BKbNoSPvuPIa;=qj(}C`!hm7NG?k@i&@-> zfcIe>Nu-d2G;)!L4D3ce?1EQNfI`@dA{3(pr6@x=`k*f=P>Fu%j{z8nK^Tl77>Z%Y zq6*dA{*D&C-m33EeWKpT^tE{F8{Qw4DHvyZR<5U~r*p8si*eb9B1Gb3t#=p~=1i^6 zJl$x}_ct*ns!6I{bp)K7*!8MGH7bc)CF;#;s%j0`Hgf2<N^zQ%)UB*~y=qX6stG6W z<aD_ls&jQND0qb{rIlld>#IhQ?=bu07|@3?fEf3{yt^F*04A<Zg5xC|SCv$bT2@tx mP>&-$$9RhXvm>{zyi%9nq1t00oE^c*My(qomR(N00{{S>s=fvQ diff --git a/docs/katex/fonts/KaTeX_Size2-Regular.ttf b/docs/katex/fonts/KaTeX_Size2-Regular.ttf deleted file mode 100644 index cf326236c0e940c533606031140a116d7ab707ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12172 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 ND0_ALP~7f1{s)AlT$TU; diff --git a/docs/katex/fonts/KaTeX_Size2-Regular.woff b/docs/katex/fonts/KaTeX_Size2-Regular.woff deleted file mode 100644 index 14f6485abb4e1483c0adf14e93c7d838efdcebfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6436 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 diff --git a/docs/katex/fonts/KaTeX_Size2-Regular.woff2 b/docs/katex/fonts/KaTeX_Size2-Regular.woff2 deleted file mode 100644 index 5ff7060676d81f040dafd77ac0ac9e68eef20767..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5392 zcmV+r74PbIPew8T0RR9102L4b4gdfE057Nj02H_Y0RR9100000000000000000000 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 diff --git a/docs/katex/fonts/KaTeX_Size3-Regular.ttf b/docs/katex/fonts/KaTeX_Size3-Regular.ttf deleted file mode 100644 index ff7e2b90106baf6920ef84d26890d21617a4730a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8120 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 Vqdh&{L<fu(V2^1R#}`xg{{euBHq-zB diff --git a/docs/katex/fonts/KaTeX_Size3-Regular.woff b/docs/katex/fonts/KaTeX_Size3-Regular.woff deleted file mode 100644 index d3626cef39b774137c67500ed0bdf3fc93391fdc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4568 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 diff --git a/docs/katex/fonts/KaTeX_Size3-Regular.woff2 b/docs/katex/fonts/KaTeX_Size3-Regular.woff2 deleted file mode 100644 index e45ca49db8c66ca43ce41bd15a219db59b0c9350..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3728 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 diff --git a/docs/katex/fonts/KaTeX_Size4-Regular.ttf b/docs/katex/fonts/KaTeX_Size4-Regular.ttf deleted file mode 100644 index 3034091cdb7b1c98799073e1770eb57c24747d70..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11016 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 be%&un-LKZRHeD~MJ}%HU#E9apa_0X72#Z!Y diff --git a/docs/katex/fonts/KaTeX_Size4-Regular.woff b/docs/katex/fonts/KaTeX_Size4-Regular.woff deleted file mode 100644 index 93c57a6f97f529f7cbf7ff49678c423a54aa34c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6184 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 A?f?J) diff --git a/docs/katex/fonts/KaTeX_Size4-Regular.woff2 b/docs/katex/fonts/KaTeX_Size4-Regular.woff2 deleted file mode 100644 index 53b65afcff022dc8512e61cb25f7075715de8f7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5028 zcmV;V6I<+ePew8T0RR91028DD4gdfE04qQM0259C0RR9100000000000000000000 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 diff --git a/docs/katex/fonts/KaTeX_Typewriter-Regular.ttf b/docs/katex/fonts/KaTeX_Typewriter-Regular.ttf deleted file mode 100644 index 2fd85294ab68105c5ae44fd65332fce36c49f8cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35924 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%0AnSGBJ}5nw;K}{-J%^|HlSG_ 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 E1ndY_VgLXD diff --git a/docs/katex/fonts/KaTeX_Typewriter-Regular.woff b/docs/katex/fonts/KaTeX_Typewriter-Regular.woff deleted file mode 100644 index e90fa2bc7ff9e2f81e57a567be2a8620ef9c8d27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20260 zcmY(JQ<Nr6u&%$hZQHhOOxw0?+qP{?+jjS~ZClf}_x$JVn;mOCl@+nxjHs)ul)JpR zH~<Ln&*Vt~@c*?jD*xaA@BROpyfOnb03c%VFRJ_x12I|6Tw_~9hkvnd004;YANQ8L zSck@LE`$Jp^y@zj!#}uz`QUZU9L#M20MWky09Z5t0F%Ckx_N7E==`s*JnlaY<o|+b zZsTe8FOMhyAkYQ?IIu(i<)5-JH8cSLgem`N*#84L(`CZqU+`aD$$vh<KS;qSfwC-Y zT|EBj4F372001y5lm`K<jlJ<dozTA;K(PN2olyR4Yv}Q>uHffC9pQffCBsVC8QPix z05bn-01p8G5G1j3NNEoC&MpAJuYcbR#Q2ZKm5FX72Pe~ieWm}^0r>qRDk<i3!eb-H z3(N;cV&jE3crr;1#z;E}tkq@=eH2lfh^GO~!V8Ro>S*Dz6v8rUSNF!$%*@QMZj6ED zKNH!uH!E$?^{V_H+0OMH?RxM(`{8AQEM^xzlW2&CM3(dxC=a+*s!qNr!Om4_`QnOi z-dywFXV|#4qxx8c96;O^hf0xn7}C@p3NU~GDzJtm@=pgQdc_`G8!9$o&Xhkn4T3&2 zq}jf559&t=dXVm!uP6`hfAMHhx^evw(47fYj(J!zN^)*Z%gmZFEuUyl&kd`5Ht(67 z7d_C;@9-C(H+5cAUjJkrJmYEy<OdQ+&{q|n8#<DwBdhpA0X<P+BHkM=O|j}uuv~20 zzOfH<B-q<5UB<rerHag^o|J)7WSx|<#X8(wr*9wrs!MpeTGgL~{)Mt`&5@J7O%gH> zD|~DIO74a0%lMvR=+qm*BveqW{bDRa!P9j^t=H?%X&z)p_=OK1vepHqjR@!-qZ;Mx zxU|3|I?MtK+s7*Ytgjp;!3x<=EN0Os@8qE6bv^Aczwx<MCipxfLEv||xun0&s|{_L zyUD91^K4){vr7;vU%h$hTn-EIox5>)2YFkB7m5GaIjUWRA8or*03xm8kH=37396>H z7YotsnNRgUKYl@;&|<APsYstT5TacRTKe>1KN^cuv@-D3E0463(8cb(+86EZXX&s4 zQ>yVq%s`1E8j7bgVnUL1>kMBV^Mq8+r7v0PBRB~o#74Zo3mAZQ0+wHH)!?=z+nI>d zGoriXLSc%+I*D2Q9md4(D;|0KGqz8*dQe`Etk#DpRL#(f;)m}K&=o=`9GkQ7@N=11 zq9o?<h978d(#e}(rs@htECikBi7*l_O*bIKv?B=$*b0XXw7v(X=X>1is0mf8B>Wn* z9~g!LhdW2}!vsgV(21F^WyR)uE1(A(tx7Bw!W=aY?p4w(k!G7s?>k1gQ6tHH)x&fd zwCgEM;W`_d#c&cDD6BMvA6-_P%NLMxnPz}u+<r6&zLa91BpZZhY6>m=<{KVeR2F3@ zVapA97XiDvAv#vEokxhN)3OS<UD*r2FEu(V6<r@j(4qK#d;fGZUkURWnF|sILP_j( zA90m6r<QyN0TKw$#M9Gh??M2^Rhx3gU@gumC+((rS*auOqPC~Na9Tk~-+UfI$R-<B zcp9+}6G(3FvcPtoNAJX`fs1E2VJdiF!Bp-R0T2~H+7VOV+=!$cT*g#Oq|+R;l&`3r zo=$8_w<)6VDi|8f;gcNy+VWGVTNx9CE4fRO4xx%lJdwN|z<e!s?u?DvbBY3P*~kD& zKKjzCDbXbBdnN>n$qo5Tckuy0*6))YjeO^$eBofTh|;`HXIdJfb)VoQ5BF=BU%V_d zz;N#sH2D6_5gI&M#3nzV2rBeT*Yo(}j#>5!p`oQ)kHOylF+a(}{rZ$U;*9=$!82{5 zXMge59S2F>{VIQZy725ar|JTui?h=QLwG%H2#b&-vxZ!t?VK^kW)N~k$up9&B_u3+ zz%i(yNk!_Os@!0WiwU!K78}3g>iIA{aW`{+;Nxpnr7_=8cdODb`<D$em5$m0JF%ro zLa|=~0Zjwh0IXWLVLpO53|YmQSQaPMicBdKTDWAVVi<i3pSPReA=s$&Ks|Y-5mIIt zP@v%)_tJ5BZ$rO617lleq{X5a^pyN<+`6u5|C=YQZWdMYHB(dAVPG_^w?e2kZ-l79 zdCg{NuY6XIB`4R--m^0+i_0F4J{lQdUfE0*!AUQ9d6-7$+nSK$)D!JlW1ZPk`sU<V zxvvJZ@_ej{gZ{W>q^_S;_;I79IkRB!EDDT{1pLS=I{?Cra5TSEj|d!>L>g9(IEu8u zj^EOVMvPMIjVh)WyWOdt2>G?-G9!Id&f~{th$-gp6&KJ?B-c`&*)iW~U5t6Az^KdP zS#=Bmeq>{o>(PIg!AcE#V<l9u7lXXcCbbYumo}x<4fI@=#uXt~6!7wYJgg)HzNw?A z$nE(EtbQu(mO^orRi?PC6m3}ZBAur(I<fVc)$YB$nq<Kr5}SZ?{x)fKytba<<6>R% zzE0M^ulP(No`Ch`B9uTBo@hqYu}XD3inH1dr>R>?t)>!C(Rb4)5t<s@UX_?UU69&c zzKW;3UD+xH`0GI_>-T;r=W({#bOcuFeoUONO!s)l@m%zn&#AsD{w}1L;}eM{f;}4s zpv0J-)J#s{C@Fx?2x@W?lP-v5{b9WvhY9*4A^}=2TRAh0gCSu}Gwrq4p|kGOS9f20 z%X|GsUHgo1z<j#E>S!)&jUC;tl?bf2_qK2{_U1Tu_1wjm$x5T-hWuPCz^HI=FQ*yw ziDFB&SmW;fv^zTB5&#64Wl#>LV1e=iNvp8|MmAJh{e4WWh%h92U#hfcGEH_5E%b^) zk?R&EMrWEGBB(};)!7^Aop2au=V>SD$pjFV<tasrj%<FBHk>};y0<!j{^IV0`35bY zN7cfdd83Mf62k=ADGa~@3_2(e_`o40i5J_IZq%f4&9+z_%9h(O7m@W)9=$^lftqcn z*bf~~^1{9yPa1qssqdID-=9o(Y^Bm@xPbLKk)?l7|87B}0e+mp$PdIhOICnS4JQjh z@}qVFPG#a{1x~346XE)!c-=CQ%Jdp8q!(7=_Xy(Ep-40B*5<{{fr35&zh)ZG)pD=$ zZ_?wjOcrNc0&8FvCWiT25ZDjtTIitv{zfsaFB~us+FVV$dj&t?WO$rP%&S9v<&S03 z**PEgdGdkSFX`73xiEAzY}_9>FDv#7cbI_6_VnZ8?Yv`j$;GyM>9@T6vNa^B{M$)H zd5P*$15_R;zyldXI39@EH6@HSkp^C$Y?&|PkueO<5Hv2{@CPKzv+YJ9cK?O5_XaVx z$AM{|B<4i#)BM%<Qw_=(Z#fK2>`gz74;b^USO3L2dfG|!JC*yeyLwL5A)GhvCMs#7 z;eum^{^)h$^J%Q^mWyAbyZ6a4c#*}qnp>NzW8KllM;&O&7{PC3b2mBU>|pft{A5hW z@8}b)p5OmwP=YIQ$DTOgCjG50%xlQaDFz4x6ce#G7@!gx#MBW5>G%h7PK7C3svMcU zN+?#wWMM&h%tq#0c{ewUasPgpoy6_wu677-wh+g4y4hL0Z*1G(2SYgh<z4WM)+g7H z#}D~%xHKfwcV9n(@<>+KEam1yPc{lt0R!l>>T`naaromS08>^v7>nK>QiPcTC1$)h zq5*OqQrinn@j?Ns#!6xYbD&LM)y_TxeCcw@4Eim`9I_eQ+NgW*4j64YU$i!&c5*&g zR)zm4U7ClP1E#chjCrIfHxnF-?-++g@8y+e=eDA&1SY1V>F2IB?tC`nQt0C1TnH-L z^L0z9weNDHc-rVqv~Nk*CnNB@v(ur>JYzcZkR0pBtZg@|sy0X0go;C10Xruh_Qd@t ziJs=UQG^j$%3up*FiQ|>dmaEqkC<+4o`NLR)&Uyalr0&72`XJo&Du{QXf9t@=bEnF z#DZ1{8c086>Ki1C+D#Fo0Lt_dS2;e+mJ%ul+q}RD9fcj?@x0b3z)73*c0Qt+<LNfu z#XOcy*Z;h!@BXvMO%MLv%<nwAjP*6e#I9{>nag#Y*Oo<X0~-~<1jr4#$(vE-QNv!l zetcYRfwjgzwG-!YzIt?w4o|Yc*_m?VcDBg{LKl-i1ywF49iZ<5079(N>JD^}fIefJ zA5B>W&BM#N#bh0T1NQJRAahp1$WZ}E9-xMQZ<qxmZY=vdh9s`og>5sFj*gC<YC6^J zu$j3LjIs#nO0c<Z3U0&$i%AM)@I);I+??hmD@|T8TH`uqE~n!^|Bwz5`h{<x!^fBj zPt8&tuygGGo;)Re-MW`Z(?>|;I*ZYPuxqlJ3Bj2HO<emCE_z~f%4M|V0Q({+*nK;v zCp;EmhX11OjTCQOa6<>ufM&D-L9Uu%FJYM`fd#Hl_(Ufn`ZtR;t5Pd4L8t`QQjYG6 z<3wn8sR|`IAe_7U(eks$1m^a=AJ#JWEM&vuPNScL6rX4Yzgp7+#W=J<23pe``Ev7C zl8&z0#m7|=s68@DKFk}w%a7ab(rHR6F00o`&qNKN!2I<dx*&T2BV-T;C3s6j04r|Y zqIhl_cqg<faFU@4{YeE!q4{)lo@z|t+GxW_3;Rx{FN+7>&q$VIsWSNSYf9e3p7Z#2 zTY`X=J{3C9S}CW5dXPbuw?4RaK6F10D1fCOQVloFKtWLgWE83k>rN9$@%A)FFIfZ3 zW$6-lL*vHorB%BrREL@f^FmNU2qgKj6ftuc4b3-1AdFZJSrI*Ma>peUCX=!V+vp(D zbxj8cjTt?6UyN^`E=~t;Y9EY)%fgLTCqoPKs7&eLM1&`sB6x-@iHi1eVRy)i`qujO z*-Ahe&^3dc<7B4ycb2hg+NgM)>li#5M;Qhl;?!E;U_jok<=NlW)X}cN-k9U}QyF$( ze$2t5z|f5}_q-C;C>So8W`TEvl6L5B#BK~9je;ym{$v=G_NoGm7Vkt4%8IUPRooQ` zaDh*|pBR(xcd|3@i;MDyvF@bTk_l#XmCAM~>HsZQ?l&>~pv5eM&kaTk+u}0Y6fJw7 z;8#g06pJBg8O!dS`__U1Hc~TT-@CiXN}hzT3wkbQ?F@hfUxB<u8nL*6xma{2aDg+K zfB=>Q+&n)2$~8Ui*cr{S<UU$J)z1}|hvtb>T>KZ?TlGqfdixF76~Co~qB1q##Ao%a zUe5dsUpH4g6wqM2?M1+qdEd1rPOxKVuxh0FH2q*^&1yc*wt&~>1elo}o_0rt`aVTC zM}K!GyFWJ&AfUm!aeP~W$NA|3i!LSbScMxK<9zyFwoTW*b@M~t&1znHIc<jaeTRL} z!qI5|;SAO5at^Uy6OU5SRam&xP&8$AR~VOQaDJNPT(t$WXsT$T9$DlN_Ho0>Tg13k zt$KPo6>PSRP?`a{bZ90hg6`W?(_HW(xrj4j+Mf_8BwpLs_tK-#R;bM5K9OdOVQ;C_ zcr1D`9VgT%gUX?4r8dWX^Vp29t@`rU?Cfl=oJ9Z~;pDo{NAn;0e9AoSo>w4&*Mxat zCAIdNdJX895Y4k~a7vyh8RcG%>hsv7LyAYDL1R7WSrU}cDD6?WT@#y|D4FT0DU~9( zOQf_cFSb$a9|C+F<<<9Z|504@;!`sFJ{m7&G_){HE@4K`v%OHps%8DRtVbdT=+X8h zUY}zF1sZm2n%Hit^G;<sD6iLhdnWaA?8`BFcx*Xh^9Yr7B19%n)&Tus7|Qt0n^|Ds zk$GTCT(hakMad1p`(w1{osSceG<}@0j?9X(%*e>rU@(|H3=nO|-k;bdR;sOO)@dl( z&&d2q!<o*kTgW#wt2Ehrnh-CjvB4Js*}hF-$YSnypjVVvrxGFr+pFGcY60?CMzEl9 z5J<pPEYgI`8X|Da9{6C$o_9*pGjRiqw}{NDtNhEmB}dUovQ`PcoYvq4$)(t9hLYV+ zrA|Xep}?m|2eOZ(oEl~4H<}lHwaDNkw#4nYln*oD43Mv(X{qqq{rf%sE;1N0b`1oN z0|1WWp-^2{g7?dzJzNWY`$wEU<SH9F_e-C>AyJmb<9Cple<)&|iTGItbS_-TUiqD| z1^m;TcGw9lA}%SDsGMzVE;b0c0|d~BpgqB(QA$&zbp%s<?0p^82?5$B&PDGj1Fah8 z><A6lS%K`uS9=>XdDMLaY@9SJroP=)@8$Tn&5)$NwDDY$;oiUBHGQEmF0U6m>d44J zc&f7#Ly2}!pB4dqN_zT4*1Fbexir)-&eE!v{xY*B((2H-EBVxCf@wJk9@0ff!_4Au zJU@9ON8GD|{aaCR&@jAVP)Kw-Sy32~AVWu%>);Tpz)$Q0-bsmdvr?*GZN+!OG`^%t zBS)Or84kGqNx8;_!H;|>O@vg$WF`HdV?BwRg+C3K)5kM$#wzHu=sFT6Fhc_?D>f1j zA8a%PbIsd)`H*<eqw*n}%e*YObR4SFi?DQpA9y#+f$I7`Zatn2>UMe-H<<X&-lzC| zzO|NnQGuc6+PcvJZ!=vt+suqDvs0}{KMj$LN{*NIcZX-7*o6c2JX`%)lpuuN!K|VQ z;(GBR3G4AjMN_FlD&F~<R!bL!>Nn)BP~x`8rd7$T90^o4im8ORa$PJ^!4Qc$YM8am zfe{<+LT5<p1hIho4ZcXYPY3qn1sM#Gsc9GC2zeA>NcKhYvF}ourC+9tj2c8!$77ii z;}d{hD8l?gcMEa>X>d=tXr-VmV8HF;(h}<^p==hVmBu8^=8t@VmaqXW^2WFSH-*6~ zJW$as)NCel{OQt+ICS9eWBVeo=BSzC<RLf=z$`%hcR*!l)NTO)5l#8I77+3$4_vWb z;%uri8E5<Xo{7tQ-=m}Q;nt!iAu2Pr6K1d!N@7n~7PAhqq_!&TyB#PY&4;Krog~)r zX)nBR7B0*chyN~_PLV55vgLGY#*@U+YhWshrJS8z)U>xZEPdkd=lT^4x!K5t5FL<! zP=-))ZBZ}iB}}Q4soSLKXiu<FZA+Q)-~AWh6`tdTgYR@Ub*xWPh5k`w;H4DUeFQQQ z%mU}3CVqvHdrn2y6TFf?NMV%IC{7OJ86|^E=gO)=HCj{Nwe)iU@GMe4TKaAh=ZF<N zo+;9|v2Rqe4=L$G+(!GiGlbr}C3fyAH{%6@9KoRs@}A#i<>lZ#p$`;k>=-NEgog{| z(*zzLJUiz5+y=xhtCCICs~Kc2h9{R4=&b^0ku|Bx(Y3jd;`ZX0WJvu4QeWwFxK?o4 z5-!ukx(^zw(_S^irP9GpzeK&RU5&}cuo2)n>K3*o(<|e2jQAv@nom2lkUfC;A4MJ) z$68y{<*@DXYHnwg+S>f^MX8Lbbu+Ojs+1gz9^NT@K6@_V!`^!0nGE_MW}G1vuz^0C zJ~z4wcwKDzE5JkS0_F~)>;nMGux$yWA#-*_L7XI?VNjS7<MV1-QB^5NwLrpW>WQ^H z@00>Fh-2}x=2`6zeVO^m59*^2=ea*$1eox8*7QXYmMPo3_Z)FETbr9Wo$2&3ZZkRX zN_w?f_f7eRcSW!pjy;vA&X~#GeCI}oY7dEb9<PKNvEG78*MgJh0nU92_Vl3w$;6l} z$<hxfukLw{Ec6_jyhQ>J^7m1*q9R-C0t61p;hD03lwU=q0_fH@Ge+vk|5i?XBo)dW zZBvMmcx8M>cBFNaTKzrDm~JszIGJEZ-GxN#&zHH%mmuI_HM$xb_hnMsMp_CHo#qt% z1wquzS7I4Qe#eh~_$83jz#(gw&*w}X!^Hj*kC|Q{6}X%(h(l=+{dD4Vf)OYD0b%ct z)oj1Z%(;4%uascs)G<{p^v=f+{^-s1YmFJW7j&{S#?Bw-3fb@ax{Eog*;FZGP*afk z{uJTb#j}kA-GZnP$XposX=7hLelTD7r7wBK)t(ou5(`=P^t1KG-7Hw#x7J3|#yVWq z7?*kIdGgTokg_YI`Me6*ujK<%lrlaOv%A#M=`wS!F^B^;d1HbJKFg3GOaTssqVIRZ z6O8qw-rNM(NKuXT3<v0VGa0TxGfZqb{@L$Zm+Ow=L+oFqhZ$8+Z8};&56L$jkNcX% zO&Z_^ZrjLiD>4VCc+;YgMUWr)3s!Z-4?{IvfRQ0eW+XOyNbyk$0YNaG$vH^j_dMgn zKMhDp&b58l6T0aPx}4r+yxCOcW@Lv_w+E9=e@W5PZ$rGO4tZpIB}SFQZC@|u%kum= zYZvGjLpQdNsp5OC?Sh?=;e7gV=T(Wn--8P+LjELrEWC`7AV;`F#j(_<<sxff83s)T zzVxcklv+hU8CdjDgnI~YSnys__%q`R1<Viy5Trn|&;PBL6C^@6rG$oLg2Fe3SGHhN zY~o#|Sp}PwL-fMppL#JuEo$L6!J!vu=<p2c;{V#H8{jq!I7I3Izb?7gY7X*|0-R(x z2Tvw`<>5Ng6<QTk#5<t0%-=gSaK%z3UXTd}&hfyKlV$og6X;e;<vn0k**F}YAyvy! z&m7k6_+L-1&A;9SAk&;fcz#gn-lm|N1eD@$t!=P45<SW^Z*5UBTD09QbpmEk%BHFW z9-jET9iP^4<;|i;6x?$(eb24^xU1~9E0leleX5LJKF^;~quFI3)kp)x#VZ|S64qE; zXhTRyCzRf=js0qv^e<Db%Nj@2f$@f;@7NuGhkJwygDQIrCM`Ev+%(Pa4d%B$+hi!Y z5YZ_S#kl#7;2GTXrX1ea*tS?%nwqy&Ib&?I%777=UAlD+I`>jOJ~+uO2n>=%VQX-C zJ{*N&c^tqy^Hz33QG0u5WAH-(vNMC|ZD-^YT%9}Y=<HSV;>d{wHAD8~Nv8Gu{Q(*M zr1L3=Kr!QV@POp|#Bm@mJjIL3o=hq-0O+nK9Mn2IHOK6-35)dYr-`+VQ98q9pD=dN zMFHM8sw)-KEWUT~MZUUqUm><1%TyHe)hvJS#(^v%@#DFTn-&Hrj1LG(O&zUU?MNzS zQ~N|UAOqS~3kBFu_z#R3k>EXnxYNi>LGJ#u6ofNc6(@N{GEZ5cDi$nasTf*2WKWq; z^K{6+y~5ec>)v|j=c#wCay|hb<(ia-^}JH8P!tdg`8C6}xXFVxnB&f1DN`;ejU)(( z&_~W;=Ux8}!74mbz7>IeN3DD?PJBkQEF8XT@?cS7$<D_m%c@$T6zeUmHjf(Gy)zKp zgAjexwZ`Faza)vklRn!`cF=UcD~ek`dH!i@ObPeCp9iDsJA_phtgj)0d=!D74~hH% zc|t*a;oG`-{^3yvTW~-=tpTX+UIZ?;vJU^9b!QmYOM8%Qh<FoAQ|5Zsh6Po(l?DOF z$Tp*FkC*5vv0UgvblmXEip=5briQ`ZCy(qwIa{vb^jnc5;_5=L*{x2bnF~I@dF5Cg zeqTe%+V;<ZG5Kdd%^`=Jlu+8Q{1BYUlkaz-zoTdEf4P5%{b-yNWuD;TAC({@ai(g_ zT4g=W4xky%Nt)h|2<PU}E2xw4YkKzWtXtaKGuFQ&uBM1xes`>|=rG$y`7kA)x4I@X z@lpr>q5oXgXLwCJX83&4PhCJ?Nmky?@v<va*=wHD@wl$M=N(kkz3Wm6p0p5odQ21q zg>niWTUN-iZsuJ5U4wfgmTf}8-=(wa$sQHNpr}*6ece{N+{WXmI6Q_OeW;^C%tn5E z^Or~po|R63?-E_U#bC+Oz<L1Yfz9t(g&ehVFE?1z&&c-+G#{^@D1R~yG=Os%uPjH| zIWN=T7EDx({k5vYRb<5-FzQtjZBC-AG6&h?nX#5R7Lx-!Q3gbrDcZ(t6r$t?LnJ6) zIIg)<@c_0uwlRS~BOS)*b=neY;%!5wpH<yKJ<Ei4v3xgr_ClO?Hk=g~00p8{2MIC8 zeJ#oZ(V$Wgu?wyyoqmPvO$kbmx=F!u*=u_^5{dOHSFGYycI$G+iK)y%OQ#?UdqH;V z5*cKlk)e_1b^p84I8a?ZFw?mMqILFjd2mgfS-){Zx~677o=L=<b*nvN^KS;aaqrF% z6f|g-WjvWX$BIN~EWDEIL-bDwoF!BGyvUh(_zbIqF$yICAgbVzG^HuRDdddKgisF7 zPeforf(VnyZF|YVrITLZI@|3h8)`!pg>^d8y!dX*Q2W;*>b7T#Ct>+jY&ntefmEZ( z6p_m=UtJe5hQr&|%DS>O&Gw9s%!15i>aQG<s)*ULiOKPJZXh&@n(<LRl7nLjDN}Ct z$MNrN8RNz5-87!o;|RedI)enJqQ5C7sEjPHu<{4TqaakU!(5~2Cq_Z*%O~13o;8eh z(K9sTW&Z|T=;&o7EdXN<U8(%<i#L_D%oN0Zy1eevCvdIBfSXtmG&$o}{Qlp6DHd3+ z?ym0z#3)#=CAx;3<HDde&J3w<<(BBdGIHD_=?}N%!BfDyBqPSj@O0O<`Mrhn_$$I= zqtNZOBsB)5fMb9~w%#M-dBp{gHtz0Rzz#6ChIES~FeWTxnIt?8<$hhN$!?-+RT#!( zuB^BucI;qca#oh3q_?)5WZ5QNmWy7TQ2#RE!?MV5;LA>Q%I8hbv?aHF-*+(u74W%$ zGXOQpN@o4Q9<8i}8IselmA5n_caR<Wg=F5MS79qqA(Cs9G~|_~Yq^4=v&!}|?{c1) zB;^Q|<Be2+3;gPRPDOJDC*rI%D4P^OM88>1X&@M2&jiT$+vu#2f+7~eB9N0OS-?|A z)X2dqqM6{YN0+KLec>mHSjA*?cnDpmSym2z@o%`fYHDl9_}dh8;((8O*)*A{&#RTR zi?6=dXIy@nb|jZdr7hZ3OTn)6SnB%fXRqYW60$dfVbU!rjoPYc-hKomQqOYDoXq#j zpoMf;BL@*F$!T-CL%eRq;>>FHww@m`XN>Q4XMTaf4<An)YIOSf&px@*J@hx<!K@+p ztDC>o`)4w6Q3%ND^8o~E^9XVN$%t62@M8)T)(QFVH=KNo?7ElVo9@x4eU3S<<On;d z+1|{7g^j;G=AFnizqj1SPg{qUHx%DT{Db|Itp!T0N>l~c08#ZN;(~}#)%v2AX>shU zAKN2u;x!2fpLcRi6zZyj6UJU&diqxgsx&CbqNA|^)B_MEiHL8Hh$;FqAy78uYtTT1 zG)pax#F7>>7?9+QCvF?tTf>8fI8#U-&Lp|5T=08?l2w@bkSI3w$=~H^LQt6>67KGy zWP{CQl*@E{LUav32j4JLhM1H3f`AY)6lkO@1?yJ5&$bAe-S9XB?-yW+#Zuq_+#A9; z#sdxPrVYaafWZxNKWDT=7Z1*5@U@New0sphjfxR)g%#PLr%wi{mO(0k4UT#-qF@z- z<A!<xl?ddQ5N-|$=<+WFe$I5Kw{VBH5>*&%d4pZ^H1S7nL0yWh5g!7IcC~*Q^5IR% zawgu~Hp~PUpv-JFX%or&k09Yu4E0DvC)LNlVfvO(xDCvrJKUF9=Pqt?P}qkGqIVbq zRiZ=0p~IlOp0#(otvj+2{%-Wz-|*L;YjxRwuQZ_oQOjP;^EEU7)#vp08TIkx!{+I& z9XYN*-MO5YeyY^#!B;%X2(L8dz?R?@)sz{DCcY`amckPx3*iVoq?R2=AkBkYR)-$V zb}g!T)kLg)27P=ltQ-IBx*yb7RFvk9yEuNa*y(IItjj^rn$8h!NXT$L5-lVZ?!|r8 zsXC!6(MC~nh39VLbh*N*E(Wej)=u%^Il^CAW<v*ju|oI01rb=aQ%|W1)n6L+d4mj) z^P&ouaD@={V^i|T&8f?NM6B&8cCbV&CWC?RFmBwr0SnG7n_-bq?h(BO)CFfKVG-C_ z(<pQ<zjG!GA8VvKJCaWJjjcGUD1xd7_v5sEFzC&J<*!f)gtyUdw`4N*{$W-cL@1e~ ziHfb$fA2l^KoJa0g9aP=WX@06gK@IjrY?e{AS0Q%f1&gJ63Pq&&=CdfCTrLfF%WPQ z5&i`#KqF+`!K#oZirPaj3Yn8Y5>6&hG7?j#;4AUzmiHlyqU`v>ZXDXsh}n5#I3`_o zLQL9i!9wM)OXFL}77iY;HmyX)BN#`M-~qOhwBg|f&oExGC-|irQ`(ejHTk1vV+4=+ z?4POW@7xT7z`RC=6?}7YEu)N%F3Q68X7PjSetLSi^RZ56<{Wy-FzNj1A0QA=Hg`}T z8fdVViA&dxg-6$Z^JgA7q-0IS)30C#E#yg@nv03lvE=3?vQHX%%I5j?@D7VTUBCCT zc5(}@RsmrL*1ixb3IV<f4b*dAxfBEQjO;X98Cq3Po;YAEE0tsnN&%CAQ6%==iu*hp z*p2ZSxwrDlAHeu6>zeTv8ZBFVPaL>A#2Nyao^X9W)`wT{_Mn9e;3Krs#w;GMbQLaQ zft}Y<Y+eC?_1sT+)YP2?gi_Im8Fn+v*FOKWy`TZ?wzfLC7a+}K5AOojt@X?pf-!=! z*|$C|-yyO}1k(SzFJpWs<emBdtZ<XUlD9RP)px25&V#kF$(a=Iv)6d6;M^G+I3}OC zTjOA@Ss%xFiK~~sI_^jBdK>({6CPafHbxq|P`Nl+6}hPVs~{$v`m#kWx8WVp#paxi zc$ldx7if}|Zh$hFLhw6@Aq1O<Al}}<0&*7EOxrCB+XSaILTK?mYTuYU`OWmUsqSYW zdw=Dt%i$6swBA*)ZqT+pN?({v<tEOAO@$oF;nU7UmPvmI>9}TFo!1w(@!wKkbgsr+ zxnb~+ACrBA2Q^ZI-o@?68+5F#e79>K=1_vP7T=DH>WJ~b)GTpB90jW9Twm6!dEiki zN|-Y4eDIKRAwX)IprmBPmWrs3vTwhJGoCvdA%Ici#v!Y7LlL9-HnQdNKWAt2lO9fY zUHn3l7Z_Z@7|R<&o9yq&m;4csiM^t$UXBG<c1$&x1h^FIPC&-cVQU7f&<3-@huBO9 z!W?jF+~VCELzmfO6{?`;@m-ek^Kecy_}3V`Q=_YmzLEl%)K1EbS;-}A2M0EIdtPpb zBO%DglwGRqRxZF<B=0we!)v@`YASV6hI~dCI?(Fl=oct<M1(XlgZhcZK<h_KW4+1` zFhfqnxdLWk2<8c=O*(nC7?@k)!iH-;D|um(E#Q3j2dY)6)Hq-=Py?`Y#ilAknT$rq z2YZGH!x_TuE5(y=QnZ_>g+<f@j6<9xD{nY*KoVdnoaP?r-T4HoC!JZAqfSW=bBd%g zV6ES-Bwdr64;JCc*oyv|xfrcC0(V}tYf52cc}I&{-*ARXlR+9->z-RS?aZX1n|;0l zYos2$6-%gDTT2b)Vd?Wull#&$E|N0r9VY=wOY3xv`ORBgydI?I&*Iw3{cXRLn)(F{ z8lClW+jOZW^TX^#S<Wi`z<Fw}aL7{(S`J}@`N_o)uQ-MT;~67BSy^Kh4(~Yf%DKRe zZ6j-DJLR@SSr~^}Jc<r$l>3lYyB2GoXxw0io_H$+KACb;Wx&T~q*tF%Bi4h0ut+gF z<J~ZD)9#tYN3+Zbe7>e;>|hD=?xg)n|0)7-y&Y^nwu~l*w_-cuD|A&7Hw6FaE(z0| zeTbZ-7pHK8#F+)&Mx7v`USr|z2z}v7i^fgiQ!<KTuG1ikzjmO!<VPIweklcyI!%~k zdKa%(2w*XxPp6jivQs0e)S5&?9j{qEpu2>%X?YbQuwOh1Zye|8r1L1+Hs2qW)_AgP zn5C^ff7RFyF{Qh0ESGM>NT5IChzfeK<i*Xe=jwNVCX)O$E~ZQ4ACA<SA0}peYtQw5 zxoeRb(+umr=@t>pIX~BZmFgH1pYNXVV*!9u6azy-gat6R2U5o7xfXnkj*gy;op5Z} z_3%-kM%i%<%2!TE6vRo-zF?7?{z)uAGratnZ(JL}!TtSu>W?qQ*WGY!l|L5gsjddU zBapwPTlwSNv~IHA;l>ZXOSlM|?!lXTWH4=0J4{JLZ0vdHZ2n?ptbIdOrk|C;UYM&~ zTl5L1PjTmbdB)R~hJe`^z<B+Z)Dm%8f(aCclr-@d1U{v4Qj&>^SiMnz#iJ>+@@H`N zli`ii6#rm3P2W~}%5hYM5bU_2ns1S%MJuVWF&@)Rgl+x;FLV)#5hcE9X}N|<K=(ce zxs_XLZBuc%Jh$hGO_hxAGp^*kzUAqd?4+Nf#JFh7ja*HV9mPXzm;J0=k=~N|YM&rf zRXNQ;MU{;Z+DYB32T(V_QLO6%&llEPg2RYN383vnlYQmR82wc*yHRXUUp3V|F#d}) zM6NrOhuW)BuFMlG)9f0cnI_JBlDc*hzKec6B$@EyojkmxlT3$-&*}PC(qSe);m=71 z^-5^C0$$&r$=@tg<2z#7)h(q4dd$YeM%FcBq&h37t$26}zkCzyO*1L4I8?UQ+FgyR zQW?uBE?B_Y5{NNjufgN-W7vKTNjXASMtSq@@@?}vd6a;WMwnM5&eZQ6{5@0Bqm4lh zq%p3Gps;>Z=`Y)DaoHYjg_!wXhBbdnOwyik1}Nt5^w=rOl)wZ4#k_ux+F0A$yVy0J ztc6jsH61prczC<9AI8l9XY+sh?Nt_-jHG3YiGxt!gb8TPUem<nzy+)VL8TJ0g$NBN zgP3BxF=*=Yh(_?)gvJ#2Y}ZVmR9voVN2^MtC!3xh(R){EwD!QPJ_zS>ptgp^0M96K zcAA4~i<qHNxF?(4sR;^I_1YsShSnmKXlUAzbE*{!pfy7?y(R{l_Bhsr-W^YMD4l$z z{yX*wk>{6*UZ<k4*6pbZ;}X^=M)AYl6=q_sa`FZv94#P{E=cV%3R~+0$IVsh>ri;Y zW>W>=hczTKy004#Sm_%%(EGQiJzcgCs((4K+(C8m^dfN7mO|}p&6VGJ63l`gN1duu zhii?1^>eVYo^_lKI**C?>V~|Gv?YYDpy8V$!**sZfm*_^5rUIjErUB4cws44RW8hr zCy>5|-dR=dtu;Kob~4L-gPxf~5(x1}V}w+7L1<gJh8Ze>6nSg6zyehfOwKO%(o#lR z<v~!JLQ}}U@)I?fj82d4maWlWA3R3Bv1N-W3Y@-59=FI>yuvcR@KUrqrV2n8p$ZrJ z{n&RKKVd(^RGQd{z6n8r-ZD6)>j|27BIJM@<|R{!r(e9vLHa(UK&%ZyK4}p+M5RiZ z6&6CcTnyW}dpno1aIh$igP*4uoB``ZP;o2Ua`p_X3J)U$-B#_~*cBv685WTiQEb8G zE|CGrmz5C~Cpp`DhfmOXE?d1<G#R?CJ}vjX*pgu7#~!E9{f?^uT{O05!3~d9Sw=%= zGIos|fd$6Nc2F)qaj~l;CG1J(Pi|l9QUXB1*603)Qp8(d!6K*qfefUl%2L=%&A7X$ z#<7<6f`QyiFMX`4*tR9@7liIJd32!jWtCGx*Y*{7I8Qmg799zNdPbC9+uVW)PIZl$ zW#xK65SS?6dWY>e!=5$U2-hg}k1C_AOo`{s1Rl8bj|Q0UTh9-jQHVGvP@OW7r?+N- z9)bHAAc5pio+&!6o*m!9rDpU`T$Y2A5A#b7|1zVDi7PD%B8>v>a8zZyoE5fzkF(SM z1>Z9$H0*XGdpu`~lqB#ugfUAX(Avea|Mn!_bm1*k`sCBsXPlBSSg&Uf)57(B#96ry ze!&2}oATEF6B`e0bAuAh>bhVVUKZS-m{tI@oQjLHY4Z*QAvp<klgt>6%fg~-i}M1X zGbp8hl}#821l)gDfJj2Cn^|VuVX*TWfiVAKxloyGZWm9cR9*bHCVkfeR*YSw)(E2q zkv?i9un4Ok+GDMc@os!43r$vGhr~i&f-$%v1f`GU)~;v()+FmCd#gr*EzO1#ZktEu z^s<#hcQsv&YO_j{XhPX~-$00A$iD&WBBV?<lccg3h`p6UCV9LUt7xUiOcICwm6J+u zfBsH%u44!kYo&+0LMK89pw-48t^bKp%5Ysg%D6(jIi_{Ar(*2$iT_)yBDq<uXoBvX z0w{0W+rjS94gB<E$C6?dW444TZs_{%c;>E{rnJ-qYR7(4+rxfSGrKo&pk-w2Z<z1J z^F;^DWVU+sKuO%m<!$+`tA(^y_AE>~%V=P6DLi|Jj-4ynVD-0GJc*H!vN!^@HD7bp zM)Ds##xpE3iPhfXamfe^Q&%0=ACB=SU2RJhU5;d|E@xjh2v-eqE1{k)_2eC-$#83H z`kQ!s4{Dp;c1_AfOJ@%@0c*;*RhdcMiHj>G%x3*&hvNy7uhLJRy?idn1E#7Ty)jj) z0Y|EVmg`1aBPu?FDp))z3LZ$Dp%w6WLSbAdY2xSXSTE-~3_kkVaG(<AWsnuOzQa+Y zU*=)N5UQR<c&a1jzn}9KIW2L|At^3PHU8Z)=4$5A_<fb{Cl5T6e_k>yz8*3ma<VY4 z-+u2$9@CI<s8PA<=VhQ@t80pK<Ee1eHgh;^)`>c`vxh((n;-`8C(L&Q@^zIb(FjmP zf~=q`qHph<<0&Dwj}M_WiR@SA(Q&|i|K6C#hM%rR(ti6!=|UNDexK(VHJl&D&#O@h zrlX)O2(7wov7Uxf?yxt#n5Zc>!5LAbW?G7`72f1<u<{2E*Sz_{+0;~fWcCCh?|$YD zy=Hgr9wLAZk~lpH0_XvOr!d+voBI=Ey-fIp&1syxqHeAe7Zofy^1}mF^r4EY<$V#P zng7so@A4(Pvw37wLr@6ohiegfjeD+bP?b|%^{hUk7C^v1zp($?JZwqS*(06T<gC`% zoc1i;DKc?h6Q#i{@($%O&wyQ#czFyWZCMN(XE5+FVB_>#^0qGZg~FJA3WC~d^iKHO z5A?_6gge!Tn8&%thd$QwV&l%3CCUReKgGE{^8xg5VE%~^FMed?Ph{joMK=z~*2LT! z7xw90424l;dp4u3UG}kUB6=E6k3vC;CsyaWV~GizxBOLKPRh3Xfn#$Re+5pR2@_Kq z-BjtV@Se9aU=Ww2)<XcRhv$!J3xf^Wm}EnhPo_{z`A6~%i+mfLhE`5)?P`?g?xL>6 ziUD1U6ROH{^ar;K!lH>*!Pg3q-5gKpEf?3v$&OajjDcMkmjSsQpiNg>jKA)=3$*)~ zu77pK*dVG--h(GS`-$#(sVkDQ)Y!<6$gnEG+|I6@tDk3A#9fX@$0GXcz0UxsuE6v$ zaDPC08iR=(jgfKHqg>cOB%CCXepWH}*-Q&k$D9`6<)`fWs#nsSOnrhGL4CrJWvjWf zj8$KTlh*gK7rRf%bekd=1`;P}#E(wBpIUITb=?IL^^sU8Bxr+oG?-WeM^<m!r^?DC zv-o+Za;ZyzJsLcL^&K?JFgqfhz6&NTMPteLq9bfsyZVFnttMSo{EioE2~B%d$ju3p zD*;dk;pVO&At-HGQmCYd<e|BE=c`-3xI)Bt=g1=Yt@^T$(|7&x_IZHJt~)8Mm1ccV zI^sj`aQ^my4WD;cR@VJD<<x2kyt^m{n)ju9UjcA|IV%966DHXnzWy2_zvi`v5~hIN z>XYW5I_m^-<?cR{k8j(NV$Ikko%iR;#$Zv9@*=mi**H!n^zK$$4)@Y-e8%A+Rf(F2 zvJ;3S>=IFN?(lqB<HA)H`_W5*H8XC;`w_S}5C53{GWo!@CW1vimXVB>ZBFancDCIi z%~Hl>4jOO=#vT+ibU0|B>8X3Pq0?~6#@wzkNK4Pm|B!J`!$;Y!{)d}3mD;I2^O;pt zXhi5O(f*loSbXLvQWBYDrUwQtkclLjAwJ0=4j8%|D9?L7f+@(8`^6&7*(e2Hh7>HL zAsT_NoSi#lTO&n<#O}B>`YEeW%SaWlC;F#NIWQ`YJ6x+lr_1YkYGAx4w<UG_E;X&X zBb7TSsVE8fEI1Hi4V)nX&5Mgw$0=2~`V9@Y!;9$Ju(udQsgHm;1+&R5om<GTmwn+- zD^H-@Xogrn<JdGjmyf^_ne1|%i}E!6SY^5E*i;}f@)x;hsjw&cc~1_bc4W$cTuy+K zahrVa-#}age*(!K!aok4?&c8jD{;ZVBor#cryH1Vy4G&!sN&iRx@XgUdP@{nM4?1g z0EzNu6j3PhD#^;DiWbR~Qj2Dap4f&F@v~1#lOaA@tZOj8ki^6!nBY4}C$^dTyyY9% z6Dh;6oM{2wwI-N1!hi@|k_GtDGRF`kXSQq8kgGn<1a)?V$HTibN384VG@rV^20?E| zGk<`59;;Cbqs~bxfiPiy+gHu6J^I@O1K(*46%9V`i#)o?(KZJu4UnW5cNRtn2UtPw zsh9Y;&8x%SBH-|Sid^)*LVVbL27CK|2K)Md4t<@9$t}`v=%$u`JL7or!!pKt6>LQZ z6y#+u(G)Nt4$V(Q0UKivSbxiBi5T!@jjQ7%@3c`PBY*?KrsMrzhHo;=Rw5Y@S<ksr z>i1@S>M)<BGjmlDA-P74wi+QRP9(N+C{^fe5ixl9V28U!@jj!5!EK5Wt~Y2NVzttY zI#Xkk>xmglOQWfZkc%tkfKXAqk^}9$`wMno1Y7UnLJ6kD_LsC@<d(%+Ix-#CWjT`3 zQEi7>>8qY9PL<SHIR64we@BQ`wx3<Zzx6I0>kPTJoB<Pj8xM@nCC)E&>A;e@8qgZb z?61;xo8Xo}w&&OBP#I<wy?Q`9*r-IzV}^xDe>lO0`3`q}Vb){_252lvDa-T%ng;Cz z+(Lsb<Fd?-pMSCEmeTT&;rFlw0lsR7Rb!;qGpk_orQk}sSa-LP2jOspFYK%*ls|Qs z_huO>4qss&-d4#q$jes@Yun(*R3w6@*`jd-(*Z-WrIN7Qz8H#vk7YSgaQz%D&8qSm zsYy(K&U3GxJ^X%8XwZkKR4mrzEWh#Bb48|>UpV#LfcwIuj_E_Or?{EVLH><Uv`t@W zl?`~sQkpOhmoz+)IT|1Zxb3Rq(vi0#pLQvWMlLdz@Jd~5i%}Ve^`QqF%Ehkmm#wfe zfvYukl+p&SCy1;dX$LyX&Z3zG?s%!lnqrZ!Te>~8Dl&#%X2Mb-@x$mtpt8-|xKo1# zSY;ta_Y*omfh<rcPk{VEt5Umq^eJZ3SEj=dF%0L!S%Q%la6B4XT?V19{!~V<!9WqW zT9Lz4V+I<S5hdT}^dhwzUiw3h$eWf~pNtHF@fVT)etv{|@a!sM$oUmLsu&r%^SXe~ zUAPg)Dc6rA9WqM&Qmo1_9oBQ(%bUr;vgzpIm;lN~7U*F89{NS5{m`69&G}*RJm-Dw zw}7tTkM{RSU2tFGf**!-!CzfmjDY%8vYaZ8Z1=B8>0u&zL38b1JHL|L)L31K;KvUz zDxQ4#GAEBtjp?#nBerz614|zJO~^TnZhk2J*pqosE7wc3vo;S=$k4oy;M6{@z|rVX z#b8gA0apC$qt!g};1XvrgnK-FTSv2d6XWUyo0FOh13{ZCDJUGm)&r=&0*X3cn$pTp z>JG5|VjF4}cKCHGQc*@mZ%jj(_0E=#q_w;w^~}V!N{Xg&%0v%n1?D6w$IQ^bRoO^I z(3r2MF9^nV+yBmY$~S-vYK4Ekn5(u`QyVb<Vx-(-B80^pNXG68^Rnx#l|NSP^!I5# z;9)PfKsR&ktNocs8m;*m6dq0e8GIl4!FsKi*zeji?GA3FTz#s%wtx+&57{&PjI&|+ zRm7h^;Go{6H~(!pd8CblgTp%zD?0HBd1`Dy9hY8Xg9sXwLYI%NycqIsh6Th8<PWnO z?5?2X%7iambS|O(r;AkDP4W+&4T4YSr6Y{a`=>McA^#`cud-@bXREO&=)&jiTkqSB zSF;G*kb0b|i_j>D!YC!+uQap1?1_@8c#vdHzb#UV_@NuNzp&=U0ZneQ^c4gZsh(<n z7gBGP^o_kh<Fh%SC)?|0yudaBm!v`ovt{?jHHQ>ZIoKX-$2g2ao}+f4{Do4rOis7) zw^#QlG81|iqjwH)I2sHb1^wxl0}4>mDlZGlTG_w$@)Cx?aeHi`^EZJhHn-)-+uEGv zM(G%n9Qb#o5UrSen$S&nC-0fLD5R9Y*^lZtiGFk``+t_bZjnD(eR5M&Q25qbz*`wY zuEG8=dTIRx2s3<bUp~NYpz7gFF{0{T`G=Vfb6i_v-8`9nPiIGBt~N#Z{sJ;k<6G?s z$?bLi2%9>~e;+q&ovCBJr2h2(TL|12j^T@;DIxKcu_G2Gj=B8A;l&%+SP?qBx*Wwm za8IF~vFhl)$0NN&A{`JXO8)Dx6a6dbbN@SG0hPfp+L4f9?e&(rRiDrdf-&rkWK>|# z0z&}4AI=Q3?n`3=889fl4@U9?E0sa|bfKU5isBDM<OC7^+`Pof3~)OXG}}#J#9^XR z#c>3?Uvj0wH}pRtp>M@-#jO%so9n`A>EO?H@@n8qMG@`!<j!)`T^ug^;{`R4tfv-& zQR%(jdJ-UgK8BN={04#Mc^?`Ar8J1?Ouy?-LsbM*c=*{kkuGU@GtRe1m=Oz!SCyJ} zcs8ZjPK7Ihv=>y^Sn1Q6z){6xLq+r``%pw|UxKHhNj6xxbYeQ&Z%wneMttROX>hMY zevOG25Vt?f!e#f)mjw<ENT`-18jl_F=i&wZ?b%|+WDM0`??L~?U<>}+GlRbkQiDX7 zuEhwN@0G%dBEC5JtW~E$0-jqLic}qTV)#jy<05$bTqX7O#uiCNc;Yv{qFh2ZMs7$a zH`rEs@iNaln?xGEUle5M=26s-`<gwjv`3I)-JH!d=va{2M*o^*TTncK*PBg3m{wor z!!#*Lzl{$Hi!2;^wC4RIjNyjxn&B<2vb7X&xPESAB&To4s2Hut`R$0h;6ikz-|R)m zzlWomH6|3(7MfU%vv|$Jmv+`xpKD}RR<HeK((dciPc1*$&@ZVXs$ixk*kZ^%ceGPc z{vl%Z_ls{{5p^Dm9EL}?2Z!j=`Y%muRPU6I5ijqJpKn95X9S+gERYz#TYUrmKW4c6 z$Ht%Q`QdkNLR(V2!}gPO=?=u-(Jf9%m$IWDGW*$i1+V&*cL=gC=Mb;H<F?x=0-m}* zM@nQNwr*kd2Zrv}*P+!4H$<G=bYOJLZ5n@uKelFKz`)_<XmNin%F>56TxCT)g2+H` z-QhuR&JPp~;9el*WVLaW-FiQBz@=|<w`8z65@S{-ETz8F=rh5-zecIYyrddM%)X6> z2)L_MIG64<^^uVIVe5+Bq@7-@JW0&VFniYKL^u!tId+E%2Hw7J1z{gXC2m2SR6ta& z9<zw6IA1kY4!F&oliWAs5FQs0S!kvD1<7Y-*Ob{=O3HP_FqL^6YXKh)FRRKPABXim zko0D1q^pDLEpyb>7>Vem$y{<aS!MC|7-7Apea7apg_paIr9sv>qY2u?Wl?Fz*g^+X z`)<i`k1zMcIvSQwHNd$d`1#YBj~t|>w*6T&vE@4@KeBG&$*!uRHg#34y;H1f8@2lb zr@G6?l!FK-BTeL>QF{1M5+k%3)a5Qz(KM*j?K1=tOFgtUUIs^b<V#SiuPUQY32<zL zAN{=n88Qw#1~`bzVW-01!CvC!JD|4BjypvCl<o+FSy}3~^}VME7gB7e+e;Q$Z#*rl z0j^CI%BURzlj+c|@lDqu*x_7_?AqO!K$5dvl_3%&f!vcnw(BD%V+t~So?)mxf9WN_ zt_la_2u<+<EUTQi@)uWDRKO|~eO(?!YLB2P>~M^V3^eV^64^WW$0_7Uv06(JtTVSe zJc^vRh|5%6Y`WZ6%>t%%wx>1J4C8=(PL2_ZE6r!jf=49UCX|{R#Mjsa<d|Y{&L<kq z$4RclJ>U9=H=80|I_CMA*&6hJRYOhLwN`9KNL8k{<!v0D`3s1g`fqX^e8TcVRxjk= zsw`a$;_0hFl;00cHQXYJNJxLbXC~=4z>P|tp;VDF?cB<cB}VA?CBfyU{Y{1GBzW~6 zurKl#Rh}?c{D`)4-n#yu0Hh9E@oeBF$wrS9z63}ro4tY6*m6YbgWnX1!}m!s0Kf#y zM9egRKo<zcX4Yf?@0~Vy^5siT&Wp#W0T@iv$c!|8|M#7&8xs`dJd>HJqsmv6|G^}> z)%zwt8h(@CTxY>skiTz*|NWQG12qrbQuE}K0fKOjy7!*|+-~0*00031004ymegFjk z000000stTYFaUP|0040dmjD0&000007629i7629iS^%a2kpfKu=L4z(<pe4OW(6e$ zYXzqT#s%dD7zSqsx(7%H=LlR02?>}9^9p(k5(`HQj0@Ea91LU(>J4=b{tk-{{ts0U zR1o?RuM$uao)Y{MixdPDpcO6^ffe)?niq>0FBq~JFd3>EL>k;1VjK<}RUCpGz#Rh} zAsu2KMjq@RX&@dTiXlEC9U`A1_9Jj44<uA2HznjIEhf$<KqulTfGH3uohkz=t}9Y2 z;w+FYE-h3o-7YRJXD(+hpf1-hEH7*?&@dA)NHB6RoiN5R1~DWtTQR#bL^6*u&@)Ce zsWdt?oHY40RW<82a5nNcGdFHGpf}|=`8W+Y8vp?S000310Fpn=o?j0<^#BV4=l}o! z0NApb3IG5A0NApb3jg%~8v-E$wg3PC2mk^A000000C?JB_{Ctv00fM_3=B-XV0;dU zs{l>Sr9rk~6adiomxjB$ySqCi;umolfNkhB90q_9un8D}h@Ehea2~lzJ)_i7&M8Nv zn5U5wMz~>=R<`J;oeKKtpgr!+s6S<d#<(YGr<@#`$tI6N8sopr2)UGyPCmn^b*rPF z(!UuWZxK(YJZignVw4uv8R3j>{xeHe{OhQQ_;SQD=rjK%;{9>gQp_?X5i2H}3QD7< zmuGrNu}>2BHHW`;t+UArw;Zs_2-l?Op@ceysicN%nrWnk2C6yZlzZC$v?mEkvYjNK zO>iuN0C?JCU}Rum+V)?XA&QCj|Cj%7m=u8`sGt=9oWusi0C?JCU}EBBoWQ`!z`)eS zw1<I#p$9@UK436pWMV)94GawL1sLAEh4BN@TvHev7!=<BXIA)c!4So?jp3;R14Dlt zGf?+>revTJ1qMa{0*?`S0C?I|OG84%aS;CgGaaB^cKck%>kiqr?NzR4+smg5%<tLb z`2c{+-3bkaNPvr1Cz@aeFpS>()r@Iu;RsiF(ne^Cen>}N3h*2+@hWfd7Vq)_ANRKP zjt&l_v)L?w-Ui1v{gIpeJX`13=cC@%qC@2^vFu0oEqkB6$WCTP)UMi53u<1%5)hww z#4S#7h&`NJ+5^8kaRRMCH<An(*?<_vW&#pS=n8;A!NSVM&cVsW&BM#bFCZu+EFvl< zE+Hu;Eh8%@ub`-;tfH!>uA!-=t)r`_Z(wL-Y+`C=ZeeL<ZDVU^@8Ia<?BeR??&0a> z?c?j`9}pN691<E99uXN89TOWDpOBbD(2=PKW(vAoEixw=&Z?^?U}kz|Awyn%L0S<* z763(sVO0PC0C?K0RppxNMhqQ?mf<qEAm6#!ZsV~pGuv^}%ZuM<7wx6r<@Oo;G;+%S z`e;3JvP`9gXhx%vj*g@WD?_Z7<0v6|`Ma8&f60x-k7K@R^K6oCP`(^<rJ4OPtcKO< zs$Q`jhgHHV)a{<KRG0=HTZSp!@VH`Vjrh+wuFZVho3%#6Xf;~+BzCl8=P~izT<q}E z#3sHgtGh`;Mc*h}<5{Q%JL0QyeifE*785|^8S&^`oI*o#HY%&zvbvqxX_6#1TW*qQ zR&()sl6YJ-1V6QA20-<2F6O!pxS<2UPuNO5t{Dwnv{uwt0+N${aQVb}w93_%1ARz2 z<?vlxtv8X@e4NhN*+LTQ1gB3IV;tF%)F86Qb;CoU+f$W3H4SWaph2+?GOny_u(b;R zTyJ?iWJo}cg{!~U)C!3Qewrp?lD12b!=^Vhrot#_Ia_3pnBC-$_PtoHh8`KG6y-Xj zwE<Jg7B-n|fZRmM)uztc{Sae^T<6QUrEu$@OXH?Ys5drRjbg{vPSSEbo-jqF67gEr z_IT33kch{_7jhj79V9#<y9Mkfu=98dZl+602n?(uE>DIj<tgzbNb`8wyj+Tl+FCog z%oFSSM~_dKm-F$<#lF&Zpg&dWPntqah0Ad<HHG?R0Z+Noq-fos7?+O;e6V#^6Y`jg z3n35L26;|p*p{PlZF6n+&v6C+r6xfpUEuFRySr3}6tGb0lm=TNtH(XdvdUVWGOAFO zXes6?9Z<w$I^a<RP6J92{+H=#OHHaE$kXCv!{zT>`x0n6joePT9-lFVCGsqI7J1GT zRgvdSQ4@K=6m^joP0<i(n_@`hJEj;G`K~EOM1E+<XX`VMTjk;_kKJ<dwa4!n#S`wo z!1z8ez6aj~j3OOi6nP04MQUIac^Mc*Hi1#(3^0oP02oEi0;5P{=waDvo<Z2@ltO_J zN;x3eNWb}pJ(oR9(iKD0r9&#Av%6ZJu_G<eN*-U`Dvx!Rue6G~bta0hCS`)xN@fl{ zzHaDNiT4KVtZ07_Oc;V6tXF4$R`Sz+tnU`rtux5zCh`a1gYmKov%4POGXA-T9^XFB z6iv7a^E)VsI@=_lx-z_=@_C+jbr;hfuONT6FwwUy>&$7yziWUB1A@QOghxVmJ@+-y z!yLZuZ6A~0_rWzCh!t@v^Zs-`{;5J$vVW?~R4*ohyn(|Z2CTH!9ZVmpxd$zQjs9a_ z3DY&r)i7I|i?PXUtW$Y|_TI@fA@q#ypoSU|I>-y6jFQqpL9|5CI7uP7j)to5^9qlW zQL_Yr&$<39w;c;5zb_mRH1(MQ2l^qXc_3=!sso&LbXS5&wH}JTOklvMT8e#os2v-c zO(mRdQ{HG|_k8EKZ@*9nb?~4vTH5&0071A}npD1?_old6%Ev~NFRXR&Fh5NE!naOi z0H18B=XR=}?zTSA=9%HU?txAN!}r`Afu&i1cE1hE;<FFaaI3?>?p6zGw7ReF4&1fz zXy6V17p>U+zq(K0XLkWh;|bYOZzmaGjD(EM{5z7rqXz`&ySr2FBIPsv;p{1y!&5%r z#4LVvi2k`Lly&+P-@^KZ;X4p7g|!I0QYQzBLD*MD+L2#k`P%gG&S3Ed3riMnP0uPd zfAv_`W)1^D6oikVkBbBi9CIu)vt?#3OVFTqR*=o}_a_!R68cL9^CywT5IGEy#}Ea~ zmqZamlrTgYLsT$75>*UQ!w_{0(ZKvmG%-XAL$onO2lFk_#SlFV(Z>+3<vpi|dBE`A zrG{D`QX{>dWPHjPTi#2mCk)k7O7)CVI=8&nlr9)bmz2^KrF3n1Zz<g{ly0@4J1ywm z3LkH-l8>iv(|d5e>nG=wyi@EmD{)O@0C?JC@ZQ02A}C@bBV%9W2F9Hn3>*x}1sfUI zoHj`?GH8Jqo4Gj{IUp=iHZY6bX%{mC10w?`kj>$=i@^cHW@d2NsKVG07_q^jBVr?S zipxed5N{Vp0|O(ALq~E*1V~9F5Nzh<VC2!-!T7&*1558lCZImn6c+$@StQE<0C?JC zzyu*c=)>T{l+3`$l+3uEf$_gGlPCiNQ{MltOeu^z{{Q+f1L6Y!v6~8{0C?I=%pD9u zQ5c8u=Y8M%vEZDYE3IA13WFeb=u(`8<?cB~<;yvTIA5-9?GML(d*A`)mN5We0@)}` zveR>vgEc3)VZl{yN!;bG>dAII<;-Ybxm5q*{|~I00XOeuW9bjs$^Mdqy}#v#O|0aW zF%EK9_2lQc$eC^XkxTU-eyveaPwGq%J<t6W^C(GK3gJPCF(Dcf0+A@h_-58J($!L$ z?n33?f8)TX7uBdYH=AZoPS2b<I}7nYUzOX+EwVsIX@fdRa?<Q1?SwAayG23M)Nzq= zOv55I(R*>b`yxK3QDzJ3r*_WE?a0pf_HMQ<xvpH`>Y5kp+*X5C5eBPj!PGGc_2|8< zxz~Ng-*DyKs-VOqru#+c_|w#MbXwQ7S=x|2+Gisf&Kf_u$-ePPD7#~J&2dz_kkK`J zt~NZUm#fjGUGj%ZnJAiN&hHe{)kCOl#Uw=TQZBsou8TQW6f&mia7>eui=BA96U6lY zz1ya3#O$q??8ds>-semj$I_sGMK<)JEQGSxYWcKsW$?zk{3Um5G8ljT2j^I!#1<7C z7RV4lI6{hs)D99kCuk~a%SX5X!>hIcVTx2Rqmasog&8U~h`x1hqx<T7EZj&mR$co@ z1<k=&O8-dkOz<u4q;63PuGKYwrCx)@e~nyp6$Uc3K+swV&pOR$_8#b4x8|YWuk|;| zdN|ENGS}$W{pF_PpYK|IPEqSISi&NR|D)7rnEf8k(D;XMayap6ud;t?4kpKlMCmja z>BhY7X#*BLy}laZ5>~qT2QOt1$(bP&#vgc01w9C=^+g!8SL9Mt-JsvkT??M88)-Jy z?jgofCpxh>a2_8x#GnuF-G<RmlYKguV6l<U32$+aT)DUHSe@9%mEwRNBH@FaQgBO- zN2+@$5d<5Zp1<z9g^Bc!|EA|Zn*?jA0C?JL!Gi$;0RRBN>$7d!=6qr9TA(`t@O?S? z=Yx<ySVUAzTtZSxT1Hk*UO`bwSw&S%T|-k#TSr$<-@wqw*u>P#+``hz+Q!z--oeqy z*~Qh(-NTO{csM`+0KhQ%{n>7qY}?i>-CLn+6VxV|oAh3UC^6zBNRlE=hAcVq6ev=n zOob{n>NIH5qD_Y`J^Bn7GGfexDKq9QSh8ZxMg|$#vE|K0Ca(NsmPJ+`Jb7{BBO3?V z<&aY@x#f{pKK2|r$*+Kd3Mu?=S)LIX2m$~A#(7SxF1Fns*4F#OtEQT2kfdJk&Hs+- zKbx)I-^n0zlsPUhA|ayk6$2ktwvmX_sPH6(BPm}95>fT09pOqAkC+sXj#LDZ!GrH^ cwwtZ5v-y0QIm?{C0ql!OFaQAn0ssF157FeRS^xk5 diff --git a/docs/katex/fonts/KaTeX_Typewriter-Regular.woff2 b/docs/katex/fonts/KaTeX_Typewriter-Regular.woff2 deleted file mode 100644 index e40ab151617cb25fce5b4e739aab4f44ff3a652d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17272 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 X7IHyB_z7@g-T~&)gHWZ(YH|VqvZ`!^ diff --git a/docs/katex/katex.min.css b/docs/katex/katex.min.css deleted file mode 100644 index c0cd1451..00000000 --- a/docs/katex/katex.min.css +++ /dev/null @@ -1 +0,0 @@ -@font-face{font-family:KaTeX_AMS;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype");font-weight:400;font-style:normal}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important}.katex .katex-version:after{content:"0.11.1"}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathdefault{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-weight:700;font-style:italic}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom;position:relative}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px;min-width:2px}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{width:0;position:relative}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{display:inline-block;border:0 solid;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline{display:inline-block;width:100%;border-bottom-style:dashed}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .op-limits>.vlist-t{text-align:center}.katex .accent>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{display:block;position:absolute;width:100%;height:inherit;fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex svg path{stroke:none}.katex img{border-style:none;min-width:0;min-height:0;max-width:none;max-height:none}.katex .stretchy{width:100%;display:block;position:relative;overflow:hidden}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{width:100%;position:relative;overflow:hidden}.katex .halfarrow-left{position:absolute;left:0;width:50.2%;overflow:hidden}.katex .halfarrow-right{position:absolute;right:0;width:50.2%;overflow:hidden}.katex .brace-left{position:absolute;left:0;width:25.1%;overflow:hidden}.katex .brace-center{position:absolute;left:25%;width:50%;overflow:hidden}.katex .brace-right{position:absolute;right:0;width:25.1%;overflow:hidden}.katex .x-arrow-pad{padding:0 .5em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{box-sizing:border-box;border:.04em solid}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{text-align:left} diff --git a/docs/katex/katex.min.js b/docs/katex/katex.min.js deleted file mode 100644 index 906ce128..00000000 --- a/docs/katex/katex.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.katex=e():t.katex=e()}("undefined"!=typeof self?self:this,function(){return function(t){var e={};function r(a){if(e[a])return e[a].exports;var n=e[a]={i:a,l:!1,exports:{}};return t[a].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=t,r.c=e,r.d=function(t,e,a){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)r.d(a,n,function(e){return t[e]}.bind(null,n));return a},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=1)}([function(t,e,r){},function(t,e,r){"use strict";r.r(e);r(0);var a=function(){function t(t,e,r){this.lexer=void 0,this.start=void 0,this.end=void 0,this.lexer=t,this.start=e,this.end=r}return t.range=function(e,r){return r?e&&e.loc&&r.loc&&e.loc.lexer===r.loc.lexer?new t(e.loc.lexer,e.loc.start,r.loc.end):null:e&&e.loc},t}(),n=function(){function t(t,e){this.text=void 0,this.loc=void 0,this.text=t,this.loc=e}return t.prototype.range=function(e,r){return new t(r,a.range(this,e))},t}(),i=function t(e,r){this.position=void 0;var a,n="KaTeX parse error: "+e,i=r&&r.loc;if(i&&i.start<=i.end){var o=i.lexer.input;a=i.start;var s=i.end;a===o.length?n+=" at end of input: ":n+=" at position "+(a+1)+": ";var h=o.slice(a,s).replace(/[^]/g,"$&\u0332");n+=(a>15?"\u2026"+o.slice(a-15,a):o.slice(0,a))+h+(s+15<o.length?o.slice(s,s+15)+"\u2026":o.slice(s))}var l=new Error(n);return l.name="ParseError",l.__proto__=t.prototype,l.position=a,l};i.prototype.__proto__=Error.prototype;var o=i,s=/([A-Z])/g,h={"&":"&amp;",">":"&gt;","<":"&lt;",'"':"&quot;","'":"&#x27;"},l=/[&><"']/g;var m=function t(e){return"ordgroup"===e.type?1===e.body.length?t(e.body[0]):e:"color"===e.type?1===e.body.length?t(e.body[0]):e:"font"===e.type?t(e.body):e},c={contains:function(t,e){return-1!==t.indexOf(e)},deflt:function(t,e){return void 0===t?e:t},escape:function(t){return String(t).replace(l,function(t){return h[t]})},hyphenate:function(t){return t.replace(s,"-$1").toLowerCase()},getBaseElem:m,isCharacterBox:function(t){var e=m(t);return"mathord"===e.type||"textord"===e.type||"atom"===e.type},protocolFromUrl:function(t){var e=/^\s*([^\\\/#]*?)(?::|&#0*58|&#x0*3a)/i.exec(t);return null!=e?e[1]:"_relative"}},u=function(){function t(t){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,t=t||{},this.displayMode=c.deflt(t.displayMode,!1),this.output=c.deflt(t.output,"htmlAndMathml"),this.leqno=c.deflt(t.leqno,!1),this.fleqn=c.deflt(t.fleqn,!1),this.throwOnError=c.deflt(t.throwOnError,!0),this.errorColor=c.deflt(t.errorColor,"#cc0000"),this.macros=t.macros||{},this.minRuleThickness=Math.max(0,c.deflt(t.minRuleThickness,0)),this.colorIsTextColor=c.deflt(t.colorIsTextColor,!1),this.strict=c.deflt(t.strict,"warn"),this.trust=c.deflt(t.trust,!1),this.maxSize=Math.max(0,c.deflt(t.maxSize,1/0)),this.maxExpand=Math.max(0,c.deflt(t.maxExpand,1e3))}var e=t.prototype;return e.reportNonstrict=function(t,e,r){var a=this.strict;if("function"==typeof a&&(a=a(t,e,r)),a&&"ignore"!==a){if(!0===a||"error"===a)throw new o("LaTeX-incompatible input and strict mode is set to 'error': "+e+" ["+t+"]",r);"warn"===a?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+e+" ["+t+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+a+"': "+e+" ["+t+"]")}},e.useStrictBehavior=function(t,e,r){var a=this.strict;if("function"==typeof a)try{a=a(t,e,r)}catch(t){a="error"}return!(!a||"ignore"===a)&&(!0===a||"error"===a||("warn"===a?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+e+" ["+t+"]"),!1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+a+"': "+e+" ["+t+"]"),!1)))},e.isTrusted=function(t){t.url&&!t.protocol&&(t.protocol=c.protocolFromUrl(t.url));var e="function"==typeof this.trust?this.trust(t):this.trust;return Boolean(e)},t}(),p=function(){function t(t,e,r){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=t,this.size=e,this.cramped=r}var e=t.prototype;return e.sup=function(){return d[f[this.id]]},e.sub=function(){return d[g[this.id]]},e.fracNum=function(){return d[x[this.id]]},e.fracDen=function(){return d[v[this.id]]},e.cramp=function(){return d[b[this.id]]},e.text=function(){return d[y[this.id]]},e.isTight=function(){return this.size>=2},t}(),d=[new p(0,0,!1),new p(1,0,!0),new p(2,1,!1),new p(3,1,!0),new p(4,2,!1),new p(5,2,!0),new p(6,3,!1),new p(7,3,!0)],f=[4,5,4,5,6,7,6,7],g=[5,5,5,5,7,7,7,7],x=[2,3,4,5,6,7,6,7],v=[3,3,5,5,7,7,7,7],b=[1,1,3,3,5,5,7,7],y=[0,1,2,3,2,3,2,3],w={DISPLAY:d[0],TEXT:d[2],SCRIPT:d[4],SCRIPTSCRIPT:d[6]},k=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];var S=[];function M(t){for(var e=0;e<S.length;e+=2)if(t>=S[e]&&t<=S[e+1])return!0;return!1}k.forEach(function(t){return t.blocks.forEach(function(t){return S.push.apply(S,t)})});var z={doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:"M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z",leftmapsto:"M40 281 V448H0V74H40V241H400000v40z\nM40 281 V448H0V74H40V241H400000v40z",leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:"M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z",midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:"M399960 241 V94 h40 V428 h-40 V281 H0 v-40z\nM399960 241 V94 h40 V428 h-40 V281 H0 v-40z",rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"},A=function(){function t(t){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=t,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){for(var t=document.createDocumentFragment(),e=0;e<this.children.length;e++)t.appendChild(this.children[e].toNode());return t},e.toMarkup=function(){for(var t="",e=0;e<this.children.length;e++)t+=this.children[e].toMarkup();return t},e.toText=function(){var t=function(t){return t.toText()};return this.children.map(t).join("")},t}(),T=function(t){return t.filter(function(t){return t}).join(" ")},B=function(t,e,r){if(this.classes=t||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=r||{},e){e.style.isTight()&&this.classes.push("mtight");var a=e.getColor();a&&(this.style.color=a)}},C=function(t){var e=document.createElement(t);for(var r in e.className=T(this.classes),this.style)this.style.hasOwnProperty(r)&&(e.style[r]=this.style[r]);for(var a in this.attributes)this.attributes.hasOwnProperty(a)&&e.setAttribute(a,this.attributes[a]);for(var n=0;n<this.children.length;n++)e.appendChild(this.children[n].toNode());return e},q=function(t){var e="<"+t;this.classes.length&&(e+=' class="'+c.escape(T(this.classes))+'"');var r="";for(var a in this.style)this.style.hasOwnProperty(a)&&(r+=c.hyphenate(a)+":"+this.style[a]+";");for(var n in r&&(e+=' style="'+c.escape(r)+'"'),this.attributes)this.attributes.hasOwnProperty(n)&&(e+=" "+n+'="'+c.escape(this.attributes[n])+'"');e+=">";for(var i=0;i<this.children.length;i++)e+=this.children[i].toMarkup();return e+="</"+t+">"},N=function(){function t(t,e,r,a){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,B.call(this,t,r,a),this.children=e||[]}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){return C.call(this,"span")},e.toMarkup=function(){return q.call(this,"span")},t}(),I=function(){function t(t,e,r,a){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,B.call(this,e,a),this.children=r||[],this.setAttribute("href",t)}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){return C.call(this,"a")},e.toMarkup=function(){return q.call(this,"a")},t}(),R=function(){function t(t,e,r){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=e,this.src=t,this.classes=["mord"],this.style=r}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){var t=document.createElement("img");for(var e in t.src=this.src,t.alt=this.alt,t.className="mord",this.style)this.style.hasOwnProperty(e)&&(t.style[e]=this.style[e]);return t},e.toMarkup=function(){var t="<img src='"+this.src+" 'alt='"+this.alt+"' ",e="";for(var r in this.style)this.style.hasOwnProperty(r)&&(e+=c.hyphenate(r)+":"+this.style[r]+";");return e&&(t+=' style="'+c.escape(e)+'"'),t+="'/>"},t}(),O={"\xee":"\u0131\u0302","\xef":"\u0131\u0308","\xed":"\u0131\u0301","\xec":"\u0131\u0300"},E=function(){function t(t,e,r,a,n,i,o,s){this.text=void 0,this.height=void 0,this.depth=void 0,this.italic=void 0,this.skew=void 0,this.width=void 0,this.maxFontSize=void 0,this.classes=void 0,this.style=void 0,this.text=t,this.height=e||0,this.depth=r||0,this.italic=a||0,this.skew=n||0,this.width=i||0,this.classes=o||[],this.style=s||{},this.maxFontSize=0;var h=function(t){for(var e=0;e<k.length;e++)for(var r=k[e],a=0;a<r.blocks.length;a++){var n=r.blocks[a];if(t>=n[0]&&t<=n[1])return r.name}return null}(this.text.charCodeAt(0));h&&this.classes.push(h+"_fallback"),/[\xee\xef\xed\xec]/.test(this.text)&&(this.text=O[this.text])}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){var t=document.createTextNode(this.text),e=null;for(var r in this.italic>0&&((e=document.createElement("span")).style.marginRight=this.italic+"em"),this.classes.length>0&&((e=e||document.createElement("span")).className=T(this.classes)),this.style)this.style.hasOwnProperty(r)&&((e=e||document.createElement("span")).style[r]=this.style[r]);return e?(e.appendChild(t),e):t},e.toMarkup=function(){var t=!1,e="<span";this.classes.length&&(t=!0,e+=' class="',e+=c.escape(T(this.classes)),e+='"');var r="";for(var a in this.italic>0&&(r+="margin-right:"+this.italic+"em;"),this.style)this.style.hasOwnProperty(a)&&(r+=c.hyphenate(a)+":"+this.style[a]+";");r&&(t=!0,e+=' style="'+c.escape(r)+'"');var n=c.escape(this.text);return t?(e+=">",e+=n,e+="</span>"):n},t}(),L=function(){function t(t,e){this.children=void 0,this.attributes=void 0,this.children=t||[],this.attributes=e||{}}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","svg");for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);for(var r=0;r<this.children.length;r++)t.appendChild(this.children[r].toNode());return t},e.toMarkup=function(){var t="<svg";for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&(t+=" "+e+"='"+this.attributes[e]+"'");t+=">";for(var r=0;r<this.children.length;r++)t+=this.children[r].toMarkup();return t+="</svg>"},t}(),H=function(){function t(t,e){this.pathName=void 0,this.alternate=void 0,this.pathName=t,this.alternate=e}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","path");return this.alternate?t.setAttribute("d",this.alternate):t.setAttribute("d",z[this.pathName]),t},e.toMarkup=function(){return this.alternate?"<path d='"+this.alternate+"'/>":"<path d='"+z[this.pathName]+"'/>"},t}(),P=function(){function t(t){this.attributes=void 0,this.attributes=t||{}}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","line");for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);return t},e.toMarkup=function(){var t="<line";for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&(t+=" "+e+"='"+this.attributes[e]+"'");return t+="/>"},t}();function D(t){if(t instanceof E)return t;throw new Error("Expected symbolNode but got "+String(t)+".")}var F={"AMS-Regular":{65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473]},"Fraktur-Regular":{33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],163:[0,.69444,0,0,.86853],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],163:[0,.69444,0,0,.76909],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],305:[0,.43056,0,.02778,.32246],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],567:[.19444,.43056,0,.08334,.38403],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.12,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,1],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.67,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.9,0,0,.278],8943:[-.19,.31,0,0,1.172],8945:[-.1,.82,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.744,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.744,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333]},"Math-Italic":{65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059]},"Math-Regular":{65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059]},"SansSerif-Bold":{33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212]},"Size1-Regular":{40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}},V={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},U={"\xc5":"A","\xc7":"C","\xd0":"D","\xde":"o","\xe5":"a","\xe7":"c","\xf0":"d","\xfe":"o","\u0410":"A","\u0411":"B","\u0412":"B","\u0413":"F","\u0414":"A","\u0415":"E","\u0416":"K","\u0417":"3","\u0418":"N","\u0419":"N","\u041a":"K","\u041b":"N","\u041c":"M","\u041d":"H","\u041e":"O","\u041f":"N","\u0420":"P","\u0421":"C","\u0422":"T","\u0423":"y","\u0424":"O","\u0425":"X","\u0426":"U","\u0427":"h","\u0428":"W","\u0429":"W","\u042a":"B","\u042b":"X","\u042c":"B","\u042d":"3","\u042e":"X","\u042f":"R","\u0430":"a","\u0431":"b","\u0432":"a","\u0433":"r","\u0434":"y","\u0435":"e","\u0436":"m","\u0437":"e","\u0438":"n","\u0439":"n","\u043a":"n","\u043b":"n","\u043c":"m","\u043d":"n","\u043e":"o","\u043f":"n","\u0440":"p","\u0441":"c","\u0442":"o","\u0443":"y","\u0444":"b","\u0445":"x","\u0446":"n","\u0447":"n","\u0448":"w","\u0449":"w","\u044a":"a","\u044b":"m","\u044c":"a","\u044d":"e","\u044e":"m","\u044f":"r"};function G(t,e,r){if(!F[e])throw new Error("Font metrics not found for font: "+e+".");var a=t.charCodeAt(0),n=F[e][a];if(!n&&t[0]in U&&(a=U[t[0]].charCodeAt(0),n=F[e][a]),n||"text"!==r||M(a)&&(n=F[e][77]),n)return{depth:n[0],height:n[1],italic:n[2],skew:n[3],width:n[4]}}var Y={};var W={bin:1,close:1,inner:1,open:1,punct:1,rel:1},X={"accent-token":1,mathord:1,"op-token":1,spacing:1,textord:1},_={math:{},text:{}},j=_;function $(t,e,r,a,n,i){_[t][n]={font:e,group:r,replace:a},i&&a&&(_[t][a]=_[t][n])}var Z="main",K="ams",J="bin",Q="mathord",tt="op-token",et="rel";$("math",Z,et,"\u2261","\\equiv",!0),$("math",Z,et,"\u227a","\\prec",!0),$("math",Z,et,"\u227b","\\succ",!0),$("math",Z,et,"\u223c","\\sim",!0),$("math",Z,et,"\u22a5","\\perp"),$("math",Z,et,"\u2aaf","\\preceq",!0),$("math",Z,et,"\u2ab0","\\succeq",!0),$("math",Z,et,"\u2243","\\simeq",!0),$("math",Z,et,"\u2223","\\mid",!0),$("math",Z,et,"\u226a","\\ll",!0),$("math",Z,et,"\u226b","\\gg",!0),$("math",Z,et,"\u224d","\\asymp",!0),$("math",Z,et,"\u2225","\\parallel"),$("math",Z,et,"\u22c8","\\bowtie",!0),$("math",Z,et,"\u2323","\\smile",!0),$("math",Z,et,"\u2291","\\sqsubseteq",!0),$("math",Z,et,"\u2292","\\sqsupseteq",!0),$("math",Z,et,"\u2250","\\doteq",!0),$("math",Z,et,"\u2322","\\frown",!0),$("math",Z,et,"\u220b","\\ni",!0),$("math",Z,et,"\u221d","\\propto",!0),$("math",Z,et,"\u22a2","\\vdash",!0),$("math",Z,et,"\u22a3","\\dashv",!0),$("math",Z,et,"\u220b","\\owns"),$("math",Z,"punct",".","\\ldotp"),$("math",Z,"punct","\u22c5","\\cdotp"),$("math",Z,"textord","#","\\#"),$("text",Z,"textord","#","\\#"),$("math",Z,"textord","&","\\&"),$("text",Z,"textord","&","\\&"),$("math",Z,"textord","\u2135","\\aleph",!0),$("math",Z,"textord","\u2200","\\forall",!0),$("math",Z,"textord","\u210f","\\hbar",!0),$("math",Z,"textord","\u2203","\\exists",!0),$("math",Z,"textord","\u2207","\\nabla",!0),$("math",Z,"textord","\u266d","\\flat",!0),$("math",Z,"textord","\u2113","\\ell",!0),$("math",Z,"textord","\u266e","\\natural",!0),$("math",Z,"textord","\u2663","\\clubsuit",!0),$("math",Z,"textord","\u2118","\\wp",!0),$("math",Z,"textord","\u266f","\\sharp",!0),$("math",Z,"textord","\u2662","\\diamondsuit",!0),$("math",Z,"textord","\u211c","\\Re",!0),$("math",Z,"textord","\u2661","\\heartsuit",!0),$("math",Z,"textord","\u2111","\\Im",!0),$("math",Z,"textord","\u2660","\\spadesuit",!0),$("text",Z,"textord","\xa7","\\S",!0),$("text",Z,"textord","\xb6","\\P",!0),$("math",Z,"textord","\u2020","\\dag"),$("text",Z,"textord","\u2020","\\dag"),$("text",Z,"textord","\u2020","\\textdagger"),$("math",Z,"textord","\u2021","\\ddag"),$("text",Z,"textord","\u2021","\\ddag"),$("text",Z,"textord","\u2021","\\textdaggerdbl"),$("math",Z,"close","\u23b1","\\rmoustache",!0),$("math",Z,"open","\u23b0","\\lmoustache",!0),$("math",Z,"close","\u27ef","\\rgroup",!0),$("math",Z,"open","\u27ee","\\lgroup",!0),$("math",Z,J,"\u2213","\\mp",!0),$("math",Z,J,"\u2296","\\ominus",!0),$("math",Z,J,"\u228e","\\uplus",!0),$("math",Z,J,"\u2293","\\sqcap",!0),$("math",Z,J,"\u2217","\\ast"),$("math",Z,J,"\u2294","\\sqcup",!0),$("math",Z,J,"\u25ef","\\bigcirc"),$("math",Z,J,"\u2219","\\bullet"),$("math",Z,J,"\u2021","\\ddagger"),$("math",Z,J,"\u2240","\\wr",!0),$("math",Z,J,"\u2a3f","\\amalg"),$("math",Z,J,"&","\\And"),$("math",Z,et,"\u27f5","\\longleftarrow",!0),$("math",Z,et,"\u21d0","\\Leftarrow",!0),$("math",Z,et,"\u27f8","\\Longleftarrow",!0),$("math",Z,et,"\u27f6","\\longrightarrow",!0),$("math",Z,et,"\u21d2","\\Rightarrow",!0),$("math",Z,et,"\u27f9","\\Longrightarrow",!0),$("math",Z,et,"\u2194","\\leftrightarrow",!0),$("math",Z,et,"\u27f7","\\longleftrightarrow",!0),$("math",Z,et,"\u21d4","\\Leftrightarrow",!0),$("math",Z,et,"\u27fa","\\Longleftrightarrow",!0),$("math",Z,et,"\u21a6","\\mapsto",!0),$("math",Z,et,"\u27fc","\\longmapsto",!0),$("math",Z,et,"\u2197","\\nearrow",!0),$("math",Z,et,"\u21a9","\\hookleftarrow",!0),$("math",Z,et,"\u21aa","\\hookrightarrow",!0),$("math",Z,et,"\u2198","\\searrow",!0),$("math",Z,et,"\u21bc","\\leftharpoonup",!0),$("math",Z,et,"\u21c0","\\rightharpoonup",!0),$("math",Z,et,"\u2199","\\swarrow",!0),$("math",Z,et,"\u21bd","\\leftharpoondown",!0),$("math",Z,et,"\u21c1","\\rightharpoondown",!0),$("math",Z,et,"\u2196","\\nwarrow",!0),$("math",Z,et,"\u21cc","\\rightleftharpoons",!0),$("math",K,et,"\u226e","\\nless",!0),$("math",K,et,"\ue010","\\@nleqslant"),$("math",K,et,"\ue011","\\@nleqq"),$("math",K,et,"\u2a87","\\lneq",!0),$("math",K,et,"\u2268","\\lneqq",!0),$("math",K,et,"\ue00c","\\@lvertneqq"),$("math",K,et,"\u22e6","\\lnsim",!0),$("math",K,et,"\u2a89","\\lnapprox",!0),$("math",K,et,"\u2280","\\nprec",!0),$("math",K,et,"\u22e0","\\npreceq",!0),$("math",K,et,"\u22e8","\\precnsim",!0),$("math",K,et,"\u2ab9","\\precnapprox",!0),$("math",K,et,"\u2241","\\nsim",!0),$("math",K,et,"\ue006","\\@nshortmid"),$("math",K,et,"\u2224","\\nmid",!0),$("math",K,et,"\u22ac","\\nvdash",!0),$("math",K,et,"\u22ad","\\nvDash",!0),$("math",K,et,"\u22ea","\\ntriangleleft"),$("math",K,et,"\u22ec","\\ntrianglelefteq",!0),$("math",K,et,"\u228a","\\subsetneq",!0),$("math",K,et,"\ue01a","\\@varsubsetneq"),$("math",K,et,"\u2acb","\\subsetneqq",!0),$("math",K,et,"\ue017","\\@varsubsetneqq"),$("math",K,et,"\u226f","\\ngtr",!0),$("math",K,et,"\ue00f","\\@ngeqslant"),$("math",K,et,"\ue00e","\\@ngeqq"),$("math",K,et,"\u2a88","\\gneq",!0),$("math",K,et,"\u2269","\\gneqq",!0),$("math",K,et,"\ue00d","\\@gvertneqq"),$("math",K,et,"\u22e7","\\gnsim",!0),$("math",K,et,"\u2a8a","\\gnapprox",!0),$("math",K,et,"\u2281","\\nsucc",!0),$("math",K,et,"\u22e1","\\nsucceq",!0),$("math",K,et,"\u22e9","\\succnsim",!0),$("math",K,et,"\u2aba","\\succnapprox",!0),$("math",K,et,"\u2246","\\ncong",!0),$("math",K,et,"\ue007","\\@nshortparallel"),$("math",K,et,"\u2226","\\nparallel",!0),$("math",K,et,"\u22af","\\nVDash",!0),$("math",K,et,"\u22eb","\\ntriangleright"),$("math",K,et,"\u22ed","\\ntrianglerighteq",!0),$("math",K,et,"\ue018","\\@nsupseteqq"),$("math",K,et,"\u228b","\\supsetneq",!0),$("math",K,et,"\ue01b","\\@varsupsetneq"),$("math",K,et,"\u2acc","\\supsetneqq",!0),$("math",K,et,"\ue019","\\@varsupsetneqq"),$("math",K,et,"\u22ae","\\nVdash",!0),$("math",K,et,"\u2ab5","\\precneqq",!0),$("math",K,et,"\u2ab6","\\succneqq",!0),$("math",K,et,"\ue016","\\@nsubseteqq"),$("math",K,J,"\u22b4","\\unlhd"),$("math",K,J,"\u22b5","\\unrhd"),$("math",K,et,"\u219a","\\nleftarrow",!0),$("math",K,et,"\u219b","\\nrightarrow",!0),$("math",K,et,"\u21cd","\\nLeftarrow",!0),$("math",K,et,"\u21cf","\\nRightarrow",!0),$("math",K,et,"\u21ae","\\nleftrightarrow",!0),$("math",K,et,"\u21ce","\\nLeftrightarrow",!0),$("math",K,et,"\u25b3","\\vartriangle"),$("math",K,"textord","\u210f","\\hslash"),$("math",K,"textord","\u25bd","\\triangledown"),$("math",K,"textord","\u25ca","\\lozenge"),$("math",K,"textord","\u24c8","\\circledS"),$("math",K,"textord","\xae","\\circledR"),$("text",K,"textord","\xae","\\circledR"),$("math",K,"textord","\u2221","\\measuredangle",!0),$("math",K,"textord","\u2204","\\nexists"),$("math",K,"textord","\u2127","\\mho"),$("math",K,"textord","\u2132","\\Finv",!0),$("math",K,"textord","\u2141","\\Game",!0),$("math",K,"textord","\u2035","\\backprime"),$("math",K,"textord","\u25b2","\\blacktriangle"),$("math",K,"textord","\u25bc","\\blacktriangledown"),$("math",K,"textord","\u25a0","\\blacksquare"),$("math",K,"textord","\u29eb","\\blacklozenge"),$("math",K,"textord","\u2605","\\bigstar"),$("math",K,"textord","\u2222","\\sphericalangle",!0),$("math",K,"textord","\u2201","\\complement",!0),$("math",K,"textord","\xf0","\\eth",!0),$("math",K,"textord","\u2571","\\diagup"),$("math",K,"textord","\u2572","\\diagdown"),$("math",K,"textord","\u25a1","\\square"),$("math",K,"textord","\u25a1","\\Box"),$("math",K,"textord","\u25ca","\\Diamond"),$("math",K,"textord","\xa5","\\yen",!0),$("text",K,"textord","\xa5","\\yen",!0),$("math",K,"textord","\u2713","\\checkmark",!0),$("text",K,"textord","\u2713","\\checkmark"),$("math",K,"textord","\u2136","\\beth",!0),$("math",K,"textord","\u2138","\\daleth",!0),$("math",K,"textord","\u2137","\\gimel",!0),$("math",K,"textord","\u03dd","\\digamma",!0),$("math",K,"textord","\u03f0","\\varkappa"),$("math",K,"open","\u250c","\\ulcorner",!0),$("math",K,"close","\u2510","\\urcorner",!0),$("math",K,"open","\u2514","\\llcorner",!0),$("math",K,"close","\u2518","\\lrcorner",!0),$("math",K,et,"\u2266","\\leqq",!0),$("math",K,et,"\u2a7d","\\leqslant",!0),$("math",K,et,"\u2a95","\\eqslantless",!0),$("math",K,et,"\u2272","\\lesssim",!0),$("math",K,et,"\u2a85","\\lessapprox",!0),$("math",K,et,"\u224a","\\approxeq",!0),$("math",K,J,"\u22d6","\\lessdot"),$("math",K,et,"\u22d8","\\lll",!0),$("math",K,et,"\u2276","\\lessgtr",!0),$("math",K,et,"\u22da","\\lesseqgtr",!0),$("math",K,et,"\u2a8b","\\lesseqqgtr",!0),$("math",K,et,"\u2251","\\doteqdot"),$("math",K,et,"\u2253","\\risingdotseq",!0),$("math",K,et,"\u2252","\\fallingdotseq",!0),$("math",K,et,"\u223d","\\backsim",!0),$("math",K,et,"\u22cd","\\backsimeq",!0),$("math",K,et,"\u2ac5","\\subseteqq",!0),$("math",K,et,"\u22d0","\\Subset",!0),$("math",K,et,"\u228f","\\sqsubset",!0),$("math",K,et,"\u227c","\\preccurlyeq",!0),$("math",K,et,"\u22de","\\curlyeqprec",!0),$("math",K,et,"\u227e","\\precsim",!0),$("math",K,et,"\u2ab7","\\precapprox",!0),$("math",K,et,"\u22b2","\\vartriangleleft"),$("math",K,et,"\u22b4","\\trianglelefteq"),$("math",K,et,"\u22a8","\\vDash",!0),$("math",K,et,"\u22aa","\\Vvdash",!0),$("math",K,et,"\u2323","\\smallsmile"),$("math",K,et,"\u2322","\\smallfrown"),$("math",K,et,"\u224f","\\bumpeq",!0),$("math",K,et,"\u224e","\\Bumpeq",!0),$("math",K,et,"\u2267","\\geqq",!0),$("math",K,et,"\u2a7e","\\geqslant",!0),$("math",K,et,"\u2a96","\\eqslantgtr",!0),$("math",K,et,"\u2273","\\gtrsim",!0),$("math",K,et,"\u2a86","\\gtrapprox",!0),$("math",K,J,"\u22d7","\\gtrdot"),$("math",K,et,"\u22d9","\\ggg",!0),$("math",K,et,"\u2277","\\gtrless",!0),$("math",K,et,"\u22db","\\gtreqless",!0),$("math",K,et,"\u2a8c","\\gtreqqless",!0),$("math",K,et,"\u2256","\\eqcirc",!0),$("math",K,et,"\u2257","\\circeq",!0),$("math",K,et,"\u225c","\\triangleq",!0),$("math",K,et,"\u223c","\\thicksim"),$("math",K,et,"\u2248","\\thickapprox"),$("math",K,et,"\u2ac6","\\supseteqq",!0),$("math",K,et,"\u22d1","\\Supset",!0),$("math",K,et,"\u2290","\\sqsupset",!0),$("math",K,et,"\u227d","\\succcurlyeq",!0),$("math",K,et,"\u22df","\\curlyeqsucc",!0),$("math",K,et,"\u227f","\\succsim",!0),$("math",K,et,"\u2ab8","\\succapprox",!0),$("math",K,et,"\u22b3","\\vartriangleright"),$("math",K,et,"\u22b5","\\trianglerighteq"),$("math",K,et,"\u22a9","\\Vdash",!0),$("math",K,et,"\u2223","\\shortmid"),$("math",K,et,"\u2225","\\shortparallel"),$("math",K,et,"\u226c","\\between",!0),$("math",K,et,"\u22d4","\\pitchfork",!0),$("math",K,et,"\u221d","\\varpropto"),$("math",K,et,"\u25c0","\\blacktriangleleft"),$("math",K,et,"\u2234","\\therefore",!0),$("math",K,et,"\u220d","\\backepsilon"),$("math",K,et,"\u25b6","\\blacktriangleright"),$("math",K,et,"\u2235","\\because",!0),$("math",K,et,"\u22d8","\\llless"),$("math",K,et,"\u22d9","\\gggtr"),$("math",K,J,"\u22b2","\\lhd"),$("math",K,J,"\u22b3","\\rhd"),$("math",K,et,"\u2242","\\eqsim",!0),$("math",Z,et,"\u22c8","\\Join"),$("math",K,et,"\u2251","\\Doteq",!0),$("math",K,J,"\u2214","\\dotplus",!0),$("math",K,J,"\u2216","\\smallsetminus"),$("math",K,J,"\u22d2","\\Cap",!0),$("math",K,J,"\u22d3","\\Cup",!0),$("math",K,J,"\u2a5e","\\doublebarwedge",!0),$("math",K,J,"\u229f","\\boxminus",!0),$("math",K,J,"\u229e","\\boxplus",!0),$("math",K,J,"\u22c7","\\divideontimes",!0),$("math",K,J,"\u22c9","\\ltimes",!0),$("math",K,J,"\u22ca","\\rtimes",!0),$("math",K,J,"\u22cb","\\leftthreetimes",!0),$("math",K,J,"\u22cc","\\rightthreetimes",!0),$("math",K,J,"\u22cf","\\curlywedge",!0),$("math",K,J,"\u22ce","\\curlyvee",!0),$("math",K,J,"\u229d","\\circleddash",!0),$("math",K,J,"\u229b","\\circledast",!0),$("math",K,J,"\u22c5","\\centerdot"),$("math",K,J,"\u22ba","\\intercal",!0),$("math",K,J,"\u22d2","\\doublecap"),$("math",K,J,"\u22d3","\\doublecup"),$("math",K,J,"\u22a0","\\boxtimes",!0),$("math",K,et,"\u21e2","\\dashrightarrow",!0),$("math",K,et,"\u21e0","\\dashleftarrow",!0),$("math",K,et,"\u21c7","\\leftleftarrows",!0),$("math",K,et,"\u21c6","\\leftrightarrows",!0),$("math",K,et,"\u21da","\\Lleftarrow",!0),$("math",K,et,"\u219e","\\twoheadleftarrow",!0),$("math",K,et,"\u21a2","\\leftarrowtail",!0),$("math",K,et,"\u21ab","\\looparrowleft",!0),$("math",K,et,"\u21cb","\\leftrightharpoons",!0),$("math",K,et,"\u21b6","\\curvearrowleft",!0),$("math",K,et,"\u21ba","\\circlearrowleft",!0),$("math",K,et,"\u21b0","\\Lsh",!0),$("math",K,et,"\u21c8","\\upuparrows",!0),$("math",K,et,"\u21bf","\\upharpoonleft",!0),$("math",K,et,"\u21c3","\\downharpoonleft",!0),$("math",K,et,"\u22b8","\\multimap",!0),$("math",K,et,"\u21ad","\\leftrightsquigarrow",!0),$("math",K,et,"\u21c9","\\rightrightarrows",!0),$("math",K,et,"\u21c4","\\rightleftarrows",!0),$("math",K,et,"\u21a0","\\twoheadrightarrow",!0),$("math",K,et,"\u21a3","\\rightarrowtail",!0),$("math",K,et,"\u21ac","\\looparrowright",!0),$("math",K,et,"\u21b7","\\curvearrowright",!0),$("math",K,et,"\u21bb","\\circlearrowright",!0),$("math",K,et,"\u21b1","\\Rsh",!0),$("math",K,et,"\u21ca","\\downdownarrows",!0),$("math",K,et,"\u21be","\\upharpoonright",!0),$("math",K,et,"\u21c2","\\downharpoonright",!0),$("math",K,et,"\u21dd","\\rightsquigarrow",!0),$("math",K,et,"\u21dd","\\leadsto"),$("math",K,et,"\u21db","\\Rrightarrow",!0),$("math",K,et,"\u21be","\\restriction"),$("math",Z,"textord","\u2018","`"),$("math",Z,"textord","$","\\$"),$("text",Z,"textord","$","\\$"),$("text",Z,"textord","$","\\textdollar"),$("math",Z,"textord","%","\\%"),$("text",Z,"textord","%","\\%"),$("math",Z,"textord","_","\\_"),$("text",Z,"textord","_","\\_"),$("text",Z,"textord","_","\\textunderscore"),$("math",Z,"textord","\u2220","\\angle",!0),$("math",Z,"textord","\u221e","\\infty",!0),$("math",Z,"textord","\u2032","\\prime"),$("math",Z,"textord","\u25b3","\\triangle"),$("math",Z,"textord","\u0393","\\Gamma",!0),$("math",Z,"textord","\u0394","\\Delta",!0),$("math",Z,"textord","\u0398","\\Theta",!0),$("math",Z,"textord","\u039b","\\Lambda",!0),$("math",Z,"textord","\u039e","\\Xi",!0),$("math",Z,"textord","\u03a0","\\Pi",!0),$("math",Z,"textord","\u03a3","\\Sigma",!0),$("math",Z,"textord","\u03a5","\\Upsilon",!0),$("math",Z,"textord","\u03a6","\\Phi",!0),$("math",Z,"textord","\u03a8","\\Psi",!0),$("math",Z,"textord","\u03a9","\\Omega",!0),$("math",Z,"textord","A","\u0391"),$("math",Z,"textord","B","\u0392"),$("math",Z,"textord","E","\u0395"),$("math",Z,"textord","Z","\u0396"),$("math",Z,"textord","H","\u0397"),$("math",Z,"textord","I","\u0399"),$("math",Z,"textord","K","\u039a"),$("math",Z,"textord","M","\u039c"),$("math",Z,"textord","N","\u039d"),$("math",Z,"textord","O","\u039f"),$("math",Z,"textord","P","\u03a1"),$("math",Z,"textord","T","\u03a4"),$("math",Z,"textord","X","\u03a7"),$("math",Z,"textord","\xac","\\neg",!0),$("math",Z,"textord","\xac","\\lnot"),$("math",Z,"textord","\u22a4","\\top"),$("math",Z,"textord","\u22a5","\\bot"),$("math",Z,"textord","\u2205","\\emptyset"),$("math",K,"textord","\u2205","\\varnothing"),$("math",Z,Q,"\u03b1","\\alpha",!0),$("math",Z,Q,"\u03b2","\\beta",!0),$("math",Z,Q,"\u03b3","\\gamma",!0),$("math",Z,Q,"\u03b4","\\delta",!0),$("math",Z,Q,"\u03f5","\\epsilon",!0),$("math",Z,Q,"\u03b6","\\zeta",!0),$("math",Z,Q,"\u03b7","\\eta",!0),$("math",Z,Q,"\u03b8","\\theta",!0),$("math",Z,Q,"\u03b9","\\iota",!0),$("math",Z,Q,"\u03ba","\\kappa",!0),$("math",Z,Q,"\u03bb","\\lambda",!0),$("math",Z,Q,"\u03bc","\\mu",!0),$("math",Z,Q,"\u03bd","\\nu",!0),$("math",Z,Q,"\u03be","\\xi",!0),$("math",Z,Q,"\u03bf","\\omicron",!0),$("math",Z,Q,"\u03c0","\\pi",!0),$("math",Z,Q,"\u03c1","\\rho",!0),$("math",Z,Q,"\u03c3","\\sigma",!0),$("math",Z,Q,"\u03c4","\\tau",!0),$("math",Z,Q,"\u03c5","\\upsilon",!0),$("math",Z,Q,"\u03d5","\\phi",!0),$("math",Z,Q,"\u03c7","\\chi",!0),$("math",Z,Q,"\u03c8","\\psi",!0),$("math",Z,Q,"\u03c9","\\omega",!0),$("math",Z,Q,"\u03b5","\\varepsilon",!0),$("math",Z,Q,"\u03d1","\\vartheta",!0),$("math",Z,Q,"\u03d6","\\varpi",!0),$("math",Z,Q,"\u03f1","\\varrho",!0),$("math",Z,Q,"\u03c2","\\varsigma",!0),$("math",Z,Q,"\u03c6","\\varphi",!0),$("math",Z,J,"\u2217","*"),$("math",Z,J,"+","+"),$("math",Z,J,"\u2212","-"),$("math",Z,J,"\u22c5","\\cdot",!0),$("math",Z,J,"\u2218","\\circ"),$("math",Z,J,"\xf7","\\div",!0),$("math",Z,J,"\xb1","\\pm",!0),$("math",Z,J,"\xd7","\\times",!0),$("math",Z,J,"\u2229","\\cap",!0),$("math",Z,J,"\u222a","\\cup",!0),$("math",Z,J,"\u2216","\\setminus"),$("math",Z,J,"\u2227","\\land"),$("math",Z,J,"\u2228","\\lor"),$("math",Z,J,"\u2227","\\wedge",!0),$("math",Z,J,"\u2228","\\vee",!0),$("math",Z,"textord","\u221a","\\surd"),$("math",Z,"open","(","("),$("math",Z,"open","[","["),$("math",Z,"open","\u27e8","\\langle",!0),$("math",Z,"open","\u2223","\\lvert"),$("math",Z,"open","\u2225","\\lVert"),$("math",Z,"close",")",")"),$("math",Z,"close","]","]"),$("math",Z,"close","?","?"),$("math",Z,"close","!","!"),$("math",Z,"close","\u27e9","\\rangle",!0),$("math",Z,"close","\u2223","\\rvert"),$("math",Z,"close","\u2225","\\rVert"),$("math",Z,et,"=","="),$("math",Z,et,"<","<"),$("math",Z,et,">",">"),$("math",Z,et,":",":"),$("math",Z,et,"\u2248","\\approx",!0),$("math",Z,et,"\u2245","\\cong",!0),$("math",Z,et,"\u2265","\\ge"),$("math",Z,et,"\u2265","\\geq",!0),$("math",Z,et,"\u2190","\\gets"),$("math",Z,et,">","\\gt"),$("math",Z,et,"\u2208","\\in",!0),$("math",Z,et,"\ue020","\\@not"),$("math",Z,et,"\u2282","\\subset",!0),$("math",Z,et,"\u2283","\\supset",!0),$("math",Z,et,"\u2286","\\subseteq",!0),$("math",Z,et,"\u2287","\\supseteq",!0),$("math",K,et,"\u2288","\\nsubseteq",!0),$("math",K,et,"\u2289","\\nsupseteq",!0),$("math",Z,et,"\u22a8","\\models"),$("math",Z,et,"\u2190","\\leftarrow",!0),$("math",Z,et,"\u2264","\\le"),$("math",Z,et,"\u2264","\\leq",!0),$("math",Z,et,"<","\\lt"),$("math",Z,et,"\u2192","\\rightarrow",!0),$("math",Z,et,"\u2192","\\to"),$("math",K,et,"\u2271","\\ngeq",!0),$("math",K,et,"\u2270","\\nleq",!0),$("math",Z,"spacing","\xa0","\\ "),$("math",Z,"spacing","\xa0","~"),$("math",Z,"spacing","\xa0","\\space"),$("math",Z,"spacing","\xa0","\\nobreakspace"),$("text",Z,"spacing","\xa0","\\ "),$("text",Z,"spacing","\xa0","~"),$("text",Z,"spacing","\xa0","\\space"),$("text",Z,"spacing","\xa0","\\nobreakspace"),$("math",Z,"spacing",null,"\\nobreak"),$("math",Z,"spacing",null,"\\allowbreak"),$("math",Z,"punct",",",","),$("math",Z,"punct",";",";"),$("math",K,J,"\u22bc","\\barwedge",!0),$("math",K,J,"\u22bb","\\veebar",!0),$("math",Z,J,"\u2299","\\odot",!0),$("math",Z,J,"\u2295","\\oplus",!0),$("math",Z,J,"\u2297","\\otimes",!0),$("math",Z,"textord","\u2202","\\partial",!0),$("math",Z,J,"\u2298","\\oslash",!0),$("math",K,J,"\u229a","\\circledcirc",!0),$("math",K,J,"\u22a1","\\boxdot",!0),$("math",Z,J,"\u25b3","\\bigtriangleup"),$("math",Z,J,"\u25bd","\\bigtriangledown"),$("math",Z,J,"\u2020","\\dagger"),$("math",Z,J,"\u22c4","\\diamond"),$("math",Z,J,"\u22c6","\\star"),$("math",Z,J,"\u25c3","\\triangleleft"),$("math",Z,J,"\u25b9","\\triangleright"),$("math",Z,"open","{","\\{"),$("text",Z,"textord","{","\\{"),$("text",Z,"textord","{","\\textbraceleft"),$("math",Z,"close","}","\\}"),$("text",Z,"textord","}","\\}"),$("text",Z,"textord","}","\\textbraceright"),$("math",Z,"open","{","\\lbrace"),$("math",Z,"close","}","\\rbrace"),$("math",Z,"open","[","\\lbrack"),$("text",Z,"textord","[","\\lbrack"),$("math",Z,"close","]","\\rbrack"),$("text",Z,"textord","]","\\rbrack"),$("math",Z,"open","(","\\lparen"),$("math",Z,"close",")","\\rparen"),$("text",Z,"textord","<","\\textless"),$("text",Z,"textord",">","\\textgreater"),$("math",Z,"open","\u230a","\\lfloor",!0),$("math",Z,"close","\u230b","\\rfloor",!0),$("math",Z,"open","\u2308","\\lceil",!0),$("math",Z,"close","\u2309","\\rceil",!0),$("math",Z,"textord","\\","\\backslash"),$("math",Z,"textord","\u2223","|"),$("math",Z,"textord","\u2223","\\vert"),$("text",Z,"textord","|","\\textbar"),$("math",Z,"textord","\u2225","\\|"),$("math",Z,"textord","\u2225","\\Vert"),$("text",Z,"textord","\u2225","\\textbardbl"),$("text",Z,"textord","~","\\textasciitilde"),$("text",Z,"textord","\\","\\textbackslash"),$("text",Z,"textord","^","\\textasciicircum"),$("math",Z,et,"\u2191","\\uparrow",!0),$("math",Z,et,"\u21d1","\\Uparrow",!0),$("math",Z,et,"\u2193","\\downarrow",!0),$("math",Z,et,"\u21d3","\\Downarrow",!0),$("math",Z,et,"\u2195","\\updownarrow",!0),$("math",Z,et,"\u21d5","\\Updownarrow",!0),$("math",Z,tt,"\u2210","\\coprod"),$("math",Z,tt,"\u22c1","\\bigvee"),$("math",Z,tt,"\u22c0","\\bigwedge"),$("math",Z,tt,"\u2a04","\\biguplus"),$("math",Z,tt,"\u22c2","\\bigcap"),$("math",Z,tt,"\u22c3","\\bigcup"),$("math",Z,tt,"\u222b","\\int"),$("math",Z,tt,"\u222b","\\intop"),$("math",Z,tt,"\u222c","\\iint"),$("math",Z,tt,"\u222d","\\iiint"),$("math",Z,tt,"\u220f","\\prod"),$("math",Z,tt,"\u2211","\\sum"),$("math",Z,tt,"\u2a02","\\bigotimes"),$("math",Z,tt,"\u2a01","\\bigoplus"),$("math",Z,tt,"\u2a00","\\bigodot"),$("math",Z,tt,"\u222e","\\oint"),$("math",Z,tt,"\u222f","\\oiint"),$("math",Z,tt,"\u2230","\\oiiint"),$("math",Z,tt,"\u2a06","\\bigsqcup"),$("math",Z,tt,"\u222b","\\smallint"),$("text",Z,"inner","\u2026","\\textellipsis"),$("math",Z,"inner","\u2026","\\mathellipsis"),$("text",Z,"inner","\u2026","\\ldots",!0),$("math",Z,"inner","\u2026","\\ldots",!0),$("math",Z,"inner","\u22ef","\\@cdots",!0),$("math",Z,"inner","\u22f1","\\ddots",!0),$("math",Z,"textord","\u22ee","\\varvdots"),$("math",Z,"accent-token","\u02ca","\\acute"),$("math",Z,"accent-token","\u02cb","\\grave"),$("math",Z,"accent-token","\xa8","\\ddot"),$("math",Z,"accent-token","~","\\tilde"),$("math",Z,"accent-token","\u02c9","\\bar"),$("math",Z,"accent-token","\u02d8","\\breve"),$("math",Z,"accent-token","\u02c7","\\check"),$("math",Z,"accent-token","^","\\hat"),$("math",Z,"accent-token","\u20d7","\\vec"),$("math",Z,"accent-token","\u02d9","\\dot"),$("math",Z,"accent-token","\u02da","\\mathring"),$("math",Z,Q,"\u0131","\\imath",!0),$("math",Z,Q,"\u0237","\\jmath",!0),$("text",Z,"textord","\u0131","\\i",!0),$("text",Z,"textord","\u0237","\\j",!0),$("text",Z,"textord","\xdf","\\ss",!0),$("text",Z,"textord","\xe6","\\ae",!0),$("text",Z,"textord","\xe6","\\ae",!0),$("text",Z,"textord","\u0153","\\oe",!0),$("text",Z,"textord","\xf8","\\o",!0),$("text",Z,"textord","\xc6","\\AE",!0),$("text",Z,"textord","\u0152","\\OE",!0),$("text",Z,"textord","\xd8","\\O",!0),$("text",Z,"accent-token","\u02ca","\\'"),$("text",Z,"accent-token","\u02cb","\\`"),$("text",Z,"accent-token","\u02c6","\\^"),$("text",Z,"accent-token","\u02dc","\\~"),$("text",Z,"accent-token","\u02c9","\\="),$("text",Z,"accent-token","\u02d8","\\u"),$("text",Z,"accent-token","\u02d9","\\."),$("text",Z,"accent-token","\u02da","\\r"),$("text",Z,"accent-token","\u02c7","\\v"),$("text",Z,"accent-token","\xa8",'\\"'),$("text",Z,"accent-token","\u02dd","\\H"),$("text",Z,"accent-token","\u25ef","\\textcircled");var rt={"--":!0,"---":!0,"``":!0,"''":!0};$("text",Z,"textord","\u2013","--"),$("text",Z,"textord","\u2013","\\textendash"),$("text",Z,"textord","\u2014","---"),$("text",Z,"textord","\u2014","\\textemdash"),$("text",Z,"textord","\u2018","`"),$("text",Z,"textord","\u2018","\\textquoteleft"),$("text",Z,"textord","\u2019","'"),$("text",Z,"textord","\u2019","\\textquoteright"),$("text",Z,"textord","\u201c","``"),$("text",Z,"textord","\u201c","\\textquotedblleft"),$("text",Z,"textord","\u201d","''"),$("text",Z,"textord","\u201d","\\textquotedblright"),$("math",Z,"textord","\xb0","\\degree",!0),$("text",Z,"textord","\xb0","\\degree"),$("text",Z,"textord","\xb0","\\textdegree",!0),$("math",Z,Q,"\xa3","\\pounds"),$("math",Z,Q,"\xa3","\\mathsterling",!0),$("text",Z,Q,"\xa3","\\pounds"),$("text",Z,Q,"\xa3","\\textsterling",!0),$("math",K,"textord","\u2720","\\maltese"),$("text",K,"textord","\u2720","\\maltese"),$("text",Z,"spacing","\xa0","\\ "),$("text",Z,"spacing","\xa0"," "),$("text",Z,"spacing","\xa0","~");for(var at=0;at<'0123456789/@."'.length;at++){var nt='0123456789/@."'.charAt(at);$("math",Z,"textord",nt,nt)}for(var it=0;it<'0123456789!@*()-=+[]<>|";:?/.,'.length;it++){var ot='0123456789!@*()-=+[]<>|";:?/.,'.charAt(it);$("text",Z,"textord",ot,ot)}for(var st="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",ht=0;ht<st.length;ht++){var lt=st.charAt(ht);$("math",Z,Q,lt,lt),$("text",Z,"textord",lt,lt)}$("math",K,"textord","C","\u2102"),$("text",K,"textord","C","\u2102"),$("math",K,"textord","H","\u210d"),$("text",K,"textord","H","\u210d"),$("math",K,"textord","N","\u2115"),$("text",K,"textord","N","\u2115"),$("math",K,"textord","P","\u2119"),$("text",K,"textord","P","\u2119"),$("math",K,"textord","Q","\u211a"),$("text",K,"textord","Q","\u211a"),$("math",K,"textord","R","\u211d"),$("text",K,"textord","R","\u211d"),$("math",K,"textord","Z","\u2124"),$("text",K,"textord","Z","\u2124"),$("math",Z,Q,"h","\u210e"),$("text",Z,Q,"h","\u210e");for(var mt="",ct=0;ct<st.length;ct++){var ut=st.charAt(ct);$("math",Z,Q,ut,mt=String.fromCharCode(55349,56320+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56372+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56424+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56580+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56736+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56788+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56840+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56944+ct)),$("text",Z,"textord",ut,mt),ct<26&&($("math",Z,Q,ut,mt=String.fromCharCode(55349,56632+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56476+ct)),$("text",Z,"textord",ut,mt))}$("math",Z,Q,"k",mt=String.fromCharCode(55349,56668)),$("text",Z,"textord","k",mt);for(var pt=0;pt<10;pt++){var dt=pt.toString();$("math",Z,Q,dt,mt=String.fromCharCode(55349,57294+pt)),$("text",Z,"textord",dt,mt),$("math",Z,Q,dt,mt=String.fromCharCode(55349,57314+pt)),$("text",Z,"textord",dt,mt),$("math",Z,Q,dt,mt=String.fromCharCode(55349,57324+pt)),$("text",Z,"textord",dt,mt),$("math",Z,Q,dt,mt=String.fromCharCode(55349,57334+pt)),$("text",Z,"textord",dt,mt)}for(var ft=0;ft<"\xc7\xd0\xde\xe7\xfe".length;ft++){var gt="\xc7\xd0\xde\xe7\xfe".charAt(ft);$("math",Z,Q,gt,gt),$("text",Z,"textord",gt,gt)}$("text",Z,"textord","\xf0","\xf0"),$("text",Z,"textord","\u2013","\u2013"),$("text",Z,"textord","\u2014","\u2014"),$("text",Z,"textord","\u2018","\u2018"),$("text",Z,"textord","\u2019","\u2019"),$("text",Z,"textord","\u201c","\u201c"),$("text",Z,"textord","\u201d","\u201d");var xt=[["mathbf","textbf","Main-Bold"],["mathbf","textbf","Main-Bold"],["mathdefault","textit","Math-Italic"],["mathdefault","textit","Math-Italic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["mathscr","textscr","Script-Regular"],["","",""],["","",""],["","",""],["mathfrak","textfrak","Fraktur-Regular"],["mathfrak","textfrak","Fraktur-Regular"],["mathbb","textbb","AMS-Regular"],["mathbb","textbb","AMS-Regular"],["","",""],["","",""],["mathsf","textsf","SansSerif-Regular"],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathitsf","textitsf","SansSerif-Italic"],["mathitsf","textitsf","SansSerif-Italic"],["","",""],["","",""],["mathtt","texttt","Typewriter-Regular"],["mathtt","texttt","Typewriter-Regular"]],vt=[["mathbf","textbf","Main-Bold"],["","",""],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathtt","texttt","Typewriter-Regular"]],bt=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],yt=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],wt=function(t,e){return e.size<2?t:bt[t-1][e.size-1]},kt=function(){function t(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||t.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=yt[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}var e=t.prototype;return e.extend=function(e){var r={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(var a in e)e.hasOwnProperty(a)&&(r[a]=e[a]);return new t(r)},e.havingStyle=function(t){return this.style===t?this:this.extend({style:t,size:wt(this.textSize,t)})},e.havingCrampedStyle=function(){return this.havingStyle(this.style.cramp())},e.havingSize=function(t){return this.size===t&&this.textSize===t?this:this.extend({style:this.style.text(),size:t,textSize:t,sizeMultiplier:yt[t-1]})},e.havingBaseStyle=function(e){e=e||this.style.text();var r=wt(t.BASESIZE,e);return this.size===r&&this.textSize===t.BASESIZE&&this.style===e?this:this.extend({style:e,size:r})},e.havingBaseSizing=function(){var t;switch(this.style.id){case 4:case 5:t=3;break;case 6:case 7:t=1;break;default:t=6}return this.extend({style:this.style.text(),size:t})},e.withColor=function(t){return this.extend({color:t})},e.withPhantom=function(){return this.extend({phantom:!0})},e.withFont=function(t){return this.extend({font:t})},e.withTextFontFamily=function(t){return this.extend({fontFamily:t,font:""})},e.withTextFontWeight=function(t){return this.extend({fontWeight:t,font:""})},e.withTextFontShape=function(t){return this.extend({fontShape:t,font:""})},e.sizingClasses=function(t){return t.size!==this.size?["sizing","reset-size"+t.size,"size"+this.size]:[]},e.baseSizingClasses=function(){return this.size!==t.BASESIZE?["sizing","reset-size"+this.size,"size"+t.BASESIZE]:[]},e.fontMetrics=function(){return this._fontMetrics||(this._fontMetrics=function(t){var e;if(!Y[e=t>=5?0:t>=3?1:2]){var r=Y[e]={cssEmPerMu:V.quad[e]/18};for(var a in V)V.hasOwnProperty(a)&&(r[a]=V[a][e])}return Y[e]}(this.size)),this._fontMetrics},e.getColor=function(){return this.phantom?"transparent":this.color},t}();kt.BASESIZE=6;var St=kt,Mt={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},zt={ex:!0,em:!0,mu:!0},At=function(t){return"string"!=typeof t&&(t=t.unit),t in Mt||t in zt||"ex"===t},Tt=function(t,e){var r;if(t.unit in Mt)r=Mt[t.unit]/e.fontMetrics().ptPerEm/e.sizeMultiplier;else if("mu"===t.unit)r=e.fontMetrics().cssEmPerMu;else{var a;if(a=e.style.isTight()?e.havingStyle(e.style.text()):e,"ex"===t.unit)r=a.fontMetrics().xHeight;else{if("em"!==t.unit)throw new o("Invalid unit: '"+t.unit+"'");r=a.fontMetrics().quad}a!==e&&(r*=a.sizeMultiplier/e.sizeMultiplier)}return Math.min(t.number*r,e.maxSize)},Bt=["\\imath","\u0131","\\jmath","\u0237","\\pounds","\\mathsterling","\\textsterling","\xa3"],Ct=function(t,e,r){return j[r][t]&&j[r][t].replace&&(t=j[r][t].replace),{value:t,metrics:G(t,e,r)}},qt=function(t,e,r,a,n){var i,o=Ct(t,e,r),s=o.metrics;if(t=o.value,s){var h=s.italic;("text"===r||a&&"mathit"===a.font)&&(h=0),i=new E(t,s.height,s.depth,h,s.skew,s.width,n)}else"undefined"!=typeof console&&console.warn("No character metrics for '"+t+"' in style '"+e+"' and mode '"+r+"'"),i=new E(t,0,0,0,0,0,n);if(a){i.maxFontSize=a.sizeMultiplier,a.style.isTight()&&i.classes.push("mtight");var l=a.getColor();l&&(i.style.color=l)}return i},Nt=function(t,e){if(T(t.classes)!==T(e.classes)||t.skew!==e.skew||t.maxFontSize!==e.maxFontSize)return!1;for(var r in t.style)if(t.style.hasOwnProperty(r)&&t.style[r]!==e.style[r])return!1;for(var a in e.style)if(e.style.hasOwnProperty(a)&&t.style[a]!==e.style[a])return!1;return!0},It=function(t){for(var e=0,r=0,a=0,n=0;n<t.children.length;n++){var i=t.children[n];i.height>e&&(e=i.height),i.depth>r&&(r=i.depth),i.maxFontSize>a&&(a=i.maxFontSize)}t.height=e,t.depth=r,t.maxFontSize=a},Rt=function(t,e,r,a){var n=new N(t,e,r,a);return It(n),n},Ot=function(t,e,r,a){return new N(t,e,r,a)},Et=function(t){var e=new A(t);return It(e),e},Lt=function(t,e,r){var a="";switch(t){case"amsrm":a="AMS";break;case"textrm":a="Main";break;case"textsf":a="SansSerif";break;case"texttt":a="Typewriter";break;default:a=t}return a+"-"+("textbf"===e&&"textit"===r?"BoldItalic":"textbf"===e?"Bold":"textit"===e?"Italic":"Regular")},Ht={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},Pt={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},Dt={fontMap:Ht,makeSymbol:qt,mathsym:function(t,e,r,a){return void 0===a&&(a=[]),"boldsymbol"===r.font&&Ct(t,"Main-Bold",e).metrics?qt(t,"Main-Bold",e,r,a.concat(["mathbf"])):"\\"===t||"main"===j[e][t].font?qt(t,"Main-Regular",e,r,a):qt(t,"AMS-Regular",e,r,a.concat(["amsrm"]))},makeSpan:Rt,makeSvgSpan:Ot,makeLineSpan:function(t,e,r){var a=Rt([t],[],e);return a.height=Math.max(r||e.fontMetrics().defaultRuleThickness,e.minRuleThickness),a.style.borderBottomWidth=a.height+"em",a.maxFontSize=1,a},makeAnchor:function(t,e,r,a){var n=new I(t,e,r,a);return It(n),n},makeFragment:Et,wrapFragment:function(t,e){return t instanceof A?Rt([],[t],e):t},makeVList:function(t,e){for(var r=function(t){if("individualShift"===t.positionType){for(var e=t.children,r=[e[0]],a=-e[0].shift-e[0].elem.depth,n=a,i=1;i<e.length;i++){var o=-e[i].shift-n-e[i].elem.depth,s=o-(e[i-1].elem.height+e[i-1].elem.depth);n+=o,r.push({type:"kern",size:s}),r.push(e[i])}return{children:r,depth:a}}var h;if("top"===t.positionType){for(var l=t.positionData,m=0;m<t.children.length;m++){var c=t.children[m];l-="kern"===c.type?c.size:c.elem.height+c.elem.depth}h=l}else if("bottom"===t.positionType)h=-t.positionData;else{var u=t.children[0];if("elem"!==u.type)throw new Error('First child must have type "elem".');if("shift"===t.positionType)h=-u.elem.depth-t.positionData;else{if("firstBaseline"!==t.positionType)throw new Error("Invalid positionType "+t.positionType+".");h=-u.elem.depth}}return{children:t.children,depth:h}}(t),a=r.children,n=r.depth,i=0,o=0;o<a.length;o++){var s=a[o];if("elem"===s.type){var h=s.elem;i=Math.max(i,h.maxFontSize,h.height)}}i+=2;var l=Rt(["pstrut"],[]);l.style.height=i+"em";for(var m=[],c=n,u=n,p=n,d=0;d<a.length;d++){var f=a[d];if("kern"===f.type)p+=f.size;else{var g=f.elem,x=f.wrapperClasses||[],v=f.wrapperStyle||{},b=Rt(x,[l,g],void 0,v);b.style.top=-i-p-g.depth+"em",f.marginLeft&&(b.style.marginLeft=f.marginLeft),f.marginRight&&(b.style.marginRight=f.marginRight),m.push(b),p+=g.height+g.depth}c=Math.min(c,p),u=Math.max(u,p)}var y,w=Rt(["vlist"],m);if(w.style.height=u+"em",c<0){var k=Rt([],[]),S=Rt(["vlist"],[k]);S.style.height=-c+"em";var M=Rt(["vlist-s"],[new E("\u200b")]);y=[Rt(["vlist-r"],[w,M]),Rt(["vlist-r"],[S])]}else y=[Rt(["vlist-r"],[w])];var z=Rt(["vlist-t"],y);return 2===y.length&&z.classes.push("vlist-t2"),z.height=u,z.depth=-c,z},makeOrd:function(t,e,r){var a,n=t.mode,i=t.text,s=["mord"],h="math"===n||"text"===n&&e.font,l=h?e.font:e.fontFamily;if(55349===i.charCodeAt(0)){var m=function(t,e){var r=1024*(t.charCodeAt(0)-55296)+(t.charCodeAt(1)-56320)+65536,a="math"===e?0:1;if(119808<=r&&r<120484){var n=Math.floor((r-119808)/26);return[xt[n][2],xt[n][a]]}if(120782<=r&&r<=120831){var i=Math.floor((r-120782)/10);return[vt[i][2],vt[i][a]]}if(120485===r||120486===r)return[xt[0][2],xt[0][a]];if(120486<r&&r<120782)return["",""];throw new o("Unsupported character: "+t)}(i,n),u=m[0],p=m[1];return qt(i,u,n,e,s.concat(p))}if(l){var d,f;if("boldsymbol"===l||"mathnormal"===l){var g="boldsymbol"===l?function(t,e,r,a){return Ct(t,"Math-BoldItalic",e).metrics?{fontName:"Math-BoldItalic",fontClass:"boldsymbol"}:{fontName:"Main-Bold",fontClass:"mathbf"}}(i,n):(a=i,c.contains(Bt,a)?{fontName:"Main-Italic",fontClass:"mathit"}:/[0-9]/.test(a.charAt(0))?{fontName:"Caligraphic-Regular",fontClass:"mathcal"}:{fontName:"Math-Italic",fontClass:"mathdefault"});d=g.fontName,f=[g.fontClass]}else c.contains(Bt,i)?(d="Main-Italic",f=["mathit"]):h?(d=Ht[l].fontName,f=[l]):(d=Lt(l,e.fontWeight,e.fontShape),f=[l,e.fontWeight,e.fontShape]);if(Ct(i,d,n).metrics)return qt(i,d,n,e,s.concat(f));if(rt.hasOwnProperty(i)&&"Typewriter"===d.substr(0,10)){for(var x=[],v=0;v<i.length;v++)x.push(qt(i[v],d,n,e,s.concat(f)));return Et(x)}}if("mathord"===r){var b=function(t,e,r,a){return/[0-9]/.test(t.charAt(0))||c.contains(Bt,t)?{fontName:"Main-Italic",fontClass:"mathit"}:{fontName:"Math-Italic",fontClass:"mathdefault"}}(i);return qt(i,b.fontName,n,e,s.concat([b.fontClass]))}if("textord"===r){var y=j[n][i]&&j[n][i].font;if("ams"===y){var w=Lt("amsrm",e.fontWeight,e.fontShape);return qt(i,w,n,e,s.concat("amsrm",e.fontWeight,e.fontShape))}if("main"!==y&&y){var k=Lt(y,e.fontWeight,e.fontShape);return qt(i,k,n,e,s.concat(k,e.fontWeight,e.fontShape))}var S=Lt("textrm",e.fontWeight,e.fontShape);return qt(i,S,n,e,s.concat(e.fontWeight,e.fontShape))}throw new Error("unexpected type: "+r+" in makeOrd")},makeGlue:function(t,e){var r=Rt(["mspace"],[],e),a=Tt(t,e);return r.style.marginRight=a+"em",r},staticSvg:function(t,e){var r=Pt[t],a=r[0],n=r[1],i=r[2],o=new H(a),s=new L([o],{width:n+"em",height:i+"em",style:"width:"+n+"em",viewBox:"0 0 "+1e3*n+" "+1e3*i,preserveAspectRatio:"xMinYMin"}),h=Ot(["overlay"],[s],e);return h.height=i,h.style.height=i+"em",h.style.width=n+"em",h},svgData:Pt,tryCombineChars:function(t){for(var e=0;e<t.length-1;e++){var r=t[e],a=t[e+1];r instanceof E&&a instanceof E&&Nt(r,a)&&(r.text+=a.text,r.height=Math.max(r.height,a.height),r.depth=Math.max(r.depth,a.depth),r.italic=a.italic,t.splice(e+1,1),e--)}return t}};function Ft(t,e){var r=Vt(t,e);if(!r)throw new Error("Expected node of type "+e+", but got "+(t?"node of type "+t.type:String(t)));return r}function Vt(t,e){return t&&t.type===e?t:null}function Ut(t,e){var r=function(t,e){return t&&"atom"===t.type&&t.family===e?t:null}(t,e);if(!r)throw new Error('Expected node of type "atom" and family "'+e+'", but got '+(t?"atom"===t.type?"atom of family "+t.family:"node of type "+t.type:String(t)));return r}function Gt(t){var e=Yt(t);if(!e)throw new Error("Expected node of symbol group type, but got "+(t?"node of type "+t.type:String(t)));return e}function Yt(t){return t&&("atom"===t.type||X.hasOwnProperty(t.type))?t:null}var Wt={number:3,unit:"mu"},Xt={number:4,unit:"mu"},_t={number:5,unit:"mu"},jt={mord:{mop:Wt,mbin:Xt,mrel:_t,minner:Wt},mop:{mord:Wt,mop:Wt,mrel:_t,minner:Wt},mbin:{mord:Xt,mop:Xt,mopen:Xt,minner:Xt},mrel:{mord:_t,mop:_t,mopen:_t,minner:_t},mopen:{},mclose:{mop:Wt,mbin:Xt,mrel:_t,minner:Wt},mpunct:{mord:Wt,mop:Wt,mrel:_t,mopen:Wt,mclose:Wt,mpunct:Wt,minner:Wt},minner:{mord:Wt,mop:Wt,mbin:Xt,mrel:_t,mopen:Wt,mpunct:Wt,minner:Wt}},$t={mord:{mop:Wt},mop:{mord:Wt,mop:Wt},mbin:{},mrel:{},mopen:{},mclose:{mop:Wt},mpunct:{},minner:{mop:Wt}},Zt={},Kt={},Jt={};function Qt(t){for(var e=t.type,r=t.names,a=t.props,n=t.handler,i=t.htmlBuilder,o=t.mathmlBuilder,s={type:e,numArgs:a.numArgs,argTypes:a.argTypes,greediness:void 0===a.greediness?1:a.greediness,allowedInText:!!a.allowedInText,allowedInMath:void 0===a.allowedInMath||a.allowedInMath,numOptionalArgs:a.numOptionalArgs||0,infix:!!a.infix,handler:n},h=0;h<r.length;++h)Zt[r[h]]=s;e&&(i&&(Kt[e]=i),o&&(Jt[e]=o))}function te(t){Qt({type:t.type,names:[],props:{numArgs:0},handler:function(){throw new Error("Should never be called.")},htmlBuilder:t.htmlBuilder,mathmlBuilder:t.mathmlBuilder})}var ee=function(t){var e=Vt(t,"ordgroup");return e?e.body:[t]},re=Dt.makeSpan,ae=["leftmost","mbin","mopen","mrel","mop","mpunct"],ne=["rightmost","mrel","mclose","mpunct"],ie={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT},oe={mord:"mord",mop:"mop",mbin:"mbin",mrel:"mrel",mopen:"mopen",mclose:"mclose",mpunct:"mpunct",minner:"minner"},se=function(t,e,r,a){void 0===a&&(a=[null,null]);for(var n=[],i=0;i<t.length;i++){var o=ue(t[i],e);if(o instanceof A){var s=o.children;n.push.apply(n,s)}else n.push(o)}if(!r)return n;var h=e;if(1===t.length){var l=Vt(t[0],"sizing")||Vt(t[0],"styling");l&&("sizing"===l.type?h=e.havingSize(l.size):"styling"===l.type&&(h=e.havingStyle(ie[l.style])))}var m=re([a[0]||"leftmost"],[],e),u=re([a[1]||"rightmost"],[],e);return he(n,function(t,e){var r=e.classes[0],a=t.classes[0];"mbin"===r&&c.contains(ne,a)?e.classes[0]="mord":"mbin"===a&&c.contains(ae,r)&&(t.classes[0]="mord")},{node:m},u),he(n,function(t,e){var r=me(e),a=me(t),n=r&&a?t.hasClass("mtight")?$t[r][a]:jt[r][a]:null;if(n)return Dt.makeGlue(n,h)},{node:m},u),n},he=function t(e,r,a,n){n&&e.push(n);for(var i=0;i<e.length;i++){var o=e[i],s=le(o);if(s)t(s.children,r,a);else if("mspace"!==o.classes[0]){var h=r(o,a.node);h&&(a.insertAfter?a.insertAfter(h):(e.unshift(h),i++)),a.node=o,a.insertAfter=function(t){return function(r){e.splice(t+1,0,r),i++}}(i)}}n&&e.pop()},le=function(t){return t instanceof A||t instanceof I?t:null},me=function(t,e){return t?(e&&(t=function t(e,r){var a=le(e);if(a){var n=a.children;if(n.length){if("right"===r)return t(n[n.length-1],"right");if("left"===r)return t(n[0],"left")}}return e}(t,e)),oe[t.classes[0]]||null):null},ce=function(t,e){var r=["nulldelimiter"].concat(t.baseSizingClasses());return re(e.concat(r))},ue=function(t,e,r){if(!t)return re();if(Kt[t.type]){var a=Kt[t.type](t,e);if(r&&e.size!==r.size){a=re(e.sizingClasses(r),[a],e);var n=e.sizeMultiplier/r.sizeMultiplier;a.height*=n,a.depth*=n}return a}throw new o("Got group of unknown type: '"+t.type+"'")};function pe(t,e){var r=re(["base"],t,e),a=re(["strut"]);return a.style.height=r.height+r.depth+"em",a.style.verticalAlign=-r.depth+"em",r.children.unshift(a),r}function de(t,e){var r=null;1===t.length&&"tag"===t[0].type&&(r=t[0].tag,t=t[0].body);for(var a,n=se(t,e,!0),i=[],o=[],s=0;s<n.length;s++)if(o.push(n[s]),n[s].hasClass("mbin")||n[s].hasClass("mrel")||n[s].hasClass("allowbreak")){for(var h=!1;s<n.length-1&&n[s+1].hasClass("mspace")&&!n[s+1].hasClass("newline");)s++,o.push(n[s]),n[s].hasClass("nobreak")&&(h=!0);h||(i.push(pe(o,e)),o=[])}else n[s].hasClass("newline")&&(o.pop(),o.length>0&&(i.push(pe(o,e)),o=[]),i.push(n[s]));o.length>0&&i.push(pe(o,e)),r&&((a=pe(se(r,e,!0))).classes=["tag"],i.push(a));var l=re(["katex-html"],i);if(l.setAttribute("aria-hidden","true"),a){var m=a.children[0];m.style.height=l.height+l.depth+"em",m.style.verticalAlign=-l.depth+"em"}return l}function fe(t){return new A(t)}var ge=function(){function t(t,e){this.type=void 0,this.attributes=void 0,this.children=void 0,this.type=t,this.attributes={},this.children=e||[]}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.getAttribute=function(t){return this.attributes[t]},e.toNode=function(){var t=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);for(var r=0;r<this.children.length;r++)t.appendChild(this.children[r].toNode());return t},e.toMarkup=function(){var t="<"+this.type;for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&(t+=" "+e+'="',t+=c.escape(this.attributes[e]),t+='"');t+=">";for(var r=0;r<this.children.length;r++)t+=this.children[r].toMarkup();return t+="</"+this.type+">"},e.toText=function(){return this.children.map(function(t){return t.toText()}).join("")},t}(),xe=function(){function t(t){this.text=void 0,this.text=t}var e=t.prototype;return e.toNode=function(){return document.createTextNode(this.text)},e.toMarkup=function(){return c.escape(this.toText())},e.toText=function(){return this.text},t}(),ve={MathNode:ge,TextNode:xe,SpaceNode:function(){function t(t){this.width=void 0,this.character=void 0,this.width=t,this.character=t>=.05555&&t<=.05556?"\u200a":t>=.1666&&t<=.1667?"\u2009":t>=.2222&&t<=.2223?"\u2005":t>=.2777&&t<=.2778?"\u2005\u200a":t>=-.05556&&t<=-.05555?"\u200a\u2063":t>=-.1667&&t<=-.1666?"\u2009\u2063":t>=-.2223&&t<=-.2222?"\u205f\u2063":t>=-.2778&&t<=-.2777?"\u2005\u2063":null}var e=t.prototype;return e.toNode=function(){if(this.character)return document.createTextNode(this.character);var t=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return t.setAttribute("width",this.width+"em"),t},e.toMarkup=function(){return this.character?"<mtext>"+this.character+"</mtext>":'<mspace width="'+this.width+'em"/>'},e.toText=function(){return this.character?this.character:" "},t}(),newDocumentFragment:fe},be=function(t,e,r){return!j[e][t]||!j[e][t].replace||55349===t.charCodeAt(0)||rt.hasOwnProperty(t)&&r&&(r.fontFamily&&"tt"===r.fontFamily.substr(4,2)||r.font&&"tt"===r.font.substr(4,2))||(t=j[e][t].replace),new ve.TextNode(t)},ye=function(t){return 1===t.length?t[0]:new ve.MathNode("mrow",t)},we=function(t,e){if("texttt"===e.fontFamily)return"monospace";if("textsf"===e.fontFamily)return"textit"===e.fontShape&&"textbf"===e.fontWeight?"sans-serif-bold-italic":"textit"===e.fontShape?"sans-serif-italic":"textbf"===e.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===e.fontShape&&"textbf"===e.fontWeight)return"bold-italic";if("textit"===e.fontShape)return"italic";if("textbf"===e.fontWeight)return"bold";var r=e.font;if(!r||"mathnormal"===r)return null;var a=t.mode;if("mathit"===r)return"italic";if("boldsymbol"===r)return"bold-italic";if("mathbf"===r)return"bold";if("mathbb"===r)return"double-struck";if("mathfrak"===r)return"fraktur";if("mathscr"===r||"mathcal"===r)return"script";if("mathsf"===r)return"sans-serif";if("mathtt"===r)return"monospace";var n=t.text;return c.contains(["\\imath","\\jmath"],n)?null:(j[a][n]&&j[a][n].replace&&(n=j[a][n].replace),G(n,Dt.fontMap[r].fontName,a)?Dt.fontMap[r].variant:null)},ke=function(t,e,r){if(1===t.length){var a=Me(t[0],e);return r&&a instanceof ge&&"mo"===a.type&&(a.setAttribute("lspace","0em"),a.setAttribute("rspace","0em")),[a]}for(var n,i=[],o=0;o<t.length;o++){var s=Me(t[o],e);if(s instanceof ge&&n instanceof ge){if("mtext"===s.type&&"mtext"===n.type&&s.getAttribute("mathvariant")===n.getAttribute("mathvariant")){var h;(h=n.children).push.apply(h,s.children);continue}if("mn"===s.type&&"mn"===n.type){var l;(l=n.children).push.apply(l,s.children);continue}if("mi"===s.type&&1===s.children.length&&"mn"===n.type){var m=s.children[0];if(m instanceof xe&&"."===m.text){var c;(c=n.children).push.apply(c,s.children);continue}}else if("mi"===n.type&&1===n.children.length){var u=n.children[0];if(u instanceof xe&&"\u0338"===u.text&&("mo"===s.type||"mi"===s.type||"mn"===s.type)){var p=s.children[0];p instanceof xe&&p.text.length>0&&(p.text=p.text.slice(0,1)+"\u0338"+p.text.slice(1),i.pop())}}}i.push(s),n=s}return i},Se=function(t,e,r){return ye(ke(t,e,r))},Me=function(t,e){if(!t)return new ve.MathNode("mrow");if(Jt[t.type])return Jt[t.type](t,e);throw new o("Got group of unknown type: '"+t.type+"'")};function ze(t,e,r,a){var n,i=ke(t,r);n=1===i.length&&i[0]instanceof ge&&c.contains(["mrow","mtable"],i[0].type)?i[0]:new ve.MathNode("mrow",i);var o=new ve.MathNode("annotation",[new ve.TextNode(e)]);o.setAttribute("encoding","application/x-tex");var s=new ve.MathNode("semantics",[n,o]),h=new ve.MathNode("math",[s]);h.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML");var l=a?"katex":"katex-mathml";return Dt.makeSpan([l],[h])}var Ae=function(t){return new St({style:t.displayMode?w.DISPLAY:w.TEXT,maxSize:t.maxSize,minRuleThickness:t.minRuleThickness})},Te=function(t,e){if(e.displayMode){var r=["katex-display"];e.leqno&&r.push("leqno"),e.fleqn&&r.push("fleqn"),t=Dt.makeSpan(r,[t])}return t},Be=function(t,e,r){var a,n=Ae(r);if("mathml"===r.output)return ze(t,e,n,!0);if("html"===r.output){var i=de(t,n);a=Dt.makeSpan(["katex"],[i])}else{var o=ze(t,e,n,!1),s=de(t,n);a=Dt.makeSpan(["katex"],[o,s])}return Te(a,r)},Ce={widehat:"^",widecheck:"\u02c7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23df",overbrace:"\u23de",overgroup:"\u23e0",undergroup:"\u23e1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21d2",xRightarrow:"\u21d2",overleftharpoon:"\u21bc",xleftharpoonup:"\u21bc",overrightharpoon:"\u21c0",xrightharpoonup:"\u21c0",xLeftarrow:"\u21d0",xLeftrightarrow:"\u21d4",xhookleftarrow:"\u21a9",xhookrightarrow:"\u21aa",xmapsto:"\u21a6",xrightharpoondown:"\u21c1",xleftharpoondown:"\u21bd",xrightleftharpoons:"\u21cc",xleftrightharpoons:"\u21cb",xtwoheadleftarrow:"\u219e",xtwoheadrightarrow:"\u21a0",xlongequal:"=",xtofrom:"\u21c4",xrightleftarrows:"\u21c4",xrightequilibrium:"\u21cc",xleftequilibrium:"\u21cb"},qe={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},Ne=function(t){return"ordgroup"===t.type?t.body.length:1},Ie=function(t,e,r,a){var n,i=t.height+t.depth+2*r;if(/fbox|color/.test(e)){if(n=Dt.makeSpan(["stretchy",e],[],a),"fbox"===e){var o=a.color&&a.getColor();o&&(n.style.borderColor=o)}}else{var s=[];/^[bx]cancel$/.test(e)&&s.push(new P({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(e)&&s.push(new P({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var h=new L(s,{width:"100%",height:i+"em"});n=Dt.makeSvgSpan([],[h],a)}return n.height=i,n.style.height=i+"em",n},Re=function(t){var e=new ve.MathNode("mo",[new ve.TextNode(Ce[t.substr(1)])]);return e.setAttribute("stretchy","true"),e},Oe=function(t,e){var r=function(){var r=4e5,a=t.label.substr(1);if(c.contains(["widehat","widecheck","widetilde","utilde"],a)){var n,i,o,s=Ne(t.base);if(s>5)"widehat"===a||"widecheck"===a?(n=420,r=2364,o=.42,i=a+"4"):(n=312,r=2340,o=.34,i="tilde4");else{var h=[1,1,2,2,3,3][s];"widehat"===a||"widecheck"===a?(r=[0,1062,2364,2364,2364][h],n=[0,239,300,360,420][h],o=[0,.24,.3,.3,.36,.42][h],i=a+h):(r=[0,600,1033,2339,2340][h],n=[0,260,286,306,312][h],o=[0,.26,.286,.3,.306,.34][h],i="tilde"+h)}var l=new H(i),m=new L([l],{width:"100%",height:o+"em",viewBox:"0 0 "+r+" "+n,preserveAspectRatio:"none"});return{span:Dt.makeSvgSpan([],[m],e),minWidth:0,height:o}}var u,p,d=[],f=qe[a],g=f[0],x=f[1],v=f[2],b=v/1e3,y=g.length;if(1===y)u=["hide-tail"],p=[f[3]];else if(2===y)u=["halfarrow-left","halfarrow-right"],p=["xMinYMin","xMaxYMin"];else{if(3!==y)throw new Error("Correct katexImagesData or update code here to support\n "+y+" children.");u=["brace-left","brace-center","brace-right"],p=["xMinYMin","xMidYMin","xMaxYMin"]}for(var w=0;w<y;w++){var k=new H(g[w]),S=new L([k],{width:"400em",height:b+"em",viewBox:"0 0 "+r+" "+v,preserveAspectRatio:p[w]+" slice"}),M=Dt.makeSvgSpan([u[w]],[S],e);if(1===y)return{span:M,minWidth:x,height:b};M.style.height=b+"em",d.push(M)}return{span:Dt.makeSpan(["stretchy"],d,e),minWidth:x,height:b}}(),a=r.span,n=r.minWidth,i=r.height;return a.height=i,a.style.height=i+"em",n>0&&(a.style.minWidth=n+"em"),a},Ee=function(t,e){var r,a,n,i=Vt(t,"supsub");i?(r=(a=Ft(i.base,"accent")).base,i.base=r,n=function(t){if(t instanceof N)return t;throw new Error("Expected span<HtmlDomNode> but got "+String(t)+".")}(ue(i,e)),i.base=a):r=(a=Ft(t,"accent")).base;var o=ue(r,e.havingCrampedStyle()),s=0;if(a.isShifty&&c.isCharacterBox(r)){var h=c.getBaseElem(r);s=D(ue(h,e.havingCrampedStyle())).skew}var l,m=Math.min(o.height,e.fontMetrics().xHeight);if(a.isStretchy)l=Oe(a,e),l=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:o},{type:"elem",elem:l,wrapperClasses:["svg-align"],wrapperStyle:s>0?{width:"calc(100% - "+2*s+"em)",marginLeft:2*s+"em"}:void 0}]},e);else{var u,p;"\\vec"===a.label?(u=Dt.staticSvg("vec",e),p=Dt.svgData.vec[1]):((u=D(u=Dt.makeOrd({mode:a.mode,text:a.label},e,"textord"))).italic=0,p=u.width),l=Dt.makeSpan(["accent-body"],[u]);var d="\\textcircled"===a.label;d&&(l.classes.push("accent-full"),m=o.height);var f=s;d||(f-=p/2),l.style.left=f+"em","\\textcircled"===a.label&&(l.style.top=".2em"),l=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:o},{type:"kern",size:-m},{type:"elem",elem:l}]},e)}var g=Dt.makeSpan(["mord","accent"],[l],e);return n?(n.children[0]=g,n.height=Math.max(g.height,n.height),n.classes[0]="mord",n):g},Le=function(t,e){var r=t.isStretchy?Re(t.label):new ve.MathNode("mo",[be(t.label,t.mode)]),a=new ve.MathNode("mover",[Me(t.base,e),r]);return a.setAttribute("accent","true"),a},He=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(function(t){return"\\"+t}).join("|"));Qt({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:function(t,e){var r=e[0],a=!He.test(t.funcName),n=!a||"\\widehat"===t.funcName||"\\widetilde"===t.funcName||"\\widecheck"===t.funcName;return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:a,isShifty:n,base:r}},htmlBuilder:Ee,mathmlBuilder:Le}),Qt({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!1},handler:function(t,e){var r=e[0];return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:Ee,mathmlBuilder:Le}),Qt({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"accentUnder",mode:r.mode,label:a,base:n}},htmlBuilder:function(t,e){var r=ue(t.base,e),a=Oe(t,e),n="\\utilde"===t.label?.12:0,i=Dt.makeVList({positionType:"bottom",positionData:a.height+n,children:[{type:"elem",elem:a,wrapperClasses:["svg-align"]},{type:"kern",size:n},{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord","accentunder"],[i],e)},mathmlBuilder:function(t,e){var r=Re(t.label),a=new ve.MathNode("munder",[Me(t.base,e),r]);return a.setAttribute("accentunder","true"),a}});var Pe=function(t){var e=new ve.MathNode("mpadded",t?[t]:[]);return e.setAttribute("width","+0.6em"),e.setAttribute("lspace","0.3em"),e};Qt({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium"],props:{numArgs:1,numOptionalArgs:1},handler:function(t,e,r){var a=t.parser,n=t.funcName;return{type:"xArrow",mode:a.mode,label:n,body:e[0],below:r[0]}},htmlBuilder:function(t,e){var r,a=e.style,n=e.havingStyle(a.sup()),i=Dt.wrapFragment(ue(t.body,n,e),e);i.classes.push("x-arrow-pad"),t.below&&(n=e.havingStyle(a.sub()),(r=Dt.wrapFragment(ue(t.below,n,e),e)).classes.push("x-arrow-pad"));var o,s=Oe(t,e),h=-e.fontMetrics().axisHeight+.5*s.height,l=-e.fontMetrics().axisHeight-.5*s.height-.111;if((i.depth>.25||"\\xleftequilibrium"===t.label)&&(l-=i.depth),r){var m=-e.fontMetrics().axisHeight+r.height+.5*s.height+.111;o=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:l},{type:"elem",elem:s,shift:h},{type:"elem",elem:r,shift:m}]},e)}else o=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:l},{type:"elem",elem:s,shift:h}]},e);return o.children[0].children[0].children[1].classes.push("svg-align"),Dt.makeSpan(["mrel","x-arrow"],[o],e)},mathmlBuilder:function(t,e){var r,a=Re(t.label);if(t.body){var n=Pe(Me(t.body,e));if(t.below){var i=Pe(Me(t.below,e));r=new ve.MathNode("munderover",[a,i,n])}else r=new ve.MathNode("mover",[a,n])}else if(t.below){var o=Pe(Me(t.below,e));r=new ve.MathNode("munder",[a,o])}else r=Pe(),r=new ve.MathNode("mover",[a,r]);return r}}),Qt({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){for(var r=t.parser,a=Ft(e[0],"ordgroup").body,n="",i=0;i<a.length;i++){n+=Ft(a[i],"textord").text}var s=parseInt(n);if(isNaN(s))throw new o("\\@char has non-numeric argument "+n);return{type:"textord",mode:r.mode,text:String.fromCharCode(s)}}});var De=function(t,e){var r=se(t.body,e.withColor(t.color),!1);return Dt.makeFragment(r)},Fe=function(t,e){var r=ke(t.body,e.withColor(t.color)),a=new ve.MathNode("mstyle",r);return a.setAttribute("mathcolor",t.color),a};Qt({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,greediness:3,argTypes:["color","original"]},handler:function(t,e){var r=t.parser,a=Ft(e[0],"color-token").color,n=e[1];return{type:"color",mode:r.mode,color:a,body:ee(n)}},htmlBuilder:De,mathmlBuilder:Fe}),Qt({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,greediness:3,argTypes:["color"]},handler:function(t,e){var r=t.parser,a=t.breakOnTokenText,n=Ft(e[0],"color-token").color;r.gullet.macros.set("\\current@color",n);var i=r.parseExpression(!0,a);return{type:"color",mode:r.mode,color:n,body:i}},htmlBuilder:De,mathmlBuilder:Fe}),Qt({type:"cr",names:["\\cr","\\newline"],props:{numArgs:0,numOptionalArgs:1,argTypes:["size"],allowedInText:!0},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=r[0],o="\\cr"===n,s=!1;return o||(s=!a.settings.displayMode||!a.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode")),{type:"cr",mode:a.mode,newLine:s,newRow:o,size:i&&Ft(i,"size").value}},htmlBuilder:function(t,e){if(t.newRow)throw new o("\\cr valid only within a tabular/array environment");var r=Dt.makeSpan(["mspace"],[],e);return t.newLine&&(r.classes.push("newline"),t.size&&(r.style.marginTop=Tt(t.size,e)+"em")),r},mathmlBuilder:function(t,e){var r=new ve.MathNode("mspace");return t.newLine&&(r.setAttribute("linebreak","newline"),t.size&&r.setAttribute("height",Tt(t.size,e)+"em")),r}});var Ve=function(t,e,r){var a=G(j.math[t]&&j.math[t].replace||t,e,r);if(!a)throw new Error("Unsupported symbol "+t+" and font size "+e+".");return a},Ue=function(t,e,r,a){var n=r.havingBaseStyle(e),i=Dt.makeSpan(a.concat(n.sizingClasses(r)),[t],r),o=n.sizeMultiplier/r.sizeMultiplier;return i.height*=o,i.depth*=o,i.maxFontSize=n.sizeMultiplier,i},Ge=function(t,e,r){var a=e.havingBaseStyle(r),n=(1-e.sizeMultiplier/a.sizeMultiplier)*e.fontMetrics().axisHeight;t.classes.push("delimcenter"),t.style.top=n+"em",t.height-=n,t.depth+=n},Ye=function(t,e,r,a,n,i){var o=function(t,e,r,a){return Dt.makeSymbol(t,"Size"+e+"-Regular",r,a)}(t,e,n,a),s=Ue(Dt.makeSpan(["delimsizing","size"+e],[o],a),w.TEXT,a,i);return r&&Ge(s,a,w.TEXT),s},We=function(t,e,r){var a;return a="Size1-Regular"===e?"delim-size1":"delim-size4",{type:"elem",elem:Dt.makeSpan(["delimsizinginner",a],[Dt.makeSpan([],[Dt.makeSymbol(t,e,r)])])}},Xe={type:"kern",size:-.005},_e=function(t,e,r,a,n,i){var o,s,h,l;o=h=l=t,s=null;var m="Size1-Regular";"\\uparrow"===t?h=l="\u23d0":"\\Uparrow"===t?h=l="\u2016":"\\downarrow"===t?o=h="\u23d0":"\\Downarrow"===t?o=h="\u2016":"\\updownarrow"===t?(o="\\uparrow",h="\u23d0",l="\\downarrow"):"\\Updownarrow"===t?(o="\\Uparrow",h="\u2016",l="\\Downarrow"):"["===t||"\\lbrack"===t?(o="\u23a1",h="\u23a2",l="\u23a3",m="Size4-Regular"):"]"===t||"\\rbrack"===t?(o="\u23a4",h="\u23a5",l="\u23a6",m="Size4-Regular"):"\\lfloor"===t||"\u230a"===t?(h=o="\u23a2",l="\u23a3",m="Size4-Regular"):"\\lceil"===t||"\u2308"===t?(o="\u23a1",h=l="\u23a2",m="Size4-Regular"):"\\rfloor"===t||"\u230b"===t?(h=o="\u23a5",l="\u23a6",m="Size4-Regular"):"\\rceil"===t||"\u2309"===t?(o="\u23a4",h=l="\u23a5",m="Size4-Regular"):"("===t||"\\lparen"===t?(o="\u239b",h="\u239c",l="\u239d",m="Size4-Regular"):")"===t||"\\rparen"===t?(o="\u239e",h="\u239f",l="\u23a0",m="Size4-Regular"):"\\{"===t||"\\lbrace"===t?(o="\u23a7",s="\u23a8",l="\u23a9",h="\u23aa",m="Size4-Regular"):"\\}"===t||"\\rbrace"===t?(o="\u23ab",s="\u23ac",l="\u23ad",h="\u23aa",m="Size4-Regular"):"\\lgroup"===t||"\u27ee"===t?(o="\u23a7",l="\u23a9",h="\u23aa",m="Size4-Regular"):"\\rgroup"===t||"\u27ef"===t?(o="\u23ab",l="\u23ad",h="\u23aa",m="Size4-Regular"):"\\lmoustache"===t||"\u23b0"===t?(o="\u23a7",l="\u23ad",h="\u23aa",m="Size4-Regular"):"\\rmoustache"!==t&&"\u23b1"!==t||(o="\u23ab",l="\u23a9",h="\u23aa",m="Size4-Regular");var c=Ve(o,m,n),u=c.height+c.depth,p=Ve(h,m,n),d=p.height+p.depth,f=Ve(l,m,n),g=f.height+f.depth,x=0,v=1;if(null!==s){var b=Ve(s,m,n);x=b.height+b.depth,v=2}var y=u+g+x,k=Math.max(0,Math.ceil((e-y)/(v*d))),S=y+k*v*d,M=a.fontMetrics().axisHeight;r&&(M*=a.sizeMultiplier);var z=S/2-M,A=.005*(k+1)-d,T=[];if(T.push(We(l,m,n)),null===s)for(var B=0;B<k;B++)T.push(Xe),T.push(We(h,m,n));else{for(var C=0;C<k;C++)T.push(Xe),T.push(We(h,m,n));T.push({type:"kern",size:A}),T.push(We(h,m,n)),T.push(Xe),T.push(We(s,m,n));for(var q=0;q<k;q++)T.push(Xe),T.push(We(h,m,n))}T.push({type:"kern",size:A}),T.push(We(h,m,n)),T.push(Xe),T.push(We(o,m,n));var N=a.havingBaseStyle(w.TEXT),I=Dt.makeVList({positionType:"bottom",positionData:z,children:T},N);return Ue(Dt.makeSpan(["delimsizing","mult"],[I],N),w.TEXT,a,i)},je=function(t,e,r,a,n){var i=function(t,e,r){e*=1e3;var a="";switch(t){case"sqrtMain":a=function(t,e){return"M95,"+(622+t+e)+"\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl"+t/2.075+" -"+t+"\nc5.3,-9.3,12,-14,20,-14\nH400000v"+(40+t)+"H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM"+(834+t)+" "+e+"h400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize1":a=function(t,e){return"M263,"+(601+t+e)+"c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl"+t/2.084+" -"+t+"\nc4.7,-7.3,11,-11,19,-11\nH40000v"+(40+t)+"H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM"+(1001+t)+" "+e+"h400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize2":a=function(t,e){return"M983 "+(10+t+e)+"\nl"+t/3.13+" -"+t+"\nc4,-6.7,10,-10,18,-10 H400000v"+(40+t)+"\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM"+(1001+t)+" "+e+"h400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize3":a=function(t,e){return"M424,"+(2398+t+e)+"\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl"+t/4.223+" -"+t+"c4,-6.7,10,-10,18,-10 H400000\nv"+(40+t)+"H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M"+(1001+t)+" "+e+"\nh400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize4":a=function(t,e){return"M473,"+(2713+t+e)+"\nc339.3,-1799.3,509.3,-2700,510,-2702 l"+t/5.298+" -"+t+"\nc3.3,-7.3,9.3,-11,18,-11 H400000v"+(40+t)+"H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM"+(1001+t)+" "+e+"h400000v"+(40+t)+"H1017.7z"}(e,80);break;case"sqrtTall":a=function(t,e,r){return"M702 "+(t+e)+"H400000"+(40+t)+"\nH742v"+(r-54-e-t)+"l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 "+e+"H400000v"+(40+t)+"H742z"}(e,80,r)}return a}(t,a,r),o=new H(t,i),s=new L([o],{width:"400em",height:e+"em",viewBox:"0 0 400000 "+r,preserveAspectRatio:"xMinYMin slice"});return Dt.makeSvgSpan(["hide-tail"],[s],n)},$e=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","\\surd"],Ze=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1"],Ke=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],Je=[0,1.2,1.8,2.4,3],Qe=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],tr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"stack"}],er=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],rr=function(t){if("small"===t.type)return"Main-Regular";if("large"===t.type)return"Size"+t.size+"-Regular";if("stack"===t.type)return"Size4-Regular";throw new Error("Add support for delim type '"+t.type+"' here.")},ar=function(t,e,r,a){for(var n=Math.min(2,3-a.style.size);n<r.length&&"stack"!==r[n].type;n++){var i=Ve(t,rr(r[n]),"math"),o=i.height+i.depth;if("small"===r[n].type&&(o*=a.havingBaseStyle(r[n].style).sizeMultiplier),o>e)return r[n]}return r[r.length-1]},nr=function(t,e,r,a,n,i){var o;"<"===t||"\\lt"===t||"\u27e8"===t?t="\\langle":">"!==t&&"\\gt"!==t&&"\u27e9"!==t||(t="\\rangle"),o=c.contains(Ke,t)?Qe:c.contains($e,t)?er:tr;var s=ar(t,e,o,a);return"small"===s.type?function(t,e,r,a,n,i){var o=Dt.makeSymbol(t,"Main-Regular",n,a),s=Ue(o,e,a,i);return r&&Ge(s,a,e),s}(t,s.style,r,a,n,i):"large"===s.type?Ye(t,s.size,r,a,n,i):_e(t,e,r,a,n,i)},ir=function(t,e){var r,a,n=e.havingBaseSizing(),i=ar("\\surd",t*n.sizeMultiplier,er,n),o=n.sizeMultiplier,s=Math.max(0,e.minRuleThickness-e.fontMetrics().sqrtRuleThickness),h=0,l=0,m=0;return"small"===i.type?(t<1?o=1:t<1.4&&(o=.7),l=(1+s)/o,(r=je("sqrtMain",h=(1+s+.08)/o,m=1e3+1e3*s+80,s,e)).style.minWidth="0.853em",a=.833/o):"large"===i.type?(m=1080*Je[i.size],l=(Je[i.size]+s)/o,h=(Je[i.size]+s+.08)/o,(r=je("sqrtSize"+i.size,h,m,s,e)).style.minWidth="1.02em",a=1/o):(h=t+s+.08,l=t+s,m=Math.floor(1e3*t+s)+80,(r=je("sqrtTall",h,m,s,e)).style.minWidth="0.742em",a=1.056),r.height=l,r.style.height=h+"em",{span:r,advanceWidth:a,ruleWidth:(e.fontMetrics().sqrtRuleThickness+s)*o}},or=function(t,e,r,a,n){if("<"===t||"\\lt"===t||"\u27e8"===t?t="\\langle":">"!==t&&"\\gt"!==t&&"\u27e9"!==t||(t="\\rangle"),c.contains($e,t)||c.contains(Ke,t))return Ye(t,e,!1,r,a,n);if(c.contains(Ze,t))return _e(t,Je[e],!1,r,a,n);throw new o("Illegal delimiter: '"+t+"'")},sr=nr,hr=function(t,e,r,a,n,i){var o=a.fontMetrics().axisHeight*a.sizeMultiplier,s=5/a.fontMetrics().ptPerEm,h=Math.max(e-o,r+o),l=Math.max(h/500*901,2*h-s);return nr(t,l,!0,a,n,i)},lr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},mr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27e8","\\rangle","\u27e9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function cr(t,e){var r=Yt(t);if(r&&c.contains(mr,r.text))return r;throw new o("Invalid delimiter: '"+(r?r.text:JSON.stringify(t))+"' after '"+e.funcName+"'",t)}function ur(t){if(!t.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}Qt({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1},handler:function(t,e){var r=cr(e[0],t);return{type:"delimsizing",mode:t.parser.mode,size:lr[t.funcName].size,mclass:lr[t.funcName].mclass,delim:r.text}},htmlBuilder:function(t,e){return"."===t.delim?Dt.makeSpan([t.mclass]):or(t.delim,t.size,e,t.mode,[t.mclass])},mathmlBuilder:function(t){var e=[];"."!==t.delim&&e.push(be(t.delim,t.mode));var r=new ve.MathNode("mo",e);return"mopen"===t.mclass||"mclose"===t.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r}}),Qt({type:"leftright-right",names:["\\right"],props:{numArgs:1},handler:function(t,e){var r=t.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new o("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:t.parser.mode,delim:cr(e[0],t).text,color:r}}}),Qt({type:"leftright",names:["\\left"],props:{numArgs:1},handler:function(t,e){var r=cr(e[0],t),a=t.parser;++a.leftrightDepth;var n=a.parseExpression(!1);--a.leftrightDepth,a.expect("\\right",!1);var i=Ft(a.parseFunction(),"leftright-right");return{type:"leftright",mode:a.mode,body:n,left:r.text,right:i.delim,rightColor:i.color}},htmlBuilder:function(t,e){ur(t);for(var r,a,n=se(t.body,e,!0,["mopen","mclose"]),i=0,o=0,s=!1,h=0;h<n.length;h++)n[h].isMiddle?s=!0:(i=Math.max(n[h].height,i),o=Math.max(n[h].depth,o));if(i*=e.sizeMultiplier,o*=e.sizeMultiplier,r="."===t.left?ce(e,["mopen"]):hr(t.left,i,o,e,t.mode,["mopen"]),n.unshift(r),s)for(var l=1;l<n.length;l++){var m=n[l].isMiddle;m&&(n[l]=hr(m.delim,i,o,m.options,t.mode,[]))}if("."===t.right)a=ce(e,["mclose"]);else{var c=t.rightColor?e.withColor(t.rightColor):e;a=hr(t.right,i,o,c,t.mode,["mclose"])}return n.push(a),Dt.makeSpan(["minner"],n,e)},mathmlBuilder:function(t,e){ur(t);var r=ke(t.body,e);if("."!==t.left){var a=new ve.MathNode("mo",[be(t.left,t.mode)]);a.setAttribute("fence","true"),r.unshift(a)}if("."!==t.right){var n=new ve.MathNode("mo",[be(t.right,t.mode)]);n.setAttribute("fence","true"),t.rightColor&&n.setAttribute("mathcolor",t.rightColor),r.push(n)}return ye(r)}}),Qt({type:"middle",names:["\\middle"],props:{numArgs:1},handler:function(t,e){var r=cr(e[0],t);if(!t.parser.leftrightDepth)throw new o("\\middle without preceding \\left",r);return{type:"middle",mode:t.parser.mode,delim:r.text}},htmlBuilder:function(t,e){var r;if("."===t.delim)r=ce(e,[]);else{r=or(t.delim,1,e,t.mode,[]);var a={delim:t.delim,options:e};r.isMiddle=a}return r},mathmlBuilder:function(t,e){var r="\\vert"===t.delim||"|"===t.delim?be("|","text"):be(t.delim,t.mode),a=new ve.MathNode("mo",[r]);return a.setAttribute("fence","true"),a.setAttribute("lspace","0.05em"),a.setAttribute("rspace","0.05em"),a}});var pr=function(t,e){var r,a,n=Dt.wrapFragment(ue(t.body,e),e),i=t.label.substr(1),o=e.sizeMultiplier,s=0,h=c.isCharacterBox(t.body);if("sout"===i)(r=Dt.makeSpan(["stretchy","sout"])).height=e.fontMetrics().defaultRuleThickness/o,s=-.5*e.fontMetrics().xHeight;else{/cancel/.test(i)?h||n.classes.push("cancel-pad"):n.classes.push("boxpad");var l=0,m=0;/box/.test(i)?(m=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness),l=e.fontMetrics().fboxsep+("colorbox"===i?0:m)):l=h?.2:0,r=Ie(n,i,l,e),/fbox|boxed|fcolorbox/.test(i)&&(r.style.borderStyle="solid",r.style.borderWidth=m+"em"),s=n.depth+l,t.backgroundColor&&(r.style.backgroundColor=t.backgroundColor,t.borderColor&&(r.style.borderColor=t.borderColor))}return a=t.backgroundColor?Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:r,shift:s},{type:"elem",elem:n,shift:0}]},e):Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:n,shift:0},{type:"elem",elem:r,shift:s,wrapperClasses:/cancel/.test(i)?["svg-align"]:[]}]},e),/cancel/.test(i)&&(a.height=n.height,a.depth=n.depth),/cancel/.test(i)&&!h?Dt.makeSpan(["mord","cancel-lap"],[a],e):Dt.makeSpan(["mord"],[a],e)},dr=function(t,e){var r=0,a=new ve.MathNode(t.label.indexOf("colorbox")>-1?"mpadded":"menclose",[Me(t.body,e)]);switch(t.label){case"\\cancel":a.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":a.setAttribute("notation","downdiagonalstrike");break;case"\\sout":a.setAttribute("notation","horizontalstrike");break;case"\\fbox":a.setAttribute("notation","box");break;case"\\fcolorbox":case"\\colorbox":if(r=e.fontMetrics().fboxsep*e.fontMetrics().ptPerEm,a.setAttribute("width","+"+2*r+"pt"),a.setAttribute("height","+"+2*r+"pt"),a.setAttribute("lspace",r+"pt"),a.setAttribute("voffset",r+"pt"),"\\fcolorbox"===t.label){var n=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness);a.setAttribute("style","border: "+n+"em solid "+String(t.borderColor))}break;case"\\xcancel":a.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return t.backgroundColor&&a.setAttribute("mathbackground",t.backgroundColor),a};Qt({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,greediness:3,argTypes:["color","text"]},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=Ft(e[0],"color-token").color,o=e[1];return{type:"enclose",mode:a.mode,label:n,backgroundColor:i,body:o}},htmlBuilder:pr,mathmlBuilder:dr}),Qt({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,greediness:3,argTypes:["color","color","text"]},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=Ft(e[0],"color-token").color,o=Ft(e[1],"color-token").color,s=e[2];return{type:"enclose",mode:a.mode,label:n,backgroundColor:o,borderColor:i,body:s}},htmlBuilder:pr,mathmlBuilder:dr}),Qt({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler:function(t,e){return{type:"enclose",mode:t.parser.mode,label:"\\fbox",body:e[0]}}}),Qt({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout"],props:{numArgs:1},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=e[0];return{type:"enclose",mode:a.mode,label:n,body:i}},htmlBuilder:pr,mathmlBuilder:dr});var fr={};function gr(t){for(var e=t.type,r=t.names,a=t.props,n=t.handler,i=t.htmlBuilder,o=t.mathmlBuilder,s={type:e,numArgs:a.numArgs||0,greediness:1,allowedInText:!1,numOptionalArgs:0,handler:n},h=0;h<r.length;++h)fr[r[h]]=s;i&&(Kt[e]=i),o&&(Jt[e]=o)}function xr(t){var e=[];t.consumeSpaces();for(var r=t.fetch().text;"\\hline"===r||"\\hdashline"===r;)t.consume(),e.push("\\hdashline"===r),t.consumeSpaces(),r=t.fetch().text;return e}function vr(t,e,r){var a=e.hskipBeforeAndAfter,n=e.addJot,i=e.cols,s=e.arraystretch,h=e.colSeparationType;if(t.gullet.beginGroup(),t.gullet.macros.set("\\\\","\\cr"),!s){var l=t.gullet.expandMacroAsText("\\arraystretch");if(null==l)s=1;else if(!(s=parseFloat(l))||s<0)throw new o("Invalid \\arraystretch: "+l)}t.gullet.beginGroup();var m=[],c=[m],u=[],p=[];for(p.push(xr(t));;){var d=t.parseExpression(!1,"\\cr");t.gullet.endGroup(),t.gullet.beginGroup(),d={type:"ordgroup",mode:t.mode,body:d},r&&(d={type:"styling",mode:t.mode,style:r,body:[d]}),m.push(d);var f=t.fetch().text;if("&"===f)t.consume();else{if("\\end"===f){1===m.length&&"styling"===d.type&&0===d.body[0].body.length&&c.pop(),p.length<c.length+1&&p.push([]);break}if("\\cr"!==f)throw new o("Expected & or \\\\ or \\cr or \\end",t.nextToken);var g=Ft(t.parseFunction(),"cr");u.push(g.size),p.push(xr(t)),m=[],c.push(m)}}return t.gullet.endGroup(),t.gullet.endGroup(),{type:"array",mode:t.mode,addJot:n,arraystretch:s,body:c,cols:i,rowGaps:u,hskipBeforeAndAfter:a,hLinesBeforeRow:p,colSeparationType:h}}function br(t){return"d"===t.substr(0,1)?"display":"text"}var yr=function(t,e){var r,a,n=t.body.length,i=t.hLinesBeforeRow,s=0,h=new Array(n),l=[],m=Math.max(e.fontMetrics().arrayRuleWidth,e.minRuleThickness),u=1/e.fontMetrics().ptPerEm,p=5*u;t.colSeparationType&&"small"===t.colSeparationType&&(p=e.havingStyle(w.SCRIPT).sizeMultiplier/e.sizeMultiplier*.2778);var d=12*u,f=3*u,g=t.arraystretch*d,x=.7*g,v=.3*g,b=0;function y(t){for(var e=0;e<t.length;++e)e>0&&(b+=.25),l.push({pos:b,isDashed:t[e]})}for(y(i[0]),r=0;r<t.body.length;++r){var k=t.body[r],S=x,M=v;s<k.length&&(s=k.length);var z=new Array(k.length);for(a=0;a<k.length;++a){var A=ue(k[a],e);M<A.depth&&(M=A.depth),S<A.height&&(S=A.height),z[a]=A}var T=t.rowGaps[r],B=0;T&&(B=Tt(T,e))>0&&(M<(B+=v)&&(M=B),B=0),t.addJot&&(M+=f),z.height=S,z.depth=M,b+=S,z.pos=b,b+=M+B,h[r]=z,y(i[r+1])}var C,q,N=b/2+e.fontMetrics().axisHeight,I=t.cols||[],R=[];for(a=0,q=0;a<s||q<I.length;++a,++q){for(var O=I[q]||{},E=!0;"separator"===O.type;){if(E||((C=Dt.makeSpan(["arraycolsep"],[])).style.width=e.fontMetrics().doubleRuleSep+"em",R.push(C)),"|"!==O.separator&&":"!==O.separator)throw new o("Invalid separator type: "+O.separator);var L="|"===O.separator?"solid":"dashed",H=Dt.makeSpan(["vertical-separator"],[],e);H.style.height=b+"em",H.style.borderRightWidth=m+"em",H.style.borderRightStyle=L,H.style.margin="0 -"+m/2+"em",H.style.verticalAlign=-(b-N)+"em",R.push(H),O=I[++q]||{},E=!1}if(!(a>=s)){var P=void 0;(a>0||t.hskipBeforeAndAfter)&&0!==(P=c.deflt(O.pregap,p))&&((C=Dt.makeSpan(["arraycolsep"],[])).style.width=P+"em",R.push(C));var D=[];for(r=0;r<n;++r){var F=h[r],V=F[a];if(V){var U=F.pos-N;V.depth=F.depth,V.height=F.height,D.push({type:"elem",elem:V,shift:U})}}D=Dt.makeVList({positionType:"individualShift",children:D},e),D=Dt.makeSpan(["col-align-"+(O.align||"c")],[D]),R.push(D),(a<s-1||t.hskipBeforeAndAfter)&&0!==(P=c.deflt(O.postgap,p))&&((C=Dt.makeSpan(["arraycolsep"],[])).style.width=P+"em",R.push(C))}}if(h=Dt.makeSpan(["mtable"],R),l.length>0){for(var G=Dt.makeLineSpan("hline",e,m),Y=Dt.makeLineSpan("hdashline",e,m),W=[{type:"elem",elem:h,shift:0}];l.length>0;){var X=l.pop(),_=X.pos-N;X.isDashed?W.push({type:"elem",elem:Y,shift:_}):W.push({type:"elem",elem:G,shift:_})}h=Dt.makeVList({positionType:"individualShift",children:W},e)}return Dt.makeSpan(["mord"],[h],e)},wr={c:"center ",l:"left ",r:"right "},kr=function(t,e){var r=new ve.MathNode("mtable",t.body.map(function(t){return new ve.MathNode("mtr",t.map(function(t){return new ve.MathNode("mtd",[Me(t,e)])}))})),a=.5===t.arraystretch?.1:.16+t.arraystretch-1+(t.addJot?.09:0);r.setAttribute("rowspacing",a+"em");var n="",i="";if(t.cols){var o=t.cols,s="",h=!1,l=0,m=o.length;"separator"===o[0].type&&(n+="top ",l=1),"separator"===o[o.length-1].type&&(n+="bottom ",m-=1);for(var c=l;c<m;c++)"align"===o[c].type?(i+=wr[o[c].align],h&&(s+="none "),h=!0):"separator"===o[c].type&&h&&(s+="|"===o[c].separator?"solid ":"dashed ",h=!1);r.setAttribute("columnalign",i.trim()),/[sd]/.test(s)&&r.setAttribute("columnlines",s.trim())}if("align"===t.colSeparationType){for(var u=t.cols||[],p="",d=1;d<u.length;d++)p+=d%2?"0em ":"1em ";r.setAttribute("columnspacing",p.trim())}else"alignat"===t.colSeparationType?r.setAttribute("columnspacing","0em"):"small"===t.colSeparationType?r.setAttribute("columnspacing","0.2778em"):r.setAttribute("columnspacing","1em");var f="",g=t.hLinesBeforeRow;n+=g[0].length>0?"left ":"",n+=g[g.length-1].length>0?"right ":"";for(var x=1;x<g.length-1;x++)f+=0===g[x].length?"none ":g[x][0]?"dashed ":"solid ";return/[sd]/.test(f)&&r.setAttribute("rowlines",f.trim()),""!==n&&(r=new ve.MathNode("menclose",[r])).setAttribute("notation",n.trim()),t.arraystretch&&t.arraystretch<1&&(r=new ve.MathNode("mstyle",[r])).setAttribute("scriptlevel","1"),r},Sr=function(t,e){var r,a=[],n=vr(t.parser,{cols:a,addJot:!0},"display"),i=0,s={type:"ordgroup",mode:t.mode,body:[]},h=Vt(e[0],"ordgroup");if(h){for(var l="",m=0;m<h.body.length;m++){l+=Ft(h.body[m],"textord").text}r=Number(l),i=2*r}var c=!i;n.body.forEach(function(t){for(var e=1;e<t.length;e+=2){var a=Ft(t[e],"styling");Ft(a.body[0],"ordgroup").body.unshift(s)}if(c)i<t.length&&(i=t.length);else{var n=t.length/2;if(r<n)throw new o("Too many math in a row: expected "+r+", but got "+n,t[0])}});for(var u=0;u<i;++u){var p="r",d=0;u%2==1?p="l":u>0&&c&&(d=1),a[u]={type:"align",align:p,pregap:d,postgap:0}}return n.colSeparationType=c?"align":"alignat",n};gr({type:"array",names:["array","darray"],props:{numArgs:1},handler:function(t,e){var r={cols:(Yt(e[0])?[e[0]]:Ft(e[0],"ordgroup").body).map(function(t){var e=Gt(t).text;if(-1!=="lcr".indexOf(e))return{type:"align",align:e};if("|"===e)return{type:"separator",separator:"|"};if(":"===e)return{type:"separator",separator:":"};throw new o("Unknown column alignment: "+e,t)}),hskipBeforeAndAfter:!0};return vr(t.parser,r,br(t.envName))},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix"],props:{numArgs:0},handler:function(t){var e={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[t.envName],r=vr(t.parser,{hskipBeforeAndAfter:!1},br(t.envName));return e?{type:"leftright",mode:t.mode,body:[r],left:e[0],right:e[1],rightColor:void 0}:r},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["smallmatrix"],props:{numArgs:0},handler:function(t){var e=vr(t.parser,{arraystretch:.5},"script");return e.colSeparationType="small",e},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["subarray"],props:{numArgs:1},handler:function(t,e){var r=(Yt(e[0])?[e[0]]:Ft(e[0],"ordgroup").body).map(function(t){var e=Gt(t).text;if(-1!=="lc".indexOf(e))return{type:"align",align:e};throw new o("Unknown column alignment: "+e,t)});if(r.length>1)throw new o("{subarray} can contain only one column");var a={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5};if((a=vr(t.parser,a,"script")).body[0].length>1)throw new o("{subarray} can contain only one column");return a},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["cases","dcases"],props:{numArgs:0},handler:function(t){var e=vr(t.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},br(t.envName));return{type:"leftright",mode:t.mode,body:[e],left:"\\{",right:".",rightColor:void 0}},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["aligned"],props:{numArgs:0},handler:Sr,htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["gathered"],props:{numArgs:0},handler:function(t){return vr(t.parser,{cols:[{type:"align",align:"c"}],addJot:!0},"display")},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["alignedat"],props:{numArgs:1},handler:Sr,htmlBuilder:yr,mathmlBuilder:kr}),Qt({type:"text",names:["\\hline","\\hdashline"],props:{numArgs:0,allowedInText:!0,allowedInMath:!0},handler:function(t,e){throw new o(t.funcName+" valid only within array environment")}});var Mr=fr;Qt({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];if("ordgroup"!==n.type)throw new o("Invalid environment name",n);for(var i="",s=0;s<n.body.length;++s)i+=Ft(n.body[s],"textord").text;if("\\begin"===a){if(!Mr.hasOwnProperty(i))throw new o("No such environment: "+i,n);var h=Mr[i],l=r.parseArguments("\\begin{"+i+"}",h),m=l.args,c=l.optArgs,u={mode:r.mode,envName:i,parser:r},p=h.handler(u,m,c);r.expect("\\end",!1);var d=r.nextToken,f=Ft(r.parseFunction(),"environment");if(f.name!==i)throw new o("Mismatch: \\begin{"+i+"} matched by \\end{"+f.name+"}",d);return p}return{type:"environment",mode:r.mode,name:i,nameGroup:n}}});var zr=Dt.makeSpan;function Ar(t,e){var r=se(t.body,e,!0);return zr([t.mclass],r,e)}function Tr(t,e){var r,a=ke(t.body,e);return"minner"===t.mclass?ve.newDocumentFragment(a):("mord"===t.mclass?t.isCharacterBox?(r=a[0]).type="mi":r=new ve.MathNode("mi",a):(t.isCharacterBox?(r=a[0]).type="mo":r=new ve.MathNode("mo",a),"mbin"===t.mclass?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):"mpunct"===t.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):"mopen"!==t.mclass&&"mclose"!==t.mclass||(r.attributes.lspace="0em",r.attributes.rspace="0em")),r)}Qt({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"mclass",mode:r.mode,mclass:"m"+a.substr(5),body:ee(n),isCharacterBox:c.isCharacterBox(n)}},htmlBuilder:Ar,mathmlBuilder:Tr});var Br=function(t){var e="ordgroup"===t.type&&t.body.length?t.body[0]:t;return"atom"!==e.type||"bin"!==e.family&&"rel"!==e.family?"mord":"m"+e.family};Qt({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler:function(t,e){return{type:"mclass",mode:t.parser.mode,mclass:Br(e[0]),body:[e[1]],isCharacterBox:c.isCharacterBox(e[1])}}}),Qt({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler:function(t,e){var r,a=t.parser,n=t.funcName,i=e[1],o=e[0];r="\\stackrel"!==n?Br(i):"mrel";var s={type:"op",mode:i.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==n,body:ee(i)},h={type:"supsub",mode:o.mode,base:s,sup:"\\underset"===n?null:o,sub:"\\underset"===n?o:null};return{type:"mclass",mode:a.mode,mclass:r,body:[h],isCharacterBox:c.isCharacterBox(h)}},htmlBuilder:Ar,mathmlBuilder:Tr});var Cr=function(t,e){var r=t.font,a=e.withFont(r);return ue(t.body,a)},qr=function(t,e){var r=t.font,a=e.withFont(r);return Me(t.body,a)},Nr={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};Qt({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,greediness:2},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0],i=a;return i in Nr&&(i=Nr[i]),{type:"font",mode:r.mode,font:i.slice(1),body:n}},htmlBuilder:Cr,mathmlBuilder:qr}),Qt({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1,greediness:2},handler:function(t,e){var r=t.parser,a=e[0],n=c.isCharacterBox(a);return{type:"mclass",mode:r.mode,mclass:Br(a),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:a}],isCharacterBox:n}}}),Qt({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it"],props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=t.breakOnTokenText,i=r.mode,o=r.parseExpression(!0,n);return{type:"font",mode:i,font:"math"+a.slice(1),body:{type:"ordgroup",mode:r.mode,body:o}}},htmlBuilder:Cr,mathmlBuilder:qr});var Ir=function(t,e){var r=e;return"display"===t?r=r.id>=w.SCRIPT.id?r.text():w.DISPLAY:"text"===t&&r.size===w.DISPLAY.size?r=w.TEXT:"script"===t?r=w.SCRIPT:"scriptscript"===t&&(r=w.SCRIPTSCRIPT),r},Rr=function(t,e){var r,a=Ir(t.size,e.style),n=a.fracNum(),i=a.fracDen();r=e.havingStyle(n);var o=ue(t.numer,r,e);if(t.continued){var s=8.5/e.fontMetrics().ptPerEm,h=3.5/e.fontMetrics().ptPerEm;o.height=o.height<s?s:o.height,o.depth=o.depth<h?h:o.depth}r=e.havingStyle(i);var l,m,c,u,p,d,f,g,x,v,b=ue(t.denom,r,e);if(t.hasBarLine?(t.barSize?(m=Tt(t.barSize,e),l=Dt.makeLineSpan("frac-line",e,m)):l=Dt.makeLineSpan("frac-line",e),m=l.height,c=l.height):(l=null,m=0,c=e.fontMetrics().defaultRuleThickness),a.size===w.DISPLAY.size||"display"===t.size?(u=e.fontMetrics().num1,p=m>0?3*c:7*c,d=e.fontMetrics().denom1):(m>0?(u=e.fontMetrics().num2,p=c):(u=e.fontMetrics().num3,p=3*c),d=e.fontMetrics().denom2),l){var y=e.fontMetrics().axisHeight;u-o.depth-(y+.5*m)<p&&(u+=p-(u-o.depth-(y+.5*m))),y-.5*m-(b.height-d)<p&&(d+=p-(y-.5*m-(b.height-d)));var k=-(y-.5*m);f=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:b,shift:d},{type:"elem",elem:l,shift:k},{type:"elem",elem:o,shift:-u}]},e)}else{var S=u-o.depth-(b.height-d);S<p&&(u+=.5*(p-S),d+=.5*(p-S)),f=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:b,shift:d},{type:"elem",elem:o,shift:-u}]},e)}return r=e.havingStyle(a),f.height*=r.sizeMultiplier/e.sizeMultiplier,f.depth*=r.sizeMultiplier/e.sizeMultiplier,g=a.size===w.DISPLAY.size?e.fontMetrics().delim1:e.fontMetrics().delim2,x=null==t.leftDelim?ce(e,["mopen"]):sr(t.leftDelim,g,!0,e.havingStyle(a),t.mode,["mopen"]),v=t.continued?Dt.makeSpan([]):null==t.rightDelim?ce(e,["mclose"]):sr(t.rightDelim,g,!0,e.havingStyle(a),t.mode,["mclose"]),Dt.makeSpan(["mord"].concat(r.sizingClasses(e)),[x,Dt.makeSpan(["mfrac"],[f]),v],e)},Or=function(t,e){var r=new ve.MathNode("mfrac",[Me(t.numer,e),Me(t.denom,e)]);if(t.hasBarLine){if(t.barSize){var a=Tt(t.barSize,e);r.setAttribute("linethickness",a+"em")}}else r.setAttribute("linethickness","0px");var n=Ir(t.size,e.style);if(n.size!==e.style.size){r=new ve.MathNode("mstyle",[r]);var i=n.size===w.DISPLAY.size?"true":"false";r.setAttribute("displaystyle",i),r.setAttribute("scriptlevel","0")}if(null!=t.leftDelim||null!=t.rightDelim){var o=[];if(null!=t.leftDelim){var s=new ve.MathNode("mo",[new ve.TextNode(t.leftDelim.replace("\\",""))]);s.setAttribute("fence","true"),o.push(s)}if(o.push(r),null!=t.rightDelim){var h=new ve.MathNode("mo",[new ve.TextNode(t.rightDelim.replace("\\",""))]);h.setAttribute("fence","true"),o.push(h)}return ye(o)}return r};Qt({type:"genfrac",names:["\\cfrac","\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,greediness:2},handler:function(t,e){var r,a=t.parser,n=t.funcName,i=e[0],o=e[1],s=null,h=null,l="auto";switch(n){case"\\cfrac":case"\\dfrac":case"\\frac":case"\\tfrac":r=!0;break;case"\\\\atopfrac":r=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":r=!1,s="(",h=")";break;case"\\\\bracefrac":r=!1,s="\\{",h="\\}";break;case"\\\\brackfrac":r=!1,s="[",h="]";break;default:throw new Error("Unrecognized genfrac command")}switch(n){case"\\cfrac":case"\\dfrac":case"\\dbinom":l="display";break;case"\\tfrac":case"\\tbinom":l="text"}return{type:"genfrac",mode:a.mode,continued:"\\cfrac"===n,numer:i,denom:o,hasBarLine:r,leftDelim:s,rightDelim:h,size:l,barSize:null}},htmlBuilder:Rr,mathmlBuilder:Or}),Qt({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler:function(t){var e,r=t.parser,a=t.funcName,n=t.token;switch(a){case"\\over":e="\\frac";break;case"\\choose":e="\\binom";break;case"\\atop":e="\\\\atopfrac";break;case"\\brace":e="\\\\bracefrac";break;case"\\brack":e="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:r.mode,replaceWith:e,token:n}}});var Er=["display","text","script","scriptscript"],Lr=function(t){var e=null;return t.length>0&&(e="."===(e=t)?null:e),e};Qt({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,greediness:6,argTypes:["math","math","size","text","math","math"]},handler:function(t,e){var r=t.parser,a=e[4],n=e[5],i=Vt(e[0],"atom");i&&(i=Ut(e[0],"open"));var o=i?Lr(i.text):null,s=Vt(e[1],"atom");s&&(s=Ut(e[1],"close"));var h,l=s?Lr(s.text):null,m=Ft(e[2],"size"),c=null;h=!!m.isBlank||(c=m.value).number>0;var u="auto",p=Vt(e[3],"ordgroup");if(p){if(p.body.length>0){var d=Ft(p.body[0],"textord");u=Er[Number(d.text)]}}else p=Ft(e[3],"textord"),u=Er[Number(p.text)];return{type:"genfrac",mode:r.mode,numer:a,denom:n,continued:!1,hasBarLine:h,barSize:c,leftDelim:o,rightDelim:l,size:u}},htmlBuilder:Rr,mathmlBuilder:Or}),Qt({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler:function(t,e){var r=t.parser,a=(t.funcName,t.token);return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:Ft(e[0],"size").value,token:a}}}),Qt({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:function(t,e){var r=t.parser,a=(t.funcName,e[0]),n=function(t){if(!t)throw new Error("Expected non-null, but got "+String(t));return t}(Ft(e[1],"infix").size),i=e[2],o=n.number>0;return{type:"genfrac",mode:r.mode,numer:a,denom:i,continued:!1,hasBarLine:o,barSize:n,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:Rr,mathmlBuilder:Or});var Hr=function(t,e){var r,a,n=e.style,i=Vt(t,"supsub");i?(r=i.sup?ue(i.sup,e.havingStyle(n.sup()),e):ue(i.sub,e.havingStyle(n.sub()),e),a=Ft(i.base,"horizBrace")):a=Ft(t,"horizBrace");var o,s=ue(a.base,e.havingBaseStyle(w.DISPLAY)),h=Oe(a,e);if(a.isOver?(o=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:h}]},e)).children[0].children[0].children[1].classes.push("svg-align"):(o=Dt.makeVList({positionType:"bottom",positionData:s.depth+.1+h.height,children:[{type:"elem",elem:h},{type:"kern",size:.1},{type:"elem",elem:s}]},e)).children[0].children[0].children[0].classes.push("svg-align"),r){var l=Dt.makeSpan(["mord",a.isOver?"mover":"munder"],[o],e);o=a.isOver?Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:l},{type:"kern",size:.2},{type:"elem",elem:r}]},e):Dt.makeVList({positionType:"bottom",positionData:l.depth+.2+r.height+r.depth,children:[{type:"elem",elem:r},{type:"kern",size:.2},{type:"elem",elem:l}]},e)}return Dt.makeSpan(["mord",a.isOver?"mover":"munder"],[o],e)};Qt({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName;return{type:"horizBrace",mode:r.mode,label:a,isOver:/^\\over/.test(a),base:e[0]}},htmlBuilder:Hr,mathmlBuilder:function(t,e){var r=Re(t.label);return new ve.MathNode(t.isOver?"mover":"munder",[Me(t.base,e),r])}}),Qt({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[1],n=Ft(e[0],"url").url;return r.settings.isTrusted({command:"\\href",url:n})?{type:"href",mode:r.mode,href:n,body:ee(a)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:function(t,e){var r=se(t.body,e,!1);return Dt.makeAnchor(t.href,[],r,e)},mathmlBuilder:function(t,e){var r=Se(t.body,e);return r instanceof ge||(r=new ge("mrow",[r])),r.setAttribute("href",t.href),r}}),Qt({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=Ft(e[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:a}))return r.formatUnsupportedCmd("\\url");for(var n=[],i=0;i<a.length;i++){var o=a[i];"~"===o&&(o="\\textasciitilde"),n.push({type:"textord",mode:"text",text:o})}var s={type:"text",mode:r.mode,font:"\\texttt",body:n};return{type:"href",mode:r.mode,href:a,body:ee(s)}}}),Qt({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:function(t,e){return{type:"htmlmathml",mode:t.parser.mode,html:ee(e[0]),mathml:ee(e[1])}},htmlBuilder:function(t,e){var r=se(t.html,e,!1);return Dt.makeFragment(r)},mathmlBuilder:function(t,e){return Se(t.mathml,e)}});var Pr=function(t){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(t))return{number:+t,unit:"bp"};var e=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(t);if(!e)throw new o("Invalid size: '"+t+"' in \\includegraphics");var r={number:+(e[1]+e[2]),unit:e[3]};if(!At(r))throw new o("Invalid unit: '"+r.unit+"' in \\includegraphics.");return r};Qt({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:function(t,e,r){var a=t.parser,n={number:0,unit:"em"},i={number:.9,unit:"em"},s={number:0,unit:"em"},h="";if(r[0])for(var l=Ft(r[0],"raw").string.split(","),m=0;m<l.length;m++){var c=l[m].split("=");if(2===c.length){var u=c[1].trim();switch(c[0].trim()){case"alt":h=u;break;case"width":n=Pr(u);break;case"height":i=Pr(u);break;case"totalheight":s=Pr(u);break;default:throw new o("Invalid key: '"+c[0]+"' in \\includegraphics.")}}}var p=Ft(e[0],"url").url;return""===h&&(h=(h=(h=p).replace(/^.*[\\\/]/,"")).substring(0,h.lastIndexOf("."))),a.settings.isTrusted({command:"\\includegraphics",url:p})?{type:"includegraphics",mode:a.mode,alt:h,width:n,height:i,totalheight:s,src:p}:a.formatUnsupportedCmd("\\includegraphics")},htmlBuilder:function(t,e){var r=Tt(t.height,e),a=0;t.totalheight.number>0&&(a=Tt(t.totalheight,e)-r,a=Number(a.toFixed(2)));var n=0;t.width.number>0&&(n=Tt(t.width,e));var i={height:r+a+"em"};n>0&&(i.width=n+"em"),a>0&&(i.verticalAlign=-a+"em");var o=new R(t.src,t.alt,i);return o.height=r,o.depth=a,o},mathmlBuilder:function(t,e){var r=new ve.MathNode("mglyph",[]);r.setAttribute("alt",t.alt);var a=Tt(t.height,e),n=0;if(t.totalheight.number>0&&(n=(n=Tt(t.totalheight,e)-a).toFixed(2),r.setAttribute("valign","-"+n+"em")),r.setAttribute("height",a+n+"em"),t.width.number>0){var i=Tt(t.width,e);r.setAttribute("width",i+"em")}return r.setAttribute("src",t.src),r}}),Qt({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=Ft(e[0],"size");if(r.settings.strict){var i="m"===a[1],o="mu"===n.value.unit;i?(o||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" supports only mu units, not "+n.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" works only in math mode")):o&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:n.value}},htmlBuilder:function(t,e){return Dt.makeGlue(t.dimension,e)},mathmlBuilder:function(t,e){var r=Tt(t.dimension,e);return new ve.SpaceNode(r)}}),Qt({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"lap",mode:r.mode,alignment:a.slice(5),body:n}},htmlBuilder:function(t,e){var r;"clap"===t.alignment?(r=Dt.makeSpan([],[ue(t.body,e)]),r=Dt.makeSpan(["inner"],[r],e)):r=Dt.makeSpan(["inner"],[ue(t.body,e)]);var a=Dt.makeSpan(["fix"],[]),n=Dt.makeSpan([t.alignment],[r,a],e),i=Dt.makeSpan(["strut"]);return i.style.height=n.height+n.depth+"em",i.style.verticalAlign=-n.depth+"em",n.children.unshift(i),n=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:n}]},e),Dt.makeSpan(["mord"],[n],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mpadded",[Me(t.body,e)]);if("rlap"!==t.alignment){var a="llap"===t.alignment?"-1":"-0.5";r.setAttribute("lspace",a+"width")}return r.setAttribute("width","0px"),r}}),Qt({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler:function(t,e){var r=t.funcName,a=t.parser,n=a.mode;a.switchMode("math");var i="\\("===r?"\\)":"$",o=a.parseExpression(!1,i);return a.expect(i),a.switchMode(n),{type:"styling",mode:a.mode,style:"text",body:o}}}),Qt({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler:function(t,e){throw new o("Mismatched "+t.funcName)}});var Dr=function(t,e){switch(e.style.size){case w.DISPLAY.size:return t.display;case w.TEXT.size:return t.text;case w.SCRIPT.size:return t.script;case w.SCRIPTSCRIPT.size:return t.scriptscript;default:return t.text}};Qt({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4},handler:function(t,e){return{type:"mathchoice",mode:t.parser.mode,display:ee(e[0]),text:ee(e[1]),script:ee(e[2]),scriptscript:ee(e[3])}},htmlBuilder:function(t,e){var r=Dr(t,e),a=se(r,e,!1);return Dt.makeFragment(a)},mathmlBuilder:function(t,e){var r=Dr(t,e);return Se(r,e)}});var Fr=function(t,e,r,a,n,i,o){var s,h,l;if(t=Dt.makeSpan([],[t]),e){var m=ue(e,a.havingStyle(n.sup()),a);h={elem:m,kern:Math.max(a.fontMetrics().bigOpSpacing1,a.fontMetrics().bigOpSpacing3-m.depth)}}if(r){var c=ue(r,a.havingStyle(n.sub()),a);s={elem:c,kern:Math.max(a.fontMetrics().bigOpSpacing2,a.fontMetrics().bigOpSpacing4-c.height)}}if(h&&s){var u=a.fontMetrics().bigOpSpacing5+s.elem.height+s.elem.depth+s.kern+t.depth+o;l=Dt.makeVList({positionType:"bottom",positionData:u,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:s.elem,marginLeft:-i+"em"},{type:"kern",size:s.kern},{type:"elem",elem:t},{type:"kern",size:h.kern},{type:"elem",elem:h.elem,marginLeft:i+"em"},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}else if(s){var p=t.height-o;l=Dt.makeVList({positionType:"top",positionData:p,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:s.elem,marginLeft:-i+"em"},{type:"kern",size:s.kern},{type:"elem",elem:t}]},a)}else{if(!h)return t;var d=t.depth+o;l=Dt.makeVList({positionType:"bottom",positionData:d,children:[{type:"elem",elem:t},{type:"kern",size:h.kern},{type:"elem",elem:h.elem,marginLeft:i+"em"},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}return Dt.makeSpan(["mop","op-limits"],[l],a)},Vr=["\\smallint"],Ur=function(t,e){var r,a,n,i=!1,o=Vt(t,"supsub");o?(r=o.sup,a=o.sub,n=Ft(o.base,"op"),i=!0):n=Ft(t,"op");var s,h=e.style,l=!1;if(h.size===w.DISPLAY.size&&n.symbol&&!c.contains(Vr,n.name)&&(l=!0),n.symbol){var m=l?"Size2-Regular":"Size1-Regular",u="";if("\\oiint"!==n.name&&"\\oiiint"!==n.name||(u=n.name.substr(1),n.name="oiint"===u?"\\iint":"\\iiint"),s=Dt.makeSymbol(n.name,m,"math",e,["mop","op-symbol",l?"large-op":"small-op"]),u.length>0){var p=s.italic,d=Dt.staticSvg(u+"Size"+(l?"2":"1"),e);s=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:s,shift:0},{type:"elem",elem:d,shift:l?.08:0}]},e),n.name="\\"+u,s.classes.unshift("mop"),s.italic=p}}else if(n.body){var f=se(n.body,e,!0);1===f.length&&f[0]instanceof E?(s=f[0]).classes[0]="mop":s=Dt.makeSpan(["mop"],Dt.tryCombineChars(f),e)}else{for(var g=[],x=1;x<n.name.length;x++)g.push(Dt.mathsym(n.name[x],n.mode,e));s=Dt.makeSpan(["mop"],g,e)}var v=0,b=0;return(s instanceof E||"\\oiint"===n.name||"\\oiiint"===n.name)&&!n.suppressBaseShift&&(v=(s.height-s.depth)/2-e.fontMetrics().axisHeight,b=s.italic),i?Fr(s,r,a,e,h,b,v):(v&&(s.style.position="relative",s.style.top=v+"em"),s)},Gr=function(t,e){var r;if(t.symbol)r=new ge("mo",[be(t.name,t.mode)]),c.contains(Vr,t.name)&&r.setAttribute("largeop","false");else if(t.body)r=new ge("mo",ke(t.body,e));else{r=new ge("mi",[new xe(t.name.slice(1))]);var a=new ge("mo",[be("\u2061","text")]);r=t.parentIsSupSub?new ge("mo",[r,a]):fe([r,a])}return r},Yr={"\u220f":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22c0":"\\bigwedge","\u22c1":"\\bigvee","\u22c2":"\\bigcap","\u22c3":"\\bigcup","\u2a00":"\\bigodot","\u2a01":"\\bigoplus","\u2a02":"\\bigotimes","\u2a04":"\\biguplus","\u2a06":"\\bigsqcup"};Qt({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220f","\u2210","\u2211","\u22c0","\u22c1","\u22c2","\u22c3","\u2a00","\u2a01","\u2a02","\u2a04","\u2a06"],props:{numArgs:0},handler:function(t,e){var r=t.parser,a=t.funcName;return 1===a.length&&(a=Yr[a]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:a}},htmlBuilder:Ur,mathmlBuilder:Gr}),Qt({type:"op",names:["\\mathop"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=e[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:ee(a)}},htmlBuilder:Ur,mathmlBuilder:Gr});var Wr={"\u222b":"\\int","\u222c":"\\iint","\u222d":"\\iiint","\u222e":"\\oint","\u222f":"\\oiint","\u2230":"\\oiiint"};Qt({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler:function(t){var e=t.parser,r=t.funcName;return{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:Ur,mathmlBuilder:Gr}),Qt({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler:function(t){var e=t.parser,r=t.funcName;return{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:Ur,mathmlBuilder:Gr}),Qt({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222b","\u222c","\u222d","\u222e","\u222f","\u2230"],props:{numArgs:0},handler:function(t){var e=t.parser,r=t.funcName;return 1===r.length&&(r=Wr[r]),{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:r}},htmlBuilder:Ur,mathmlBuilder:Gr});var Xr=function(t,e){var r,a,n,i,o=!1,s=Vt(t,"supsub");if(s?(r=s.sup,a=s.sub,n=Ft(s.base,"operatorname"),o=!0):n=Ft(t,"operatorname"),n.body.length>0){for(var h=n.body.map(function(t){var e=t.text;return"string"==typeof e?{type:"textord",mode:t.mode,text:e}:t}),l=se(h,e.withFont("mathrm"),!0),m=0;m<l.length;m++){var c=l[m];c instanceof E&&(c.text=c.text.replace(/\u2212/,"-").replace(/\u2217/,"*"))}i=Dt.makeSpan(["mop"],l,e)}else i=Dt.makeSpan(["mop"],[],e);return o?Fr(i,r,a,e,e.style,0,0):i};function _r(t,e,r){for(var a=se(t,e,!1),n=e.sizeMultiplier/r.sizeMultiplier,i=0;i<a.length;i++){var o=a[i].classes.indexOf("sizing");o<0?Array.prototype.push.apply(a[i].classes,e.sizingClasses(r)):a[i].classes[o+1]==="reset-size"+e.size&&(a[i].classes[o+1]="reset-size"+r.size),a[i].height*=n,a[i].depth*=n}return Dt.makeFragment(a)}Qt({type:"operatorname",names:["\\operatorname","\\operatorname*"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"operatorname",mode:r.mode,body:ee(n),alwaysHandleSupSub:"\\operatorname*"===a,limits:!1,parentIsSupSub:!1}},htmlBuilder:Xr,mathmlBuilder:function(t,e){for(var r=ke(t.body,e.withFont("mathrm")),a=!0,n=0;n<r.length;n++){var i=r[n];if(i instanceof ve.SpaceNode);else if(i instanceof ve.MathNode)switch(i.type){case"mi":case"mn":case"ms":case"mspace":case"mtext":break;case"mo":var o=i.children[0];1===i.children.length&&o instanceof ve.TextNode?o.text=o.text.replace(/\u2212/,"-").replace(/\u2217/,"*"):a=!1;break;default:a=!1}else a=!1}if(a){var s=r.map(function(t){return t.toText()}).join("");r=[new ve.TextNode(s)]}var h=new ve.MathNode("mi",r);h.setAttribute("mathvariant","normal");var l=new ve.MathNode("mo",[be("\u2061","text")]);return t.parentIsSupSub?new ve.MathNode("mo",[h,l]):ve.newDocumentFragment([h,l])}}),te({type:"ordgroup",htmlBuilder:function(t,e){return t.semisimple?Dt.makeFragment(se(t.body,e,!1)):Dt.makeSpan(["mord"],se(t.body,e,!0),e)},mathmlBuilder:function(t,e){return Se(t.body,e,!0)}}),Qt({type:"overline",names:["\\overline"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=e[0];return{type:"overline",mode:r.mode,body:a}},htmlBuilder:function(t,e){var r=ue(t.body,e.havingCrampedStyle()),a=Dt.makeLineSpan("overline-line",e),n=e.fontMetrics().defaultRuleThickness,i=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*n},{type:"elem",elem:a},{type:"kern",size:n}]},e);return Dt.makeSpan(["mord","overline"],[i],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[new ve.TextNode("\u203e")]);r.setAttribute("stretchy","true");var a=new ve.MathNode("mover",[Me(t.body,e),r]);return a.setAttribute("accent","true"),a}}),Qt({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[0];return{type:"phantom",mode:r.mode,body:ee(a)}},htmlBuilder:function(t,e){var r=se(t.body,e.withPhantom(),!1);return Dt.makeFragment(r)},mathmlBuilder:function(t,e){var r=ke(t.body,e);return new ve.MathNode("mphantom",r)}}),Qt({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[0];return{type:"hphantom",mode:r.mode,body:a}},htmlBuilder:function(t,e){var r=Dt.makeSpan([],[ue(t.body,e.withPhantom())]);if(r.height=0,r.depth=0,r.children)for(var a=0;a<r.children.length;a++)r.children[a].height=0,r.children[a].depth=0;return r=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r}]},e),Dt.makeSpan(["mord"],[r],e)},mathmlBuilder:function(t,e){var r=ke(ee(t.body),e),a=new ve.MathNode("mphantom",r),n=new ve.MathNode("mpadded",[a]);return n.setAttribute("height","0px"),n.setAttribute("depth","0px"),n}}),Qt({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[0];return{type:"vphantom",mode:r.mode,body:a}},htmlBuilder:function(t,e){var r=Dt.makeSpan(["inner"],[ue(t.body,e.withPhantom())]),a=Dt.makeSpan(["fix"],[]);return Dt.makeSpan(["mord","rlap"],[r,a],e)},mathmlBuilder:function(t,e){var r=ke(ee(t.body),e),a=new ve.MathNode("mphantom",r),n=new ve.MathNode("mpadded",[a]);return n.setAttribute("width","0px"),n}}),Qt({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=Ft(e[0],"size").value,n=e[1];return{type:"raisebox",mode:r.mode,dy:a,body:n}},htmlBuilder:function(t,e){var r=ue(t.body,e),a=Tt(t.dy,e);return Dt.makeVList({positionType:"shift",positionData:-a,children:[{type:"elem",elem:r}]},e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mpadded",[Me(t.body,e)]),a=t.dy.number+t.dy.unit;return r.setAttribute("voffset",a),r}}),Qt({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,argTypes:["size","size","size"]},handler:function(t,e,r){var a=t.parser,n=r[0],i=Ft(e[0],"size"),o=Ft(e[1],"size");return{type:"rule",mode:a.mode,shift:n&&Ft(n,"size").value,width:i.value,height:o.value}},htmlBuilder:function(t,e){var r=Dt.makeSpan(["mord","rule"],[],e),a=Tt(t.width,e),n=Tt(t.height,e),i=t.shift?Tt(t.shift,e):0;return r.style.borderRightWidth=a+"em",r.style.borderTopWidth=n+"em",r.style.bottom=i+"em",r.width=a,r.height=n+i,r.depth=-i,r.maxFontSize=1.125*n*e.sizeMultiplier,r},mathmlBuilder:function(t,e){var r=Tt(t.width,e),a=Tt(t.height,e),n=t.shift?Tt(t.shift,e):0,i=e.color&&e.getColor()||"black",o=new ve.MathNode("mspace");o.setAttribute("mathbackground",i),o.setAttribute("width",r+"em"),o.setAttribute("height",a+"em");var s=new ve.MathNode("mpadded",[o]);return n>=0?s.setAttribute("height","+"+n+"em"):(s.setAttribute("height",n+"em"),s.setAttribute("depth","+"+-n+"em")),s.setAttribute("voffset",n+"em"),s}});var jr=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];Qt({type:"sizing",names:jr,props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.breakOnTokenText,a=t.funcName,n=t.parser,i=n.parseExpression(!1,r);return{type:"sizing",mode:n.mode,size:jr.indexOf(a)+1,body:i}},htmlBuilder:function(t,e){var r=e.havingSize(t.size);return _r(t.body,r,e)},mathmlBuilder:function(t,e){var r=e.havingSize(t.size),a=ke(t.body,r),n=new ve.MathNode("mstyle",a);return n.setAttribute("mathsize",r.sizeMultiplier+"em"),n}}),Qt({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:function(t,e,r){var a=t.parser,n=!1,i=!1,o=r[0]&&Ft(r[0],"ordgroup");if(o)for(var s="",h=0;h<o.body.length;++h){if("t"===(s=o.body[h].text))n=!0;else{if("b"!==s){n=!1,i=!1;break}i=!0}}else n=!0,i=!0;var l=e[0];return{type:"smash",mode:a.mode,body:l,smashHeight:n,smashDepth:i}},htmlBuilder:function(t,e){var r=Dt.makeSpan([],[ue(t.body,e)]);if(!t.smashHeight&&!t.smashDepth)return r;if(t.smashHeight&&(r.height=0,r.children))for(var a=0;a<r.children.length;a++)r.children[a].height=0;if(t.smashDepth&&(r.depth=0,r.children))for(var n=0;n<r.children.length;n++)r.children[n].depth=0;var i=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord"],[i],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mpadded",[Me(t.body,e)]);return t.smashHeight&&r.setAttribute("height","0px"),t.smashDepth&&r.setAttribute("depth","0px"),r}}),Qt({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler:function(t,e,r){var a=t.parser,n=r[0],i=e[0];return{type:"sqrt",mode:a.mode,body:i,index:n}},htmlBuilder:function(t,e){var r=ue(t.body,e.havingCrampedStyle());0===r.height&&(r.height=e.fontMetrics().xHeight),r=Dt.wrapFragment(r,e);var a=e.fontMetrics().defaultRuleThickness,n=a;e.style.id<w.TEXT.id&&(n=e.fontMetrics().xHeight);var i=a+n/4,o=r.height+r.depth+i+a,s=ir(o,e),h=s.span,l=s.ruleWidth,m=s.advanceWidth,c=h.height-l;c>r.height+r.depth+i&&(i=(i+c-r.height-r.depth)/2);var u=h.height-r.height-i-l;r.style.paddingLeft=m+"em";var p=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+u)},{type:"elem",elem:h},{type:"kern",size:l}]},e);if(t.index){var d=e.havingStyle(w.SCRIPTSCRIPT),f=ue(t.index,d,e),g=.6*(p.height-p.depth),x=Dt.makeVList({positionType:"shift",positionData:-g,children:[{type:"elem",elem:f}]},e),v=Dt.makeSpan(["root"],[x]);return Dt.makeSpan(["mord","sqrt"],[v,p],e)}return Dt.makeSpan(["mord","sqrt"],[p],e)},mathmlBuilder:function(t,e){var r=t.body,a=t.index;return a?new ve.MathNode("mroot",[Me(r,e),Me(a,e)]):new ve.MathNode("msqrt",[Me(r,e)])}});var $r={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT};Qt({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.breakOnTokenText,a=t.funcName,n=t.parser,i=n.parseExpression(!0,r),o=a.slice(1,a.length-5);return{type:"styling",mode:n.mode,style:o,body:i}},htmlBuilder:function(t,e){var r=$r[t.style],a=e.havingStyle(r).withFont("");return _r(t.body,a,e)},mathmlBuilder:function(t,e){var r=$r[t.style],a=e.havingStyle(r),n=ke(t.body,a),i=new ve.MathNode("mstyle",n),o={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[t.style];return i.setAttribute("scriptlevel",o[0]),i.setAttribute("displaystyle",o[1]),i}});te({type:"supsub",htmlBuilder:function(t,e){var r=function(t,e){var r=t.base;return r?"op"===r.type?r.limits&&(e.style.size===w.DISPLAY.size||r.alwaysHandleSupSub)?Ur:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(e.style.size===w.DISPLAY.size||r.limits)?Xr:null:"accent"===r.type?c.isCharacterBox(r.base)?Ee:null:"horizBrace"===r.type&&!t.sub===r.isOver?Hr:null:null}(t,e);if(r)return r(t,e);var a,n,i,o=t.base,s=t.sup,h=t.sub,l=ue(o,e),m=e.fontMetrics(),u=0,p=0,d=o&&c.isCharacterBox(o);if(s){var f=e.havingStyle(e.style.sup());a=ue(s,f,e),d||(u=l.height-f.fontMetrics().supDrop*f.sizeMultiplier/e.sizeMultiplier)}if(h){var g=e.havingStyle(e.style.sub());n=ue(h,g,e),d||(p=l.depth+g.fontMetrics().subDrop*g.sizeMultiplier/e.sizeMultiplier)}i=e.style===w.DISPLAY?m.sup1:e.style.cramped?m.sup3:m.sup2;var x,v=e.sizeMultiplier,b=.5/m.ptPerEm/v+"em",y=null;if(n){var k=t.base&&"op"===t.base.type&&t.base.name&&("\\oiint"===t.base.name||"\\oiiint"===t.base.name);(l instanceof E||k)&&(y=-l.italic+"em")}if(a&&n){u=Math.max(u,i,a.depth+.25*m.xHeight),p=Math.max(p,m.sub2);var S=4*m.defaultRuleThickness;if(u-a.depth-(n.height-p)<S){p=S-(u-a.depth)+n.height;var M=.8*m.xHeight-(u-a.depth);M>0&&(u+=M,p-=M)}var z=[{type:"elem",elem:n,shift:p,marginRight:b,marginLeft:y},{type:"elem",elem:a,shift:-u,marginRight:b}];x=Dt.makeVList({positionType:"individualShift",children:z},e)}else if(n){p=Math.max(p,m.sub1,n.height-.8*m.xHeight);var A=[{type:"elem",elem:n,marginLeft:y,marginRight:b}];x=Dt.makeVList({positionType:"shift",positionData:p,children:A},e)}else{if(!a)throw new Error("supsub must have either sup or sub.");u=Math.max(u,i,a.depth+.25*m.xHeight),x=Dt.makeVList({positionType:"shift",positionData:-u,children:[{type:"elem",elem:a,marginRight:b}]},e)}var T=me(l,"right")||"mord";return Dt.makeSpan([T],[l,Dt.makeSpan(["msupsub"],[x])],e)},mathmlBuilder:function(t,e){var r,a=!1,n=Vt(t.base,"horizBrace");n&&!!t.sup===n.isOver&&(a=!0,r=n.isOver),!t.base||"op"!==t.base.type&&"operatorname"!==t.base.type||(t.base.parentIsSupSub=!0);var i,o=[Me(t.base,e)];if(t.sub&&o.push(Me(t.sub,e)),t.sup&&o.push(Me(t.sup,e)),a)i=r?"mover":"munder";else if(t.sub)if(t.sup){var s=t.base;i=s&&"op"===s.type&&s.limits&&e.style===w.DISPLAY?"munderover":s&&"operatorname"===s.type&&s.alwaysHandleSupSub&&(e.style===w.DISPLAY||s.limits)?"munderover":"msubsup"}else{var h=t.base;i=h&&"op"===h.type&&h.limits&&(e.style===w.DISPLAY||h.alwaysHandleSupSub)?"munder":h&&"operatorname"===h.type&&h.alwaysHandleSupSub&&(h.limits||e.style===w.DISPLAY)?"munder":"msub"}else{var l=t.base;i=l&&"op"===l.type&&l.limits&&(e.style===w.DISPLAY||l.alwaysHandleSupSub)?"mover":l&&"operatorname"===l.type&&l.alwaysHandleSupSub&&(l.limits||e.style===w.DISPLAY)?"mover":"msup"}return new ve.MathNode(i,o)}}),te({type:"atom",htmlBuilder:function(t,e){return Dt.mathsym(t.text,t.mode,e,["m"+t.family])},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[be(t.text,t.mode)]);if("bin"===t.family){var a=we(t,e);"bold-italic"===a&&r.setAttribute("mathvariant",a)}else"punct"===t.family?r.setAttribute("separator","true"):"open"!==t.family&&"close"!==t.family||r.setAttribute("stretchy","false");return r}});var Zr={mi:"italic",mn:"normal",mtext:"normal"};te({type:"mathord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"mathord")},mathmlBuilder:function(t,e){var r=new ve.MathNode("mi",[be(t.text,t.mode,e)]),a=we(t,e)||"italic";return a!==Zr[r.type]&&r.setAttribute("mathvariant",a),r}}),te({type:"textord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"textord")},mathmlBuilder:function(t,e){var r,a=be(t.text,t.mode,e),n=we(t,e)||"normal";return r="text"===t.mode?new ve.MathNode("mtext",[a]):/[0-9]/.test(t.text)?new ve.MathNode("mn",[a]):"\\prime"===t.text?new ve.MathNode("mo",[a]):new ve.MathNode("mi",[a]),n!==Zr[r.type]&&r.setAttribute("mathvariant",n),r}});var Kr={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},Jr={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};te({type:"spacing",htmlBuilder:function(t,e){if(Jr.hasOwnProperty(t.text)){var r=Jr[t.text].className||"";if("text"===t.mode){var a=Dt.makeOrd(t,e,"textord");return a.classes.push(r),a}return Dt.makeSpan(["mspace",r],[Dt.mathsym(t.text,t.mode,e)],e)}if(Kr.hasOwnProperty(t.text))return Dt.makeSpan(["mspace",Kr[t.text]],[],e);throw new o('Unknown type of space "'+t.text+'"')},mathmlBuilder:function(t,e){if(!Jr.hasOwnProperty(t.text)){if(Kr.hasOwnProperty(t.text))return new ve.MathNode("mspace");throw new o('Unknown type of space "'+t.text+'"')}return new ve.MathNode("mtext",[new ve.TextNode("\xa0")])}});var Qr=function(){var t=new ve.MathNode("mtd",[]);return t.setAttribute("width","50%"),t};te({type:"tag",mathmlBuilder:function(t,e){var r=new ve.MathNode("mtable",[new ve.MathNode("mtr",[Qr(),new ve.MathNode("mtd",[Se(t.body,e)]),Qr(),new ve.MathNode("mtd",[Se(t.tag,e)])])]);return r.setAttribute("width","100%"),r}});var ta={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},ea={"\\textbf":"textbf","\\textmd":"textmd"},ra={"\\textit":"textit","\\textup":"textup"},aa=function(t,e){var r=t.font;return r?ta[r]?e.withTextFontFamily(ta[r]):ea[r]?e.withTextFontWeight(ea[r]):e.withTextFontShape(ra[r]):e};Qt({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup"],props:{numArgs:1,argTypes:["text"],greediness:2,allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"text",mode:r.mode,body:ee(n),font:a}},htmlBuilder:function(t,e){var r=aa(t,e),a=se(t.body,r,!0);return Dt.makeSpan(["mord","text"],Dt.tryCombineChars(a),r)},mathmlBuilder:function(t,e){var r=aa(t,e);return Se(t.body,r)}}),Qt({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){return{type:"underline",mode:t.parser.mode,body:e[0]}},htmlBuilder:function(t,e){var r=ue(t.body,e),a=Dt.makeLineSpan("underline-line",e),n=e.fontMetrics().defaultRuleThickness,i=Dt.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:n},{type:"elem",elem:a},{type:"kern",size:3*n},{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord","underline"],[i],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[new ve.TextNode("\u203e")]);r.setAttribute("stretchy","true");var a=new ve.MathNode("munder",[Me(t.body,e),r]);return a.setAttribute("accentunder","true"),a}}),Qt({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler:function(t,e,r){throw new o("\\verb ended by end of line instead of matching delimiter")},htmlBuilder:function(t,e){for(var r=na(t),a=[],n=e.havingStyle(e.style.text()),i=0;i<r.length;i++){var o=r[i];"~"===o&&(o="\\textasciitilde"),a.push(Dt.makeSymbol(o,"Typewriter-Regular",t.mode,n,["mord","texttt"]))}return Dt.makeSpan(["mord","text"].concat(n.sizingClasses(e)),Dt.tryCombineChars(a),n)},mathmlBuilder:function(t,e){var r=new ve.TextNode(na(t)),a=new ve.MathNode("mtext",[r]);return a.setAttribute("mathvariant","monospace"),a}});var na=function(t){return t.body.replace(/ /g,t.star?"\u2423":"\xa0")},ia=Zt,oa=new RegExp("^(\\\\[a-zA-Z@]+)[ \r\n\t]*$"),sa=new RegExp("[\u0300-\u036f]+$"),ha="([ \r\n\t]+)|([!-\\[\\]-\u2027\u202a-\ud7ff\uf900-\uffff][\u0300-\u036f]*|[\ud800-\udbff][\udc00-\udfff][\u0300-\u036f]*|\\\\verb\\*([^]).*?\\3|\\\\verb([^*a-zA-Z]).*?\\4|\\\\operatorname\\*|\\\\[a-zA-Z@]+[ \r\n\t]*|\\\\[^\ud800-\udfff])",la=function(){function t(t,e){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=t,this.settings=e,this.tokenRegex=new RegExp(ha,"g"),this.catcodes={"%":14}}var e=t.prototype;return e.setCatcode=function(t,e){this.catcodes[t]=e},e.lex=function(){var t=this.input,e=this.tokenRegex.lastIndex;if(e===t.length)return new n("EOF",new a(this,e,e));var r=this.tokenRegex.exec(t);if(null===r||r.index!==e)throw new o("Unexpected character: '"+t[e]+"'",new n(t[e],new a(this,e,e+1)));var i=r[2]||" ";if(14===this.catcodes[i]){var s=t.indexOf("\n",this.tokenRegex.lastIndex);return-1===s?(this.tokenRegex.lastIndex=t.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=s+1,this.lex()}var h=i.match(oa);return h&&(i=h[1]),new n(i,new a(this,e,this.tokenRegex.lastIndex))},t}(),ma=function(){function t(t,e){void 0===t&&(t={}),void 0===e&&(e={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=e,this.builtins=t,this.undefStack=[]}var e=t.prototype;return e.beginGroup=function(){this.undefStack.push({})},e.endGroup=function(){if(0===this.undefStack.length)throw new o("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");var t=this.undefStack.pop();for(var e in t)t.hasOwnProperty(e)&&(void 0===t[e]?delete this.current[e]:this.current[e]=t[e])},e.has=function(t){return this.current.hasOwnProperty(t)||this.builtins.hasOwnProperty(t)},e.get=function(t){return this.current.hasOwnProperty(t)?this.current[t]:this.builtins[t]},e.set=function(t,e,r){if(void 0===r&&(r=!1),r){for(var a=0;a<this.undefStack.length;a++)delete this.undefStack[a][t];this.undefStack.length>0&&(this.undefStack[this.undefStack.length-1][t]=e)}else{var n=this.undefStack[this.undefStack.length-1];n&&!n.hasOwnProperty(t)&&(n[t]=this.current[t])}this.current[t]=e},t}(),ca={},ua=ca;function pa(t,e){ca[t]=e}pa("\\@firstoftwo",function(t){return{tokens:t.consumeArgs(2)[0],numArgs:0}}),pa("\\@secondoftwo",function(t){return{tokens:t.consumeArgs(2)[1],numArgs:0}}),pa("\\@ifnextchar",function(t){var e=t.consumeArgs(3),r=t.future();return 1===e[0].length&&e[0][0].text===r.text?{tokens:e[1],numArgs:0}:{tokens:e[2],numArgs:0}}),pa("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),pa("\\TextOrMath",function(t){var e=t.consumeArgs(2);return"text"===t.mode?{tokens:e[0],numArgs:0}:{tokens:e[1],numArgs:0}});var da={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};pa("\\char",function(t){var e,r=t.popToken(),a="";if("'"===r.text)e=8,r=t.popToken();else if('"'===r.text)e=16,r=t.popToken();else if("`"===r.text)if("\\"===(r=t.popToken()).text[0])a=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new o("\\char` missing argument");a=r.text.charCodeAt(0)}else e=10;if(e){if(null==(a=da[r.text])||a>=e)throw new o("Invalid base-"+e+" digit "+r.text);for(var n;null!=(n=da[t.future().text])&&n<e;)a*=e,a+=n,t.popToken()}return"\\@char{"+a+"}"});var fa=function(t,e){var r=t.consumeArgs(1)[0];if(1!==r.length)throw new o("\\gdef's first argument must be a macro name");var a=r[0].text,n=0;for(r=t.consumeArgs(1)[0];1===r.length&&"#"===r[0].text;){if(1!==(r=t.consumeArgs(1)[0]).length)throw new o('Invalid argument number length "'+r.length+'"');if(!/^[1-9]$/.test(r[0].text))throw new o('Invalid argument number "'+r[0].text+'"');if(n++,parseInt(r[0].text)!==n)throw new o('Argument number "'+r[0].text+'" out of order');r=t.consumeArgs(1)[0]}return t.macros.set(a,{tokens:r,numArgs:n},e),""};pa("\\gdef",function(t){return fa(t,!0)}),pa("\\def",function(t){return fa(t,!1)}),pa("\\global",function(t){var e=t.consumeArgs(1)[0];if(1!==e.length)throw new o("Invalid command after \\global");var r=e[0].text;if("\\def"===r)return fa(t,!0);throw new o("Invalid command '"+r+"' after \\global")});var ga=function(t,e,r){var a=t.consumeArgs(1)[0];if(1!==a.length)throw new o("\\newcommand's first argument must be a macro name");var n=a[0].text,i=t.isDefined(n);if(i&&!e)throw new o("\\newcommand{"+n+"} attempting to redefine "+n+"; use \\renewcommand");if(!i&&!r)throw new o("\\renewcommand{"+n+"} when command "+n+" does not yet exist; use \\newcommand");var s=0;if(1===(a=t.consumeArgs(1)[0]).length&&"["===a[0].text){for(var h="",l=t.expandNextToken();"]"!==l.text&&"EOF"!==l.text;)h+=l.text,l=t.expandNextToken();if(!h.match(/^\s*[0-9]+\s*$/))throw new o("Invalid number of arguments: "+h);s=parseInt(h),a=t.consumeArgs(1)[0]}return t.macros.set(n,{tokens:a,numArgs:s}),""};pa("\\newcommand",function(t){return ga(t,!1,!0)}),pa("\\renewcommand",function(t){return ga(t,!0,!1)}),pa("\\providecommand",function(t){return ga(t,!0,!0)}),pa("\\bgroup","{"),pa("\\egroup","}"),pa("\\lq","`"),pa("\\rq","'"),pa("\\aa","\\r a"),pa("\\AA","\\r A"),pa("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xa9}"),pa("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),pa("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xae}"),pa("\u212c","\\mathscr{B}"),pa("\u2130","\\mathscr{E}"),pa("\u2131","\\mathscr{F}"),pa("\u210b","\\mathscr{H}"),pa("\u2110","\\mathscr{I}"),pa("\u2112","\\mathscr{L}"),pa("\u2133","\\mathscr{M}"),pa("\u211b","\\mathscr{R}"),pa("\u212d","\\mathfrak{C}"),pa("\u210c","\\mathfrak{H}"),pa("\u2128","\\mathfrak{Z}"),pa("\\Bbbk","\\Bbb{k}"),pa("\xb7","\\cdotp"),pa("\\llap","\\mathllap{\\textrm{#1}}"),pa("\\rlap","\\mathrlap{\\textrm{#1}}"),pa("\\clap","\\mathclap{\\textrm{#1}}"),pa("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'),pa("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}"),pa("\\ne","\\neq"),pa("\u2260","\\neq"),pa("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}"),pa("\u2209","\\notin"),pa("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}"),pa("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"),pa("\u225a","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225a}}"),pa("\u225b","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225b}}"),pa("\u225d","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225d}}"),pa("\u225e","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225e}}"),pa("\u225f","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225f}}"),pa("\u27c2","\\perp"),pa("\u203c","\\mathclose{!\\mkern-0.8mu!}"),pa("\u220c","\\notni"),pa("\u231c","\\ulcorner"),pa("\u231d","\\urcorner"),pa("\u231e","\\llcorner"),pa("\u231f","\\lrcorner"),pa("\xa9","\\copyright"),pa("\xae","\\textregistered"),pa("\ufe0f","\\textregistered"),pa("\\vdots","\\mathord{\\varvdots\\rule{0pt}{15pt}}"),pa("\u22ee","\\vdots"),pa("\\varGamma","\\mathit{\\Gamma}"),pa("\\varDelta","\\mathit{\\Delta}"),pa("\\varTheta","\\mathit{\\Theta}"),pa("\\varLambda","\\mathit{\\Lambda}"),pa("\\varXi","\\mathit{\\Xi}"),pa("\\varPi","\\mathit{\\Pi}"),pa("\\varSigma","\\mathit{\\Sigma}"),pa("\\varUpsilon","\\mathit{\\Upsilon}"),pa("\\varPhi","\\mathit{\\Phi}"),pa("\\varPsi","\\mathit{\\Psi}"),pa("\\varOmega","\\mathit{\\Omega}"),pa("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),pa("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu"),pa("\\boxed","\\fbox{$\\displaystyle{#1}$}"),pa("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),pa("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),pa("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;");var xa={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};pa("\\dots",function(t){var e="\\dotso",r=t.expandAfterFuture().text;return r in xa?e=xa[r]:"\\not"===r.substr(0,4)?e="\\dotsb":r in j.math&&c.contains(["bin","rel"],j.math[r].group)&&(e="\\dotsb"),e});var va={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};pa("\\dotso",function(t){return t.future().text in va?"\\ldots\\,":"\\ldots"}),pa("\\dotsc",function(t){var e=t.future().text;return e in va&&","!==e?"\\ldots\\,":"\\ldots"}),pa("\\cdots",function(t){return t.future().text in va?"\\@cdots\\,":"\\@cdots"}),pa("\\dotsb","\\cdots"),pa("\\dotsm","\\cdots"),pa("\\dotsi","\\!\\cdots"),pa("\\dotsx","\\ldots\\,"),pa("\\DOTSI","\\relax"),pa("\\DOTSB","\\relax"),pa("\\DOTSX","\\relax"),pa("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),pa("\\,","\\tmspace+{3mu}{.1667em}"),pa("\\thinspace","\\,"),pa("\\>","\\mskip{4mu}"),pa("\\:","\\tmspace+{4mu}{.2222em}"),pa("\\medspace","\\:"),pa("\\;","\\tmspace+{5mu}{.2777em}"),pa("\\thickspace","\\;"),pa("\\!","\\tmspace-{3mu}{.1667em}"),pa("\\negthinspace","\\!"),pa("\\negmedspace","\\tmspace-{4mu}{.2222em}"),pa("\\negthickspace","\\tmspace-{5mu}{.277em}"),pa("\\enspace","\\kern.5em "),pa("\\enskip","\\hskip.5em\\relax"),pa("\\quad","\\hskip1em\\relax"),pa("\\qquad","\\hskip2em\\relax"),pa("\\tag","\\@ifstar\\tag@literal\\tag@paren"),pa("\\tag@paren","\\tag@literal{({#1})}"),pa("\\tag@literal",function(t){if(t.macros.get("\\df@tag"))throw new o("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"}),pa("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),pa("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),pa("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),pa("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),pa("\\pmb","\\html@mathml{\\@binrel{#1}{\\mathrlap{#1}\\kern0.5px#1}}{\\mathbf{#1}}"),pa("\\\\","\\newline"),pa("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");var ba=F["Main-Regular"]["T".charCodeAt(0)][1]-.7*F["Main-Regular"]["A".charCodeAt(0)][1]+"em";pa("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+ba+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),pa("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+ba+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),pa("\\hspace","\\@ifstar\\@hspacer\\@hspace"),pa("\\@hspace","\\hskip #1\\relax"),pa("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),pa("\\ordinarycolon",":"),pa("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),pa("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),pa("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),pa("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),pa("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),pa("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),pa("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),pa("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),pa("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),pa("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),pa("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),pa("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),pa("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),pa("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),pa("\u2237","\\dblcolon"),pa("\u2239","\\eqcolon"),pa("\u2254","\\coloneqq"),pa("\u2255","\\eqqcolon"),pa("\u2a74","\\Coloneqq"),pa("\\ratio","\\vcentcolon"),pa("\\coloncolon","\\dblcolon"),pa("\\colonequals","\\coloneqq"),pa("\\coloncolonequals","\\Coloneqq"),pa("\\equalscolon","\\eqqcolon"),pa("\\equalscoloncolon","\\Eqqcolon"),pa("\\colonminus","\\coloneq"),pa("\\coloncolonminus","\\Coloneq"),pa("\\minuscolon","\\eqcolon"),pa("\\minuscoloncolon","\\Eqcolon"),pa("\\coloncolonapprox","\\Colonapprox"),pa("\\coloncolonsim","\\Colonsim"),pa("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),pa("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),pa("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),pa("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),pa("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220c}}"),pa("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),pa("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),pa("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}"),pa("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}"),pa("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}"),pa("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}"),pa("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}"),pa("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}"),pa("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}"),pa("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}"),pa("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}"),pa("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}"),pa("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228a}"),pa("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2acb}"),pa("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228b}"),pa("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2acc}"),pa("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27e6}}"),pa("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27e7}}"),pa("\u27e6","\\llbracket"),pa("\u27e7","\\rrbracket"),pa("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}"),pa("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}"),pa("\u2983","\\lBrace"),pa("\u2984","\\rBrace"),pa("\\darr","\\downarrow"),pa("\\dArr","\\Downarrow"),pa("\\Darr","\\Downarrow"),pa("\\lang","\\langle"),pa("\\rang","\\rangle"),pa("\\uarr","\\uparrow"),pa("\\uArr","\\Uparrow"),pa("\\Uarr","\\Uparrow"),pa("\\N","\\mathbb{N}"),pa("\\R","\\mathbb{R}"),pa("\\Z","\\mathbb{Z}"),pa("\\alef","\\aleph"),pa("\\alefsym","\\aleph"),pa("\\Alpha","\\mathrm{A}"),pa("\\Beta","\\mathrm{B}"),pa("\\bull","\\bullet"),pa("\\Chi","\\mathrm{X}"),pa("\\clubs","\\clubsuit"),pa("\\cnums","\\mathbb{C}"),pa("\\Complex","\\mathbb{C}"),pa("\\Dagger","\\ddagger"),pa("\\diamonds","\\diamondsuit"),pa("\\empty","\\emptyset"),pa("\\Epsilon","\\mathrm{E}"),pa("\\Eta","\\mathrm{H}"),pa("\\exist","\\exists"),pa("\\harr","\\leftrightarrow"),pa("\\hArr","\\Leftrightarrow"),pa("\\Harr","\\Leftrightarrow"),pa("\\hearts","\\heartsuit"),pa("\\image","\\Im"),pa("\\infin","\\infty"),pa("\\Iota","\\mathrm{I}"),pa("\\isin","\\in"),pa("\\Kappa","\\mathrm{K}"),pa("\\larr","\\leftarrow"),pa("\\lArr","\\Leftarrow"),pa("\\Larr","\\Leftarrow"),pa("\\lrarr","\\leftrightarrow"),pa("\\lrArr","\\Leftrightarrow"),pa("\\Lrarr","\\Leftrightarrow"),pa("\\Mu","\\mathrm{M}"),pa("\\natnums","\\mathbb{N}"),pa("\\Nu","\\mathrm{N}"),pa("\\Omicron","\\mathrm{O}"),pa("\\plusmn","\\pm"),pa("\\rarr","\\rightarrow"),pa("\\rArr","\\Rightarrow"),pa("\\Rarr","\\Rightarrow"),pa("\\real","\\Re"),pa("\\reals","\\mathbb{R}"),pa("\\Reals","\\mathbb{R}"),pa("\\Rho","\\mathrm{P}"),pa("\\sdot","\\cdot"),pa("\\sect","\\S"),pa("\\spades","\\spadesuit"),pa("\\sub","\\subset"),pa("\\sube","\\subseteq"),pa("\\supe","\\supseteq"),pa("\\Tau","\\mathrm{T}"),pa("\\thetasym","\\vartheta"),pa("\\weierp","\\wp"),pa("\\Zeta","\\mathrm{Z}"),pa("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),pa("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),pa("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),pa("\\blue","\\textcolor{##6495ed}{#1}"),pa("\\orange","\\textcolor{##ffa500}{#1}"),pa("\\pink","\\textcolor{##ff00af}{#1}"),pa("\\red","\\textcolor{##df0030}{#1}"),pa("\\green","\\textcolor{##28ae7b}{#1}"),pa("\\gray","\\textcolor{gray}{#1}"),pa("\\purple","\\textcolor{##9d38bd}{#1}"),pa("\\blueA","\\textcolor{##ccfaff}{#1}"),pa("\\blueB","\\textcolor{##80f6ff}{#1}"),pa("\\blueC","\\textcolor{##63d9ea}{#1}"),pa("\\blueD","\\textcolor{##11accd}{#1}"),pa("\\blueE","\\textcolor{##0c7f99}{#1}"),pa("\\tealA","\\textcolor{##94fff5}{#1}"),pa("\\tealB","\\textcolor{##26edd5}{#1}"),pa("\\tealC","\\textcolor{##01d1c1}{#1}"),pa("\\tealD","\\textcolor{##01a995}{#1}"),pa("\\tealE","\\textcolor{##208170}{#1}"),pa("\\greenA","\\textcolor{##b6ffb0}{#1}"),pa("\\greenB","\\textcolor{##8af281}{#1}"),pa("\\greenC","\\textcolor{##74cf70}{#1}"),pa("\\greenD","\\textcolor{##1fab54}{#1}"),pa("\\greenE","\\textcolor{##0d923f}{#1}"),pa("\\goldA","\\textcolor{##ffd0a9}{#1}"),pa("\\goldB","\\textcolor{##ffbb71}{#1}"),pa("\\goldC","\\textcolor{##ff9c39}{#1}"),pa("\\goldD","\\textcolor{##e07d10}{#1}"),pa("\\goldE","\\textcolor{##a75a05}{#1}"),pa("\\redA","\\textcolor{##fca9a9}{#1}"),pa("\\redB","\\textcolor{##ff8482}{#1}"),pa("\\redC","\\textcolor{##f9685d}{#1}"),pa("\\redD","\\textcolor{##e84d39}{#1}"),pa("\\redE","\\textcolor{##bc2612}{#1}"),pa("\\maroonA","\\textcolor{##ffbde0}{#1}"),pa("\\maroonB","\\textcolor{##ff92c6}{#1}"),pa("\\maroonC","\\textcolor{##ed5fa6}{#1}"),pa("\\maroonD","\\textcolor{##ca337c}{#1}"),pa("\\maroonE","\\textcolor{##9e034e}{#1}"),pa("\\purpleA","\\textcolor{##ddd7ff}{#1}"),pa("\\purpleB","\\textcolor{##c6b9fc}{#1}"),pa("\\purpleC","\\textcolor{##aa87ff}{#1}"),pa("\\purpleD","\\textcolor{##7854ab}{#1}"),pa("\\purpleE","\\textcolor{##543b78}{#1}"),pa("\\mintA","\\textcolor{##f5f9e8}{#1}"),pa("\\mintB","\\textcolor{##edf2df}{#1}"),pa("\\mintC","\\textcolor{##e0e5cc}{#1}"),pa("\\grayA","\\textcolor{##f6f7f7}{#1}"),pa("\\grayB","\\textcolor{##f0f1f2}{#1}"),pa("\\grayC","\\textcolor{##e3e5e6}{#1}"),pa("\\grayD","\\textcolor{##d6d8da}{#1}"),pa("\\grayE","\\textcolor{##babec2}{#1}"),pa("\\grayF","\\textcolor{##888d93}{#1}"),pa("\\grayG","\\textcolor{##626569}{#1}"),pa("\\grayH","\\textcolor{##3b3e40}{#1}"),pa("\\grayI","\\textcolor{##21242c}{#1}"),pa("\\kaBlue","\\textcolor{##314453}{#1}"),pa("\\kaGreen","\\textcolor{##71B307}{#1}");var ya={"\\relax":!0,"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0},wa=function(){function t(t,e,r){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=e,this.expansionCount=0,this.feed(t),this.macros=new ma(ua,e.macros),this.mode=r,this.stack=[]}var e=t.prototype;return e.feed=function(t){this.lexer=new la(t,this.settings)},e.switchMode=function(t){this.mode=t},e.beginGroup=function(){this.macros.beginGroup()},e.endGroup=function(){this.macros.endGroup()},e.future=function(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]},e.popToken=function(){return this.future(),this.stack.pop()},e.pushToken=function(t){this.stack.push(t)},e.pushTokens=function(t){var e;(e=this.stack).push.apply(e,t)},e.consumeSpaces=function(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}},e.consumeArgs=function(t){for(var e=[],r=0;r<t;++r){this.consumeSpaces();var a=this.popToken();if("{"===a.text){for(var n=[],i=1;0!==i;){var s=this.popToken();if(n.push(s),"{"===s.text)++i;else if("}"===s.text)--i;else if("EOF"===s.text)throw new o("End of input in macro argument",a)}n.pop(),n.reverse(),e[r]=n}else{if("EOF"===a.text)throw new o("End of input expecting macro argument");e[r]=[a]}}return e},e.expandOnce=function(){var t=this.popToken(),e=t.text,r=this._getExpansion(e);if(null==r)return this.pushToken(t),t;if(this.expansionCount++,this.expansionCount>this.settings.maxExpand)throw new o("Too many expansions: infinite loop or need to increase maxExpand setting");var a=r.tokens;if(r.numArgs)for(var n=this.consumeArgs(r.numArgs),i=(a=a.slice()).length-1;i>=0;--i){var s=a[i];if("#"===s.text){if(0===i)throw new o("Incomplete placeholder at end of macro body",s);if("#"===(s=a[--i]).text)a.splice(i+1,1);else{if(!/^[1-9]$/.test(s.text))throw new o("Not a valid argument number",s);var h;(h=a).splice.apply(h,[i,2].concat(n[+s.text-1]))}}}return this.pushTokens(a),a},e.expandAfterFuture=function(){return this.expandOnce(),this.future()},e.expandNextToken=function(){for(;;){var t=this.expandOnce();if(t instanceof n){if("\\relax"!==t.text)return this.stack.pop();this.stack.pop()}}throw new Error},e.expandMacro=function(t){if(this.macros.get(t)){var e=[],r=this.stack.length;for(this.pushToken(new n(t));this.stack.length>r;){this.expandOnce()instanceof n&&e.push(this.stack.pop())}return e}},e.expandMacroAsText=function(t){var e=this.expandMacro(t);return e?e.map(function(t){return t.text}).join(""):e},e._getExpansion=function(t){var e=this.macros.get(t);if(null==e)return e;var r="function"==typeof e?e(this):e;if("string"==typeof r){var a=0;if(-1!==r.indexOf("#"))for(var n=r.replace(/##/g,"");-1!==n.indexOf("#"+(a+1));)++a;for(var i=new la(r,this.settings),o=[],s=i.lex();"EOF"!==s.text;)o.push(s),s=i.lex();return o.reverse(),{tokens:o,numArgs:a}}return r},e.isDefined=function(t){return this.macros.has(t)||ia.hasOwnProperty(t)||j.math.hasOwnProperty(t)||j.text.hasOwnProperty(t)||ya.hasOwnProperty(t)},t}(),ka={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030c":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030a":{text:"\\r",math:"\\mathring"},"\u030b":{text:"\\H"}},Sa={"\xe1":"a\u0301","\xe0":"a\u0300","\xe4":"a\u0308","\u01df":"a\u0308\u0304","\xe3":"a\u0303","\u0101":"a\u0304","\u0103":"a\u0306","\u1eaf":"a\u0306\u0301","\u1eb1":"a\u0306\u0300","\u1eb5":"a\u0306\u0303","\u01ce":"a\u030c","\xe2":"a\u0302","\u1ea5":"a\u0302\u0301","\u1ea7":"a\u0302\u0300","\u1eab":"a\u0302\u0303","\u0227":"a\u0307","\u01e1":"a\u0307\u0304","\xe5":"a\u030a","\u01fb":"a\u030a\u0301","\u1e03":"b\u0307","\u0107":"c\u0301","\u010d":"c\u030c","\u0109":"c\u0302","\u010b":"c\u0307","\u010f":"d\u030c","\u1e0b":"d\u0307","\xe9":"e\u0301","\xe8":"e\u0300","\xeb":"e\u0308","\u1ebd":"e\u0303","\u0113":"e\u0304","\u1e17":"e\u0304\u0301","\u1e15":"e\u0304\u0300","\u0115":"e\u0306","\u011b":"e\u030c","\xea":"e\u0302","\u1ebf":"e\u0302\u0301","\u1ec1":"e\u0302\u0300","\u1ec5":"e\u0302\u0303","\u0117":"e\u0307","\u1e1f":"f\u0307","\u01f5":"g\u0301","\u1e21":"g\u0304","\u011f":"g\u0306","\u01e7":"g\u030c","\u011d":"g\u0302","\u0121":"g\u0307","\u1e27":"h\u0308","\u021f":"h\u030c","\u0125":"h\u0302","\u1e23":"h\u0307","\xed":"i\u0301","\xec":"i\u0300","\xef":"i\u0308","\u1e2f":"i\u0308\u0301","\u0129":"i\u0303","\u012b":"i\u0304","\u012d":"i\u0306","\u01d0":"i\u030c","\xee":"i\u0302","\u01f0":"j\u030c","\u0135":"j\u0302","\u1e31":"k\u0301","\u01e9":"k\u030c","\u013a":"l\u0301","\u013e":"l\u030c","\u1e3f":"m\u0301","\u1e41":"m\u0307","\u0144":"n\u0301","\u01f9":"n\u0300","\xf1":"n\u0303","\u0148":"n\u030c","\u1e45":"n\u0307","\xf3":"o\u0301","\xf2":"o\u0300","\xf6":"o\u0308","\u022b":"o\u0308\u0304","\xf5":"o\u0303","\u1e4d":"o\u0303\u0301","\u1e4f":"o\u0303\u0308","\u022d":"o\u0303\u0304","\u014d":"o\u0304","\u1e53":"o\u0304\u0301","\u1e51":"o\u0304\u0300","\u014f":"o\u0306","\u01d2":"o\u030c","\xf4":"o\u0302","\u1ed1":"o\u0302\u0301","\u1ed3":"o\u0302\u0300","\u1ed7":"o\u0302\u0303","\u022f":"o\u0307","\u0231":"o\u0307\u0304","\u0151":"o\u030b","\u1e55":"p\u0301","\u1e57":"p\u0307","\u0155":"r\u0301","\u0159":"r\u030c","\u1e59":"r\u0307","\u015b":"s\u0301","\u1e65":"s\u0301\u0307","\u0161":"s\u030c","\u1e67":"s\u030c\u0307","\u015d":"s\u0302","\u1e61":"s\u0307","\u1e97":"t\u0308","\u0165":"t\u030c","\u1e6b":"t\u0307","\xfa":"u\u0301","\xf9":"u\u0300","\xfc":"u\u0308","\u01d8":"u\u0308\u0301","\u01dc":"u\u0308\u0300","\u01d6":"u\u0308\u0304","\u01da":"u\u0308\u030c","\u0169":"u\u0303","\u1e79":"u\u0303\u0301","\u016b":"u\u0304","\u1e7b":"u\u0304\u0308","\u016d":"u\u0306","\u01d4":"u\u030c","\xfb":"u\u0302","\u016f":"u\u030a","\u0171":"u\u030b","\u1e7d":"v\u0303","\u1e83":"w\u0301","\u1e81":"w\u0300","\u1e85":"w\u0308","\u0175":"w\u0302","\u1e87":"w\u0307","\u1e98":"w\u030a","\u1e8d":"x\u0308","\u1e8b":"x\u0307","\xfd":"y\u0301","\u1ef3":"y\u0300","\xff":"y\u0308","\u1ef9":"y\u0303","\u0233":"y\u0304","\u0177":"y\u0302","\u1e8f":"y\u0307","\u1e99":"y\u030a","\u017a":"z\u0301","\u017e":"z\u030c","\u1e91":"z\u0302","\u017c":"z\u0307","\xc1":"A\u0301","\xc0":"A\u0300","\xc4":"A\u0308","\u01de":"A\u0308\u0304","\xc3":"A\u0303","\u0100":"A\u0304","\u0102":"A\u0306","\u1eae":"A\u0306\u0301","\u1eb0":"A\u0306\u0300","\u1eb4":"A\u0306\u0303","\u01cd":"A\u030c","\xc2":"A\u0302","\u1ea4":"A\u0302\u0301","\u1ea6":"A\u0302\u0300","\u1eaa":"A\u0302\u0303","\u0226":"A\u0307","\u01e0":"A\u0307\u0304","\xc5":"A\u030a","\u01fa":"A\u030a\u0301","\u1e02":"B\u0307","\u0106":"C\u0301","\u010c":"C\u030c","\u0108":"C\u0302","\u010a":"C\u0307","\u010e":"D\u030c","\u1e0a":"D\u0307","\xc9":"E\u0301","\xc8":"E\u0300","\xcb":"E\u0308","\u1ebc":"E\u0303","\u0112":"E\u0304","\u1e16":"E\u0304\u0301","\u1e14":"E\u0304\u0300","\u0114":"E\u0306","\u011a":"E\u030c","\xca":"E\u0302","\u1ebe":"E\u0302\u0301","\u1ec0":"E\u0302\u0300","\u1ec4":"E\u0302\u0303","\u0116":"E\u0307","\u1e1e":"F\u0307","\u01f4":"G\u0301","\u1e20":"G\u0304","\u011e":"G\u0306","\u01e6":"G\u030c","\u011c":"G\u0302","\u0120":"G\u0307","\u1e26":"H\u0308","\u021e":"H\u030c","\u0124":"H\u0302","\u1e22":"H\u0307","\xcd":"I\u0301","\xcc":"I\u0300","\xcf":"I\u0308","\u1e2e":"I\u0308\u0301","\u0128":"I\u0303","\u012a":"I\u0304","\u012c":"I\u0306","\u01cf":"I\u030c","\xce":"I\u0302","\u0130":"I\u0307","\u0134":"J\u0302","\u1e30":"K\u0301","\u01e8":"K\u030c","\u0139":"L\u0301","\u013d":"L\u030c","\u1e3e":"M\u0301","\u1e40":"M\u0307","\u0143":"N\u0301","\u01f8":"N\u0300","\xd1":"N\u0303","\u0147":"N\u030c","\u1e44":"N\u0307","\xd3":"O\u0301","\xd2":"O\u0300","\xd6":"O\u0308","\u022a":"O\u0308\u0304","\xd5":"O\u0303","\u1e4c":"O\u0303\u0301","\u1e4e":"O\u0303\u0308","\u022c":"O\u0303\u0304","\u014c":"O\u0304","\u1e52":"O\u0304\u0301","\u1e50":"O\u0304\u0300","\u014e":"O\u0306","\u01d1":"O\u030c","\xd4":"O\u0302","\u1ed0":"O\u0302\u0301","\u1ed2":"O\u0302\u0300","\u1ed6":"O\u0302\u0303","\u022e":"O\u0307","\u0230":"O\u0307\u0304","\u0150":"O\u030b","\u1e54":"P\u0301","\u1e56":"P\u0307","\u0154":"R\u0301","\u0158":"R\u030c","\u1e58":"R\u0307","\u015a":"S\u0301","\u1e64":"S\u0301\u0307","\u0160":"S\u030c","\u1e66":"S\u030c\u0307","\u015c":"S\u0302","\u1e60":"S\u0307","\u0164":"T\u030c","\u1e6a":"T\u0307","\xda":"U\u0301","\xd9":"U\u0300","\xdc":"U\u0308","\u01d7":"U\u0308\u0301","\u01db":"U\u0308\u0300","\u01d5":"U\u0308\u0304","\u01d9":"U\u0308\u030c","\u0168":"U\u0303","\u1e78":"U\u0303\u0301","\u016a":"U\u0304","\u1e7a":"U\u0304\u0308","\u016c":"U\u0306","\u01d3":"U\u030c","\xdb":"U\u0302","\u016e":"U\u030a","\u0170":"U\u030b","\u1e7c":"V\u0303","\u1e82":"W\u0301","\u1e80":"W\u0300","\u1e84":"W\u0308","\u0174":"W\u0302","\u1e86":"W\u0307","\u1e8c":"X\u0308","\u1e8a":"X\u0307","\xdd":"Y\u0301","\u1ef2":"Y\u0300","\u0178":"Y\u0308","\u1ef8":"Y\u0303","\u0232":"Y\u0304","\u0176":"Y\u0302","\u1e8e":"Y\u0307","\u0179":"Z\u0301","\u017d":"Z\u030c","\u1e90":"Z\u0302","\u017b":"Z\u0307","\u03ac":"\u03b1\u0301","\u1f70":"\u03b1\u0300","\u1fb1":"\u03b1\u0304","\u1fb0":"\u03b1\u0306","\u03ad":"\u03b5\u0301","\u1f72":"\u03b5\u0300","\u03ae":"\u03b7\u0301","\u1f74":"\u03b7\u0300","\u03af":"\u03b9\u0301","\u1f76":"\u03b9\u0300","\u03ca":"\u03b9\u0308","\u0390":"\u03b9\u0308\u0301","\u1fd2":"\u03b9\u0308\u0300","\u1fd1":"\u03b9\u0304","\u1fd0":"\u03b9\u0306","\u03cc":"\u03bf\u0301","\u1f78":"\u03bf\u0300","\u03cd":"\u03c5\u0301","\u1f7a":"\u03c5\u0300","\u03cb":"\u03c5\u0308","\u03b0":"\u03c5\u0308\u0301","\u1fe2":"\u03c5\u0308\u0300","\u1fe1":"\u03c5\u0304","\u1fe0":"\u03c5\u0306","\u03ce":"\u03c9\u0301","\u1f7c":"\u03c9\u0300","\u038e":"\u03a5\u0301","\u1fea":"\u03a5\u0300","\u03ab":"\u03a5\u0308","\u1fe9":"\u03a5\u0304","\u1fe8":"\u03a5\u0306","\u038f":"\u03a9\u0301","\u1ffa":"\u03a9\u0300"},Ma=function(){function t(t,e){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new wa(t,e,this.mode),this.settings=e,this.leftrightDepth=0}var e=t.prototype;return e.expect=function(t,e){if(void 0===e&&(e=!0),this.fetch().text!==t)throw new o("Expected '"+t+"', got '"+this.fetch().text+"'",this.fetch());e&&this.consume()},e.consume=function(){this.nextToken=null},e.fetch=function(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken},e.switchMode=function(t){this.mode=t,this.gullet.switchMode(t)},e.parse=function(){this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");var t=this.parseExpression(!1);return this.expect("EOF"),this.gullet.endGroup(),t},e.parseExpression=function(e,r){for(var a=[];;){"math"===this.mode&&this.consumeSpaces();var n=this.fetch();if(-1!==t.endOfExpression.indexOf(n.text))break;if(r&&n.text===r)break;if(e&&ia[n.text]&&ia[n.text].infix)break;var i=this.parseAtom(r);if(!i)break;a.push(i)}return"text"===this.mode&&this.formLigatures(a),this.handleInfixNodes(a)},e.handleInfixNodes=function(t){for(var e,r=-1,a=0;a<t.length;a++){var n=Vt(t[a],"infix");if(n){if(-1!==r)throw new o("only one infix operator per group",n.token);r=a,e=n.replaceWith}}if(-1!==r&&e){var i,s,h=t.slice(0,r),l=t.slice(r+1);return i=1===h.length&&"ordgroup"===h[0].type?h[0]:{type:"ordgroup",mode:this.mode,body:h},s=1===l.length&&"ordgroup"===l[0].type?l[0]:{type:"ordgroup",mode:this.mode,body:l},["\\\\abovefrac"===e?this.callFunction(e,[i,t[r],s],[]):this.callFunction(e,[i,s],[])]}return t},e.handleSupSubscript=function(e){var r=this.fetch(),a=r.text;this.consume();var n=this.parseGroup(e,!1,t.SUPSUB_GREEDINESS,void 0,void 0,!0);if(!n)throw new o("Expected group after '"+a+"'",r);return n},e.formatUnsupportedCmd=function(t){for(var e=[],r=0;r<t.length;r++)e.push({type:"textord",mode:"text",text:t[r]});var a={type:"text",mode:this.mode,body:e};return{type:"color",mode:this.mode,color:this.settings.errorColor,body:[a]}},e.parseAtom=function(t){var e,r,a=this.parseGroup("atom",!1,null,t);if("text"===this.mode)return a;for(;;){this.consumeSpaces();var n=this.fetch();if("\\limits"===n.text||"\\nolimits"===n.text){var i=Vt(a,"op");if(i){var s="\\limits"===n.text;i.limits=s,i.alwaysHandleSupSub=!0}else{if(!(i=Vt(a,"operatorname"))||!i.alwaysHandleSupSub)throw new o("Limit controls must follow a math operator",n);var h="\\limits"===n.text;i.limits=h}this.consume()}else if("^"===n.text){if(e)throw new o("Double superscript",n);e=this.handleSupSubscript("superscript")}else if("_"===n.text){if(r)throw new o("Double subscript",n);r=this.handleSupSubscript("subscript")}else{if("'"!==n.text)break;if(e)throw new o("Double superscript",n);var l={type:"textord",mode:this.mode,text:"\\prime"},m=[l];for(this.consume();"'"===this.fetch().text;)m.push(l),this.consume();"^"===this.fetch().text&&m.push(this.handleSupSubscript("superscript")),e={type:"ordgroup",mode:this.mode,body:m}}}return e||r?{type:"supsub",mode:this.mode,base:a,sup:e,sub:r}:a},e.parseFunction=function(t,e,r){var a=this.fetch(),n=a.text,i=ia[n];if(!i)return null;if(this.consume(),null!=r&&i.greediness<=r)throw new o("Got function '"+n+"' with no arguments"+(e?" as "+e:""),a);if("text"===this.mode&&!i.allowedInText)throw new o("Can't use function '"+n+"' in text mode",a);if("math"===this.mode&&!1===i.allowedInMath)throw new o("Can't use function '"+n+"' in math mode",a);var s=this.parseArguments(n,i),h=s.args,l=s.optArgs;return this.callFunction(n,h,l,a,t)},e.callFunction=function(t,e,r,a,n){var i={funcName:t,parser:this,token:a,breakOnTokenText:n},s=ia[t];if(s&&s.handler)return s.handler(i,e,r);throw new o("No function handler for "+t)},e.parseArguments=function(t,e){var r=e.numArgs+e.numOptionalArgs;if(0===r)return{args:[],optArgs:[]};for(var a=e.greediness,n=[],i=[],s=0;s<r;s++){var h=e.argTypes&&e.argTypes[s],l=s<e.numOptionalArgs,m=s>0&&!l||0===s&&!l&&"math"===this.mode,c=this.parseGroupOfType("argument to '"+t+"'",h,l,a,m);if(!c){if(l){i.push(null);continue}throw new o("Expected group after '"+t+"'",this.fetch())}(l?i:n).push(c)}return{args:n,optArgs:i}},e.parseGroupOfType=function(t,e,r,a,n){switch(e){case"color":return n&&this.consumeSpaces(),this.parseColorGroup(r);case"size":return n&&this.consumeSpaces(),this.parseSizeGroup(r);case"url":return this.parseUrlGroup(r,n);case"math":case"text":return this.parseGroup(t,r,a,void 0,e,n);case"hbox":var i=this.parseGroup(t,r,a,void 0,"text",n);return i?{type:"styling",mode:i.mode,body:[i],style:"text"}:i;case"raw":if(n&&this.consumeSpaces(),r&&"{"===this.fetch().text)return null;var s=this.parseStringGroup("raw",r,!0);if(s)return{type:"raw",mode:"text",string:s.text};throw new o("Expected raw group",this.fetch());case"original":case null:case void 0:return this.parseGroup(t,r,a,void 0,void 0,n);default:throw new o("Unknown group type as "+t,this.fetch())}},e.consumeSpaces=function(){for(;" "===this.fetch().text;)this.consume()},e.parseStringGroup=function(t,e,r){var a=e?"[":"{",n=e?"]":"}",i=this.fetch();if(i.text!==a){if(e)return null;if(r&&"EOF"!==i.text&&/[^{}[\]]/.test(i.text))return this.consume(),i}var s=this.mode;this.mode="text",this.expect(a);for(var h,l="",m=this.fetch(),c=0,u=m;(h=this.fetch()).text!==n||r&&c>0;){switch(h.text){case"EOF":throw new o("Unexpected end of input in "+t,m.range(u,l));case a:c++;break;case n:c--}l+=(u=h).text,this.consume()}return this.expect(n),this.mode=s,m.range(u,l)},e.parseRegexGroup=function(t,e){var r=this.mode;this.mode="text";for(var a,n=this.fetch(),i=n,s="";"EOF"!==(a=this.fetch()).text&&t.test(s+a.text);)s+=(i=a).text,this.consume();if(""===s)throw new o("Invalid "+e+": '"+n.text+"'",n);return this.mode=r,n.range(i,s)},e.parseColorGroup=function(t){var e=this.parseStringGroup("color",t);if(!e)return null;var r=/^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i.exec(e.text);if(!r)throw new o("Invalid color: '"+e.text+"'",e);var a=r[0];return/^[0-9a-f]{6}$/i.test(a)&&(a="#"+a),{type:"color-token",mode:this.mode,color:a}},e.parseSizeGroup=function(t){var e,r=!1;if(!(e=t||"{"===this.fetch().text?this.parseStringGroup("size",t):this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/,"size")))return null;t||0!==e.text.length||(e.text="0pt",r=!0);var a=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e.text);if(!a)throw new o("Invalid size: '"+e.text+"'",e);var n={number:+(a[1]+a[2]),unit:a[3]};if(!At(n))throw new o("Invalid unit: '"+n.unit+"'",e);return{type:"size",mode:this.mode,value:n,isBlank:r}},e.parseUrlGroup=function(t,e){this.gullet.lexer.setCatcode("%",13);var r=this.parseStringGroup("url",t,!0);if(this.gullet.lexer.setCatcode("%",14),!r)return null;var a=r.text.replace(/\\([#$%&~_^{}])/g,"$1");return{type:"url",mode:this.mode,url:a}},e.parseGroup=function(e,r,n,i,s,h){var l=this.mode;s&&this.switchMode(s),h&&this.consumeSpaces();var m,c=this.fetch(),u=c.text;if(r?"["===u:"{"===u||"\\begingroup"===u){this.consume();var p=t.endOfGroup[u];this.gullet.beginGroup();var d=this.parseExpression(!1,p),f=this.fetch();this.expect(p),this.gullet.endGroup(),m={type:"ordgroup",mode:this.mode,loc:a.range(c,f),body:d,semisimple:"\\begingroup"===u||void 0}}else if(r)m=null;else if(null==(m=this.parseFunction(i,e,n)||this.parseSymbol())&&"\\"===u[0]&&!ya.hasOwnProperty(u)){if(this.settings.throwOnError)throw new o("Undefined control sequence: "+u,c);m=this.formatUnsupportedCmd(u),this.consume()}return s&&this.switchMode(l),m},e.formLigatures=function(t){for(var e=t.length-1,r=0;r<e;++r){var n=t[r],i=n.text;"-"===i&&"-"===t[r+1].text&&(r+1<e&&"-"===t[r+2].text?(t.splice(r,3,{type:"textord",mode:"text",loc:a.range(n,t[r+2]),text:"---"}),e-=2):(t.splice(r,2,{type:"textord",mode:"text",loc:a.range(n,t[r+1]),text:"--"}),e-=1)),"'"!==i&&"`"!==i||t[r+1].text!==i||(t.splice(r,2,{type:"textord",mode:"text",loc:a.range(n,t[r+1]),text:i+i}),e-=1)}},e.parseSymbol=function(){var t=this.fetch(),e=t.text;if(/^\\verb[^a-zA-Z]/.test(e)){this.consume();var r=e.slice(5),n="*"===r.charAt(0);if(n&&(r=r.slice(1)),r.length<2||r.charAt(0)!==r.slice(-1))throw new o("\\verb assertion failed --\n please report what input caused this bug");return{type:"verb",mode:"text",body:r=r.slice(1,-1),star:n}}Sa.hasOwnProperty(e[0])&&!j[this.mode][e[0]]&&(this.settings.strict&&"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Accented Unicode text character "'+e[0]+'" used in math mode',t),e=Sa[e[0]]+e.substr(1));var i,s=sa.exec(e);if(s&&("i"===(e=e.substring(0,s.index))?e="\u0131":"j"===e&&(e="\u0237")),j[this.mode][e]){this.settings.strict&&"math"===this.mode&&"\xc7\xd0\xde\xe7\xfe".indexOf(e)>=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+e[0]+'" used in math mode',t);var h,l=j[this.mode][e].group,m=a.range(t);if(W.hasOwnProperty(l)){var c=l;h={type:"atom",mode:this.mode,family:c,loc:m,text:e}}else h={type:l,mode:this.mode,loc:m,text:e};i=h}else{if(!(e.charCodeAt(0)>=128))return null;this.settings.strict&&(M(e.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+e[0]+'" used in math mode',t):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+e[0]+'" ('+e.charCodeAt(0)+")",t)),i={type:"textord",mode:"text",loc:a.range(t),text:e}}if(this.consume(),s)for(var u=0;u<s[0].length;u++){var p=s[0][u];if(!ka[p])throw new o("Unknown accent ' "+p+"'",t);var d=ka[p][this.mode];if(!d)throw new o("Accent "+p+" unsupported in "+this.mode+" mode",t);i={type:"accent",mode:this.mode,loc:a.range(t),label:d,isStretchy:!1,isShifty:!0,base:i}}return i},t}();Ma.endOfExpression=["}","\\endgroup","\\end","\\right","&"],Ma.endOfGroup={"[":"]","{":"}","\\begingroup":"\\endgroup"},Ma.SUPSUB_GREEDINESS=1;var za=function(t,e){if(!("string"==typeof t||t instanceof String))throw new TypeError("KaTeX can only parse string typed expression");var r=new Ma(t,e);delete r.gullet.macros.current["\\df@tag"];var a=r.parse();if(r.gullet.macros.get("\\df@tag")){if(!e.displayMode)throw new o("\\tag works only in display equations");r.gullet.feed("\\df@tag"),a=[{type:"tag",mode:"text",body:a,tag:r.parse()}]}return a},Aa=function(t,e,r){e.textContent="";var a=Ba(t,r).toNode();e.appendChild(a)};"undefined"!=typeof document&&"CSS1Compat"!==document.compatMode&&("undefined"!=typeof console&&console.warn("Warning: KaTeX doesn't work in quirks mode. Make sure your website has a suitable doctype."),Aa=function(){throw new o("KaTeX doesn't work in quirks mode.")});var Ta=function(t,e,r){if(r.throwOnError||!(t instanceof o))throw t;var a=Dt.makeSpan(["katex-error"],[new E(e)]);return a.setAttribute("title",t.toString()),a.setAttribute("style","color:"+r.errorColor),a},Ba=function(t,e){var r=new u(e);try{var a=za(t,r);return Be(a,t,r)}catch(e){return Ta(e,t,r)}},Ca={version:"0.11.1",render:Aa,renderToString:function(t,e){return Ba(t,e).toMarkup()},ParseError:o,__parse:function(t,e){var r=new u(e);return za(t,r)},__renderToDomTree:Ba,__renderToHTMLTree:function(t,e){var r=new u(e);try{return function(t,e,r){var a=de(t,Ae(r)),n=Dt.makeSpan(["katex"],[a]);return Te(n,r)}(za(t,r),0,r)}catch(e){return Ta(e,t,r)}},__setFontMetrics:function(t,e){F[t]=e},__defineSymbol:$,__defineMacro:pa,__domTree:{Span:N,Anchor:I,SymbolNode:E,SvgNode:L,PathNode:H,LineNode:P}};e.default=Ca}]).default}); \ No newline at end of file diff --git a/docs/ldsctrlest-logo.png b/docs/ldsctrlest-logo.png deleted file mode 100644 index 73c068377b7643f5043515a6dff556a73a8964a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16128 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 I)y{<fKf>47UjP6A diff --git a/docs/manifest.json b/docs/manifest.json deleted file mode 100644 index 83ffb796..00000000 --- a/docs/manifest.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "LDS C&E", - "short_name": "LDS C&E", - "start_url": "/lds-ctrl-est/", - "scope": "/lds-ctrl-est/", - "display": "standalone", - "background_color": "#000000", - "theme_color": "#000000", - "icons": [ - { - "src": "/lds-ctrl-est/favicon.svg", - "sizes": "512x512" - } - ] -} diff --git a/docs/mermaid.min.js b/docs/mermaid.min.js deleted file mode 100644 index 48da6d6c..00000000 --- a/docs/mermaid.min.js +++ /dev/null @@ -1,32 +0,0 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.mermaid=e():t.mermaid=e()}("undefined"!=typeof self?self:this,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=383)}([function(t,e,n){"use strict";n.r(e);var r=function(t,e){return t<e?-1:t>e?1:t>=e?0:NaN},i=function(t){var e;return 1===t.length&&(e=t,t=function(t,n){return r(e(t),n)}),{left:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)<0?r=a+1:i=a}return r},right:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)>0?i=a:r=a+1}return r}}};var a=i(r),o=a.right,s=a.left,c=o,u=function(t,e){null==e&&(e=l);for(var n=0,r=t.length-1,i=t[0],a=new Array(r<0?0:r);n<r;)a[n]=e(i,i=t[++n]);return a};function l(t,e){return[t,e]}var h=function(t,e,n){var r,i,a,o,s=t.length,c=e.length,u=new Array(s*c);for(null==n&&(n=l),r=a=0;r<s;++r)for(o=t[r],i=0;i<c;++i,++a)u[a]=n(o,e[i]);return u},f=function(t,e){return e<t?-1:e>t?1:e>=t?0:NaN},d=function(t){return null===t?NaN:+t},p=function(t,e){var n,r,i=t.length,a=0,o=-1,s=0,c=0;if(null==e)for(;++o<i;)isNaN(n=d(t[o]))||(c+=(r=n-s)*(n-(s+=r/++a)));else for(;++o<i;)isNaN(n=d(e(t[o],o,t)))||(c+=(r=n-s)*(n-(s+=r/++a)));if(a>1)return c/(a-1)},g=function(t,e){var n=p(t,e);return n?Math.sqrt(n):n},y=function(t,e){var n,r,i,a=t.length,o=-1;if(null==e){for(;++o<a;)if(null!=(n=t[o])&&n>=n)for(r=i=n;++o<a;)null!=(n=t[o])&&(r>n&&(r=n),i<n&&(i=n))}else for(;++o<a;)if(null!=(n=e(t[o],o,t))&&n>=n)for(r=i=n;++o<a;)null!=(n=e(t[o],o,t))&&(r>n&&(r=n),i<n&&(i=n));return[r,i]},v=Array.prototype,m=v.slice,b=v.map,x=function(t){return function(){return t}},_=function(t){return t},k=function(t,e,n){t=+t,e=+e,n=(i=arguments.length)<2?(e=t,t=0,1):i<3?1:+n;for(var r=-1,i=0|Math.max(0,Math.ceil((e-t)/n)),a=new Array(i);++r<i;)a[r]=t+r*n;return a},w=Math.sqrt(50),E=Math.sqrt(10),T=Math.sqrt(2),C=function(t,e,n){var r,i,a,o,s=-1;if(n=+n,(t=+t)===(e=+e)&&n>0)return[t];if((r=e<t)&&(i=t,t=e,e=i),0===(o=A(t,e,n))||!isFinite(o))return[];if(o>0)for(t=Math.ceil(t/o),e=Math.floor(e/o),a=new Array(i=Math.ceil(e-t+1));++s<i;)a[s]=(t+s)*o;else for(t=Math.floor(t*o),e=Math.ceil(e*o),a=new Array(i=Math.ceil(t-e+1));++s<i;)a[s]=(t-s)/o;return r&&a.reverse(),a};function A(t,e,n){var r=(e-t)/Math.max(0,n),i=Math.floor(Math.log(r)/Math.LN10),a=r/Math.pow(10,i);return i>=0?(a>=w?10:a>=E?5:a>=T?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=w?10:a>=E?5:a>=T?2:1)}function S(t,e,n){var r=Math.abs(e-t)/Math.max(0,n),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),a=r/i;return a>=w?i*=10:a>=E?i*=5:a>=T&&(i*=2),e<t?-i:i}var M=function(t){return Math.ceil(Math.log(t.length)/Math.LN2)+1},O=function(){var t=_,e=y,n=M;function r(r){var i,a,o=r.length,s=new Array(o);for(i=0;i<o;++i)s[i]=t(r[i],i,r);var u=e(s),l=u[0],h=u[1],f=n(s,l,h);Array.isArray(f)||(f=S(l,h,f),f=k(Math.ceil(l/f)*f,h,f));for(var d=f.length;f[0]<=l;)f.shift(),--d;for(;f[d-1]>h;)f.pop(),--d;var p,g=new Array(d+1);for(i=0;i<=d;++i)(p=g[i]=[]).x0=i>0?f[i-1]:l,p.x1=i<d?f[i]:h;for(i=0;i<o;++i)l<=(a=s[i])&&a<=h&&g[c(f,a,0,d)].push(r[i]);return g}return r.value=function(e){return arguments.length?(t="function"==typeof e?e:x(e),r):t},r.domain=function(t){return arguments.length?(e="function"==typeof t?t:x([t[0],t[1]]),r):e},r.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?x(m.call(t)):x(t),r):n},r},D=function(t,e,n){if(null==n&&(n=d),r=t.length){if((e=+e)<=0||r<2)return+n(t[0],0,t);if(e>=1)return+n(t[r-1],r-1,t);var r,i=(r-1)*e,a=Math.floor(i),o=+n(t[a],a,t);return o+(+n(t[a+1],a+1,t)-o)*(i-a)}},N=function(t,e,n){return t=b.call(t,d).sort(r),Math.ceil((n-e)/(2*(D(t,.75)-D(t,.25))*Math.pow(t.length,-1/3)))},B=function(t,e,n){return Math.ceil((n-e)/(3.5*g(t)*Math.pow(t.length,-1/3)))},L=function(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&n>r&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&n>r&&(r=n);return r},P=function(t,e){var n,r=t.length,i=r,a=-1,o=0;if(null==e)for(;++a<r;)isNaN(n=d(t[a]))?--i:o+=n;else for(;++a<r;)isNaN(n=d(e(t[a],a,t)))?--i:o+=n;if(i)return o/i},I=function(t,e){var n,i=t.length,a=-1,o=[];if(null==e)for(;++a<i;)isNaN(n=d(t[a]))||o.push(n);else for(;++a<i;)isNaN(n=d(e(t[a],a,t)))||o.push(n);return D(o.sort(r),.5)},F=function(t){for(var e,n,r,i=t.length,a=-1,o=0;++a<i;)o+=t[a].length;for(n=new Array(o);--i>=0;)for(e=(r=t[i]).length;--e>=0;)n[--o]=r[e];return n},j=function(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&r>n&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&r>n&&(r=n);return r},R=function(t,e){for(var n=e.length,r=new Array(n);n--;)r[n]=t[e[n]];return r},Y=function(t,e){if(n=t.length){var n,i,a=0,o=0,s=t[o];for(null==e&&(e=r);++a<n;)(e(i=t[a],s)<0||0!==e(s,s))&&(s=i,o=a);return 0===e(s,s)?o:void 0}},z=function(t,e,n){for(var r,i,a=(null==n?t.length:n)-(e=null==e?0:+e);a;)i=Math.random()*a--|0,r=t[a+e],t[a+e]=t[i+e],t[i+e]=r;return t},U=function(t,e){var n,r=t.length,i=-1,a=0;if(null==e)for(;++i<r;)(n=+t[i])&&(a+=n);else for(;++i<r;)(n=+e(t[i],i,t))&&(a+=n);return a},$=function(t){if(!(i=t.length))return[];for(var e=-1,n=j(t,W),r=new Array(n);++e<n;)for(var i,a=-1,o=r[e]=new Array(i);++a<i;)o[a]=t[a][e];return r};function W(t){return t.length}var H=function(){return $(arguments)},V=Array.prototype.slice,G=function(t){return t};function q(t){return"translate("+(t+.5)+",0)"}function X(t){return"translate(0,"+(t+.5)+")"}function Z(t){return function(e){return+t(e)}}function J(t){var e=Math.max(0,t.bandwidth()-1)/2;return t.round()&&(e=Math.round(e)),function(n){return+t(n)+e}}function K(){return!this.__axis}function Q(t,e){var n=[],r=null,i=null,a=6,o=6,s=3,c=1===t||4===t?-1:1,u=4===t||2===t?"x":"y",l=1===t||3===t?q:X;function h(h){var f=null==r?e.ticks?e.ticks.apply(e,n):e.domain():r,d=null==i?e.tickFormat?e.tickFormat.apply(e,n):G:i,p=Math.max(a,0)+s,g=e.range(),y=+g[0]+.5,v=+g[g.length-1]+.5,m=(e.bandwidth?J:Z)(e.copy()),b=h.selection?h.selection():h,x=b.selectAll(".domain").data([null]),_=b.selectAll(".tick").data(f,e).order(),k=_.exit(),w=_.enter().append("g").attr("class","tick"),E=_.select("line"),T=_.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),_=_.merge(w),E=E.merge(w.append("line").attr("stroke","currentColor").attr(u+"2",c*a)),T=T.merge(w.append("text").attr("fill","currentColor").attr(u,c*p).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==b&&(x=x.transition(h),_=_.transition(h),E=E.transition(h),T=T.transition(h),k=k.transition(h).attr("opacity",1e-6).attr("transform",(function(t){return isFinite(t=m(t))?l(t):this.getAttribute("transform")})),w.attr("opacity",1e-6).attr("transform",(function(t){var e=this.parentNode.__axis;return l(e&&isFinite(e=e(t))?e:m(t))}))),k.remove(),x.attr("d",4===t||2==t?o?"M"+c*o+","+y+"H0.5V"+v+"H"+c*o:"M0.5,"+y+"V"+v:o?"M"+y+","+c*o+"V0.5H"+v+"V"+c*o:"M"+y+",0.5H"+v),_.attr("opacity",1).attr("transform",(function(t){return l(m(t))})),E.attr(u+"2",c*a),T.attr(u,c*p).text(d),b.filter(K).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),b.each((function(){this.__axis=m}))}return h.scale=function(t){return arguments.length?(e=t,h):e},h.ticks=function(){return n=V.call(arguments),h},h.tickArguments=function(t){return arguments.length?(n=null==t?[]:V.call(t),h):n.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:V.call(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(a=o=+t,h):a},h.tickSizeInner=function(t){return arguments.length?(a=+t,h):a},h.tickSizeOuter=function(t){return arguments.length?(o=+t,h):o},h.tickPadding=function(t){return arguments.length?(s=+t,h):s},h}function tt(t){return Q(1,t)}function et(t){return Q(2,t)}function nt(t){return Q(3,t)}function rt(t){return Q(4,t)}var it={value:function(){}};function at(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new ot(r)}function ot(t){this._=t}function st(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function ct(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function ut(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=it,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}ot.prototype=at.prototype={constructor:ot,on:function(t,e){var n,r=this._,i=st(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=ut(r[n],t.name,e);else if(null==e)for(n in r)r[n]=ut(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=ct(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new ot(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};var lt=at;function ht(){}var ft=function(t){return null==t?ht:function(){return this.querySelector(t)}};function dt(){return[]}var pt=function(t){return null==t?dt:function(){return this.querySelectorAll(t)}},gt=function(t){return function(){return this.matches(t)}},yt=function(t){return new Array(t.length)};function vt(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}vt.prototype={constructor:vt,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};function mt(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new vt(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function bt(t,e,n,r,i,a,o){var s,c,u,l={},h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u="$"+o.call(c,c.__data__,s,e),u in l?i[s]=c:l[u]=c);for(s=0;s<f;++s)(c=l[u="$"+o.call(t,a[s],s,a)])?(r[s]=c,c.__data__=a[s],l[u]=null):n[s]=new vt(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l[d[s]]===c&&(i[s]=c)}function xt(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}var _t="http://www.w3.org/1999/xhtml",kt={svg:"http://www.w3.org/2000/svg",xhtml:_t,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},wt=function(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),kt.hasOwnProperty(e)?{space:kt[e],local:t}:t};function Et(t){return function(){this.removeAttribute(t)}}function Tt(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Ct(t,e){return function(){this.setAttribute(t,e)}}function At(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function St(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function Mt(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}var Ot=function(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView};function Dt(t){return function(){this.style.removeProperty(t)}}function Nt(t,e,n){return function(){this.style.setProperty(t,e,n)}}function Bt(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function Lt(t,e){return t.style.getPropertyValue(e)||Ot(t).getComputedStyle(t,null).getPropertyValue(e)}function Pt(t){return function(){delete this[t]}}function It(t,e){return function(){this[t]=e}}function Ft(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function jt(t){return t.trim().split(/^|\s+/)}function Rt(t){return t.classList||new Yt(t)}function Yt(t){this._node=t,this._names=jt(t.getAttribute("class")||"")}function zt(t,e){for(var n=Rt(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function Ut(t,e){for(var n=Rt(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function $t(t){return function(){zt(this,t)}}function Wt(t){return function(){Ut(this,t)}}function Ht(t,e){return function(){(e.apply(this,arguments)?zt:Ut)(this,t)}}Yt.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function Vt(){this.textContent=""}function Gt(t){return function(){this.textContent=t}}function qt(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function Xt(){this.innerHTML=""}function Zt(t){return function(){this.innerHTML=t}}function Jt(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function Kt(){this.nextSibling&&this.parentNode.appendChild(this)}function Qt(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function te(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===_t&&e.documentElement.namespaceURI===_t?e.createElement(t):e.createElementNS(n,t)}}function ee(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}var ne=function(t){var e=wt(t);return(e.local?ee:te)(e)};function re(){return null}function ie(){var t=this.parentNode;t&&t.removeChild(this)}function ae(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function oe(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}var se={},ce=null;"undefined"!=typeof document&&("onmouseenter"in document.documentElement||(se={mouseenter:"mouseover",mouseleave:"mouseout"}));function ue(t,e,n){return t=le(t,e,n),function(e){var n=e.relatedTarget;n&&(n===this||8&n.compareDocumentPosition(this))||t.call(this,e)}}function le(t,e,n){return function(r){var i=ce;ce=r;try{t.call(this,this.__data__,e,n)}finally{ce=i}}}function he(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function fe(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.capture);++i?e.length=i:delete this.__on}}}function de(t,e,n){var r=se.hasOwnProperty(t.type)?ue:le;return function(i,a,o){var s,c=this.__on,u=r(e,a,o);if(c)for(var l=0,h=c.length;l<h;++l)if((s=c[l]).type===t.type&&s.name===t.name)return this.removeEventListener(s.type,s.listener,s.capture),this.addEventListener(s.type,s.listener=u,s.capture=n),void(s.value=e);this.addEventListener(t.type,u,n),s={type:t.type,name:t.name,value:e,listener:u,capture:n},c?c.push(s):this.__on=[s]}}function pe(t,e,n,r){var i=ce;t.sourceEvent=ce,ce=t;try{return e.apply(n,r)}finally{ce=i}}function ge(t,e,n){var r=Ot(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function ye(t,e){return function(){return ge(this,t,e)}}function ve(t,e){return function(){return ge(this,t,e.apply(this,arguments))}}var me=[null];function be(t,e){this._groups=t,this._parents=e}function xe(){return new be([[document.documentElement]],me)}be.prototype=xe.prototype={constructor:be,select:function(t){"function"!=typeof t&&(t=ft(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new be(r,this._parents)},selectAll:function(t){"function"!=typeof t&&(t=pt(t));for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new be(r,i)},filter:function(t){"function"!=typeof t&&(t=gt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new be(r,this._parents)},data:function(t,e){if(!t)return p=new Array(this.size()),l=-1,this.each((function(t){p[++l]=t})),p;var n,r=e?bt:mt,i=this._parents,a=this._groups;"function"!=typeof t&&(n=t,t=function(){return n});for(var o=a.length,s=new Array(o),c=new Array(o),u=new Array(o),l=0;l<o;++l){var h=i[l],f=a[l],d=f.length,p=t.call(h,h&&h.__data__,l,i),g=p.length,y=c[l]=new Array(g),v=s[l]=new Array(g);r(h,f,y,v,u[l]=new Array(d),p,e);for(var m,b,x=0,_=0;x<g;++x)if(m=y[x]){for(x>=_&&(_=x+1);!(b=v[_])&&++_<g;);m._next=b||null}}return(s=new be(s,i))._enter=c,s._exit=u,s},enter:function(){return new be(this._enter||this._groups.map(yt),this._parents)},exit:function(){return new be(this._exit||this._groups.map(yt),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return r="function"==typeof t?t(r):r.append(t+""),null!=e&&(i=e(i)),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new be(o,this._parents)},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=xt);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new be(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){var t=new Array(this.size()),e=-1;return this.each((function(){t[++e]=this})),t},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){var t=0;return this.each((function(){++t})),t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=wt(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?Tt:Et:"function"==typeof e?n.local?Mt:St:n.local?At:Ct)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?Dt:"function"==typeof e?Bt:Nt)(t,e,null==n?"":n)):Lt(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?Pt:"function"==typeof e?Ft:It)(t,e)):this.node()[t]},classed:function(t,e){var n=jt(t+"");if(arguments.length<2){for(var r=Rt(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?Ht:e?$t:Wt)(n,e))},text:function(t){return arguments.length?this.each(null==t?Vt:("function"==typeof t?qt:Gt)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?Xt:("function"==typeof t?Jt:Zt)(t)):this.node().innerHTML},raise:function(){return this.each(Kt)},lower:function(){return this.each(Qt)},append:function(t){var e="function"==typeof t?t:ne(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:ne(t),r=null==e?re:"function"==typeof e?e:ft(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(ie)},clone:function(t){return this.select(t?oe:ae)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=he(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?de:fe,null==n&&(n=!1),r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?ve:ye)(t,e))}};var _e=xe,ke=function(t){return"string"==typeof t?new be([[document.querySelector(t)]],[document.documentElement]):new be([[t]],me)};function we(){ce.stopImmediatePropagation()}var Ee=function(){ce.preventDefault(),ce.stopImmediatePropagation()},Te=function(t){var e=t.document.documentElement,n=ke(t).on("dragstart.drag",Ee,!0);"onselectstart"in e?n.on("selectstart.drag",Ee,!0):(e.__noselect=e.style.MozUserSelect,e.style.MozUserSelect="none")};function Ce(t,e){var n=t.document.documentElement,r=ke(t).on("dragstart.drag",null);e&&(r.on("click.drag",Ee,!0),setTimeout((function(){r.on("click.drag",null)}),0)),"onselectstart"in n?r.on("selectstart.drag",null):(n.style.MozUserSelect=n.__noselect,delete n.__noselect)}var Ae=function(t,e,n){t.prototype=e.prototype=n,n.constructor=t};function Se(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function Me(){}var Oe="\\s*([+-]?\\d+)\\s*",De="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Ne="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Be=/^#([0-9a-f]{3,8})$/,Le=new RegExp("^rgb\\("+[Oe,Oe,Oe]+"\\)$"),Pe=new RegExp("^rgb\\("+[Ne,Ne,Ne]+"\\)$"),Ie=new RegExp("^rgba\\("+[Oe,Oe,Oe,De]+"\\)$"),Fe=new RegExp("^rgba\\("+[Ne,Ne,Ne,De]+"\\)$"),je=new RegExp("^hsl\\("+[De,Ne,Ne]+"\\)$"),Re=new RegExp("^hsla\\("+[De,Ne,Ne,De]+"\\)$"),Ye={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function ze(){return this.rgb().formatHex()}function Ue(){return this.rgb().formatRgb()}function $e(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Be.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?We(e):3===n?new qe(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?new qe(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?new qe(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Le.exec(t))?new qe(e[1],e[2],e[3],1):(e=Pe.exec(t))?new qe(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Ie.exec(t))?He(e[1],e[2],e[3],e[4]):(e=Fe.exec(t))?He(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=je.exec(t))?Ke(e[1],e[2]/100,e[3]/100,1):(e=Re.exec(t))?Ke(e[1],e[2]/100,e[3]/100,e[4]):Ye.hasOwnProperty(t)?We(Ye[t]):"transparent"===t?new qe(NaN,NaN,NaN,0):null}function We(t){return new qe(t>>16&255,t>>8&255,255&t,1)}function He(t,e,n,r){return r<=0&&(t=e=n=NaN),new qe(t,e,n,r)}function Ve(t){return t instanceof Me||(t=$e(t)),t?new qe((t=t.rgb()).r,t.g,t.b,t.opacity):new qe}function Ge(t,e,n,r){return 1===arguments.length?Ve(t):new qe(t,e,n,null==r?1:r)}function qe(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function Xe(){return"#"+Je(this.r)+Je(this.g)+Je(this.b)}function Ze(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function Je(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function Ke(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new en(t,e,n,r)}function Qe(t){if(t instanceof en)return new en(t.h,t.s,t.l,t.opacity);if(t instanceof Me||(t=$e(t)),!t)return new en;if(t instanceof en)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new en(o,s,c,t.opacity)}function tn(t,e,n,r){return 1===arguments.length?Qe(t):new en(t,e,n,null==r?1:r)}function en(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function nn(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function rn(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}Ae(Me,$e,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:ze,formatHex:ze,formatHsl:function(){return Qe(this).formatHsl()},formatRgb:Ue,toString:Ue}),Ae(qe,Ge,Se(Me,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Xe,formatHex:Xe,formatRgb:Ze,toString:Ze})),Ae(en,tn,Se(Me,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new en(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new en(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new qe(nn(t>=240?t-240:t+120,i,r),nn(t,i,r),nn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));var an=function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return rn((n-r/e)*e,o,i,a,s)}},on=function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return rn((n-r/e)*e,i,a,o,s)}},sn=function(t){return function(){return t}};function cn(t,e){return function(n){return t+n*e}}function un(t,e){var n=e-t;return n?cn(t,n>180||n<-180?n-360*Math.round(n/360):n):sn(isNaN(t)?e:t)}function ln(t){return 1==(t=+t)?hn:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):sn(isNaN(e)?n:e)}}function hn(t,e){var n=e-t;return n?cn(t,n):sn(isNaN(t)?e:t)}var fn=function t(e){var n=ln(e);function r(t,e){var r=n((t=Ge(t)).r,(e=Ge(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=hn(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function dn(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=Ge(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}var pn=dn(an),gn=dn(on),yn=function(t,e){e||(e=[]);var n,r=t?Math.min(e.length,t.length):0,i=e.slice();return function(a){for(n=0;n<r;++n)i[n]=t[n]*(1-a)+e[n]*a;return i}};function vn(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}var mn=function(t,e){return(vn(e)?yn:bn)(t,e)};function bn(t,e){var n,r=e?e.length:0,i=t?Math.min(r,t.length):0,a=new Array(i),o=new Array(r);for(n=0;n<i;++n)a[n]=Sn(t[n],e[n]);for(;n<r;++n)o[n]=e[n];return function(t){for(n=0;n<i;++n)o[n]=a[n](t);return o}}var xn=function(t,e){var n=new Date;return t=+t,e=+e,function(r){return n.setTime(t*(1-r)+e*r),n}},_n=function(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}},kn=function(t,e){var n,r={},i={};for(n in null!==t&&"object"==typeof t||(t={}),null!==e&&"object"==typeof e||(e={}),e)n in t?r[n]=Sn(t[n],e[n]):i[n]=e[n];return function(t){for(n in r)i[n]=r[n](t);return i}},wn=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,En=new RegExp(wn.source,"g");var Tn,Cn,An=function(t,e){var n,r,i,a=wn.lastIndex=En.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=wn.exec(t))&&(r=En.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:_n(n,r)})),a=En.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})},Sn=function(t,e){var n,r=typeof e;return null==e||"boolean"===r?sn(e):("number"===r?_n:"string"===r?(n=$e(e))?(e=n,fn):An:e instanceof $e?fn:e instanceof Date?xn:vn(e)?yn:Array.isArray(e)?bn:"function"!=typeof e.valueOf&&"function"!=typeof e.toString||isNaN(e)?kn:_n)(t,e)},Mn=function(){for(var t,e=ce;t=e.sourceEvent;)e=t;return e},On=function(t,e){var n=t.ownerSVGElement||t;if(n.createSVGPoint){var r=n.createSVGPoint();return r.x=e.clientX,r.y=e.clientY,[(r=r.matrixTransform(t.getScreenCTM().inverse())).x,r.y]}var i=t.getBoundingClientRect();return[e.clientX-i.left-t.clientLeft,e.clientY-i.top-t.clientTop]},Dn=function(t,e,n){arguments.length<3&&(n=e,e=Mn().changedTouches);for(var r,i=0,a=e?e.length:0;i<a;++i)if((r=e[i]).identifier===n)return On(t,r);return null},Nn=function(t){var e=Mn();return e.changedTouches&&(e=e.changedTouches[0]),On(t,e)},Bn=0,Ln=0,Pn=0,In=0,Fn=0,jn=0,Rn="object"==typeof performance&&performance.now?performance:Date,Yn="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function zn(){return Fn||(Yn(Un),Fn=Rn.now()+jn)}function Un(){Fn=0}function $n(){this._call=this._time=this._next=null}function Wn(t,e,n){var r=new $n;return r.restart(t,e,n),r}function Hn(){zn(),++Bn;for(var t,e=Tn;e;)(t=Fn-e._time)>=0&&e._call.call(null,t),e=e._next;--Bn}function Vn(){Fn=(In=Rn.now())+jn,Bn=Ln=0;try{Hn()}finally{Bn=0,function(){var t,e,n=Tn,r=1/0;for(;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:Tn=e);Cn=t,qn(r)}(),Fn=0}}function Gn(){var t=Rn.now(),e=t-In;e>1e3&&(jn-=e,In=t)}function qn(t){Bn||(Ln&&(Ln=clearTimeout(Ln)),t-Fn>24?(t<1/0&&(Ln=setTimeout(Vn,t-Rn.now()-jn)),Pn&&(Pn=clearInterval(Pn))):(Pn||(In=Rn.now(),Pn=setInterval(Gn,1e3)),Bn=1,Yn(Vn)))}$n.prototype=Wn.prototype={constructor:$n,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?zn():+n)+(null==e?0:+e),this._next||Cn===this||(Cn?Cn._next=this:Tn=this,Cn=this),this._call=t,this._time=n,qn()},stop:function(){this._call&&(this._call=null,this._time=1/0,qn())}};var Xn=function(t,e,n){var r=new $n;return e=null==e?0:+e,r.restart((function(n){r.stop(),t(n+e)}),e,n),r},Zn=lt("start","end","cancel","interrupt"),Jn=[],Kn=function(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return Xn(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(Xn((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=Wn((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:Zn,tween:Jn,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})};function Qn(t,e){var n=er(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function tr(t,e){var n=er(t,e);if(n.state>3)throw new Error("too late; already running");return n}function er(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}var nr,rr,ir,ar,or=function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}},sr=180/Math.PI,cr={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1},ur=function(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*sr,skewX:Math.atan(c)*sr,scaleX:o,scaleY:s}};function lr(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:_n(t,i)},{i:c-2,x:_n(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:_n(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:_n(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:_n(t,n)},{i:s-2,x:_n(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var hr=lr((function(t){return"none"===t?cr:(nr||(nr=document.createElement("DIV"),rr=document.documentElement,ir=document.defaultView),nr.style.transform=t,t=ir.getComputedStyle(rr.appendChild(nr),null).getPropertyValue("transform"),rr.removeChild(nr),t=t.slice(7,-1).split(","),ur(+t[0],+t[1],+t[2],+t[3],+t[4],+t[5]))}),"px, ","px)","deg)"),fr=lr((function(t){return null==t?cr:(ar||(ar=document.createElementNS("http://www.w3.org/2000/svg","g")),ar.setAttribute("transform",t),(t=ar.transform.baseVal.consolidate())?(t=t.matrix,ur(t.a,t.b,t.c,t.d,t.e,t.f)):cr)}),", ",")",")");function dr(t,e){var n,r;return function(){var i=tr(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function pr(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=tr(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function gr(t,e,n){var r=t._id;return t.each((function(){var t=tr(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return er(t,r).value[e]}}var yr=function(t,e){var n;return("number"==typeof e?_n:e instanceof $e?fn:(n=$e(e))?(e=n,fn):An)(t,e)};function vr(t){return function(){this.removeAttribute(t)}}function mr(t){return function(){this.removeAttributeNS(t.space,t.local)}}function br(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function xr(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function _r(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function kr(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function wr(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function Er(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function Tr(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Er(t,i)),n}return i._value=e,i}function Cr(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&wr(t,i)),n}return i._value=e,i}function Ar(t,e){return function(){Qn(this,t).delay=+e.apply(this,arguments)}}function Sr(t,e){return e=+e,function(){Qn(this,t).delay=e}}function Mr(t,e){return function(){tr(this,t).duration=+e.apply(this,arguments)}}function Or(t,e){return e=+e,function(){tr(this,t).duration=e}}function Dr(t,e){if("function"!=typeof e)throw new Error;return function(){tr(this,t).ease=e}}function Nr(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?Qn:tr;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var Br=_e.prototype.constructor;function Lr(t){return function(){this.style.removeProperty(t)}}function Pr(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function Ir(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Pr(t,a,n)),r}return a._value=e,a}function Fr(t){return function(e){this.textContent=t.call(this,e)}}function jr(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Fr(r)),e}return r._value=t,r}var Rr=0;function Yr(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function zr(t){return _e().transition(t)}function Ur(){return++Rr}var $r=_e.prototype;function Wr(t){return t*t*t}function Hr(t){return--t*t*t+1}function Vr(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}Yr.prototype=zr.prototype={constructor:Yr,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=ft(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,Kn(h[f],e,n,f,h,er(s,n)));return new Yr(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=pt(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=er(c,n),g=0,y=d.length;g<y;++g)(f=d[g])&&Kn(f,e,n,g,d,p);a.push(d),o.push(c)}return new Yr(a,o,e,n)},filter:function(t){"function"!=typeof t&&(t=gt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Yr(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new Yr(o,this._parents,this._name,this._id)},selection:function(){return new Br(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=Ur(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=er(o,e);Kn(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new Yr(r,this._parents,t,n)},call:$r.call,nodes:$r.nodes,node:$r.node,size:$r.size,empty:$r.empty,each:$r.each,on:function(t,e){var n=this._id;return arguments.length<2?er(this.node(),n).on.on(t):this.each(Nr(n,t,e))},attr:function(t,e){var n=wt(t),r="transform"===n?fr:yr;return this.attrTween(t,"function"==typeof e?(n.local?kr:_r)(n,r,gr(this,"attr."+t,e)):null==e?(n.local?mr:vr)(n):(n.local?xr:br)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=wt(t);return this.tween(n,(r.local?Tr:Cr)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?hr:yr;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=Lt(this,t),o=(this.style.removeProperty(t),Lt(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Lr(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=Lt(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=Lt(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,gr(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=tr(this,t),u=c.on,l=null==c.value[o]?a||(a=Lr(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=Lt(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,Ir(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(gr(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,jr(t))},remove:function(){return this.on("end.remove",(t=this._id,function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}));var t},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=er(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?dr:pr)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Ar:Sr)(e,t)):er(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Mr:Or)(e,t)):er(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(Dr(e,t)):er(this.node(),e).ease},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=tr(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e}))}))}};var Gr={time:null,delay:0,duration:250,ease:Vr};function qr(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))return Gr.time=zn(),Gr;return n}_e.prototype.interrupt=function(t){return this.each((function(){or(this,t)}))},_e.prototype.transition=function(t){var e,n;t instanceof Yr?(e=t._id,t=t._name):(e=Ur(),(n=Gr).time=zn(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&Kn(o,t,e,u,s,n||qr(o,e));return new Yr(r,this._parents,t,e)};var Xr=[null],Zr=function(t,e){var n,r,i=t.__transition;if(i)for(r in e=null==e?null:e+"",i)if((n=i[r]).state>1&&n.name===e)return new Yr([[t]],Xr,e,+r);return null},Jr=function(t){return function(){return t}},Kr=function(t,e,n){this.target=t,this.type=e,this.selection=n};function Qr(){ce.stopImmediatePropagation()}var ti=function(){ce.preventDefault(),ce.stopImmediatePropagation()},ei={name:"drag"},ni={name:"space"},ri={name:"handle"},ii={name:"center"};function ai(t){return[+t[0],+t[1]]}function oi(t){return[ai(t[0]),ai(t[1])]}function si(t){return function(e){return Dn(e,ce.touches,t)}}var ci={name:"x",handles:["w","e"].map(yi),input:function(t,e){return null==t?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},ui={name:"y",handles:["n","s"].map(yi),input:function(t,e){return null==t?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},li={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(yi),input:function(t){return null==t?null:oi(t)},output:function(t){return t}},hi={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},fi={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},di={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},pi={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},gi={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function yi(t){return{type:t}}function vi(){return!ce.ctrlKey&&!ce.button}function mi(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function bi(){return navigator.maxTouchPoints||"ontouchstart"in this}function xi(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function _i(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function ki(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function wi(){return Ci(ci)}function Ei(){return Ci(ui)}var Ti=function(){return Ci(li)};function Ci(t){var e,n=mi,r=vi,i=bi,a=!0,o=lt("start","brush","end"),s=6;function c(e){var n=e.property("__brush",g).selectAll(".overlay").data([yi("overlay")]);n.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",hi.overlay).merge(n).each((function(){var t=xi(this).extent;ke(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),e.selectAll(".selection").data([yi("selection")]).enter().append("rect").attr("class","selection").attr("cursor",hi.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=e.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return hi[t.type]})),e.each(u).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",f).filter(i).on("touchstart.brush",f).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function u(){var t=ke(this),e=xi(this).selection;e?(t.selectAll(".selection").style("display",null).attr("x",e[0][0]).attr("y",e[0][1]).attr("width",e[1][0]-e[0][0]).attr("height",e[1][1]-e[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?e[1][0]-s/2:e[0][0]-s/2})).attr("y",(function(t){return"s"===t.type[0]?e[1][1]-s/2:e[0][1]-s/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?e[1][0]-e[0][0]+s:s})).attr("height",(function(t){return"e"===t.type||"w"===t.type?e[1][1]-e[0][1]+s:s}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function l(t,e,n){return!n&&t.__brush.emitter||new h(t,e)}function h(t,e){this.that=t,this.args=e,this.state=t.__brush,this.active=0}function f(){if((!e||ce.touches)&&r.apply(this,arguments)){var n,i,o,s,c,h,f,d,p,g,y,v=this,m=ce.target.__data__.type,b="selection"===(a&&ce.metaKey?m="overlay":m)?ei:a&&ce.altKey?ii:ri,x=t===ui?null:pi[m],_=t===ci?null:gi[m],k=xi(v),w=k.extent,E=k.selection,T=w[0][0],C=w[0][1],A=w[1][0],S=w[1][1],M=0,O=0,D=x&&_&&a&&ce.shiftKey,N=ce.touches?si(ce.changedTouches[0].identifier):Nn,B=N(v),L=B,P=l(v,arguments,!0).beforestart();"overlay"===m?(E&&(p=!0),k.selection=E=[[n=t===ui?T:B[0],o=t===ci?C:B[1]],[c=t===ui?A:n,f=t===ci?S:o]]):(n=E[0][0],o=E[0][1],c=E[1][0],f=E[1][1]),i=n,s=o,h=c,d=f;var I=ke(v).attr("pointer-events","none"),F=I.selectAll(".overlay").attr("cursor",hi[m]);if(ce.touches)P.moved=R,P.ended=z;else{var j=ke(ce.view).on("mousemove.brush",R,!0).on("mouseup.brush",z,!0);a&&j.on("keydown.brush",U,!0).on("keyup.brush",$,!0),Te(ce.view)}Qr(),or(v),u.call(v),P.start()}function R(){var t=N(v);!D||g||y||(Math.abs(t[0]-L[0])>Math.abs(t[1]-L[1])?y=!0:g=!0),L=t,p=!0,ti(),Y()}function Y(){var t;switch(M=L[0]-B[0],O=L[1]-B[1],b){case ni:case ei:x&&(M=Math.max(T-n,Math.min(A-c,M)),i=n+M,h=c+M),_&&(O=Math.max(C-o,Math.min(S-f,O)),s=o+O,d=f+O);break;case ri:x<0?(M=Math.max(T-n,Math.min(A-n,M)),i=n+M,h=c):x>0&&(M=Math.max(T-c,Math.min(A-c,M)),i=n,h=c+M),_<0?(O=Math.max(C-o,Math.min(S-o,O)),s=o+O,d=f):_>0&&(O=Math.max(C-f,Math.min(S-f,O)),s=o,d=f+O);break;case ii:x&&(i=Math.max(T,Math.min(A,n-M*x)),h=Math.max(T,Math.min(A,c+M*x))),_&&(s=Math.max(C,Math.min(S,o-O*_)),d=Math.max(C,Math.min(S,f+O*_)))}h<i&&(x*=-1,t=n,n=c,c=t,t=i,i=h,h=t,m in fi&&F.attr("cursor",hi[m=fi[m]])),d<s&&(_*=-1,t=o,o=f,f=t,t=s,s=d,d=t,m in di&&F.attr("cursor",hi[m=di[m]])),k.selection&&(E=k.selection),g&&(i=E[0][0],h=E[1][0]),y&&(s=E[0][1],d=E[1][1]),E[0][0]===i&&E[0][1]===s&&E[1][0]===h&&E[1][1]===d||(k.selection=[[i,s],[h,d]],u.call(v),P.brush())}function z(){if(Qr(),ce.touches){if(ce.touches.length)return;e&&clearTimeout(e),e=setTimeout((function(){e=null}),500)}else Ce(ce.view,p),j.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null);I.attr("pointer-events","all"),F.attr("cursor",hi.overlay),k.selection&&(E=k.selection),_i(E)&&(k.selection=null,u.call(v)),P.end()}function U(){switch(ce.keyCode){case 16:D=x&&_;break;case 18:b===ri&&(x&&(c=h-M*x,n=i+M*x),_&&(f=d-O*_,o=s+O*_),b=ii,Y());break;case 32:b!==ri&&b!==ii||(x<0?c=h-M:x>0&&(n=i-M),_<0?f=d-O:_>0&&(o=s-O),b=ni,F.attr("cursor",hi.selection),Y());break;default:return}ti()}function $(){switch(ce.keyCode){case 16:D&&(g=y=D=!1,Y());break;case 18:b===ii&&(x<0?c=h:x>0&&(n=i),_<0?f=d:_>0&&(o=s),b=ri,Y());break;case 32:b===ni&&(ce.altKey?(x&&(c=h-M*x,n=i+M*x),_&&(f=d-O*_,o=s+O*_),b=ii):(x<0?c=h:x>0&&(n=i),_<0?f=d:_>0&&(o=s),b=ri),F.attr("cursor",hi[m]),Y());break;default:return}ti()}}function d(){l(this,arguments).moved()}function p(){l(this,arguments).ended()}function g(){var e=this.__brush||{selection:null};return e.extent=oi(n.apply(this,arguments)),e.dim=t,e}return c.move=function(e,n){e.selection?e.on("start.brush",(function(){l(this,arguments).beforestart().start()})).on("interrupt.brush end.brush",(function(){l(this,arguments).end()})).tween("brush",(function(){var e=this,r=e.__brush,i=l(e,arguments),a=r.selection,o=t.input("function"==typeof n?n.apply(this,arguments):n,r.extent),s=Sn(a,o);function c(t){r.selection=1===t&&null===o?null:s(t),u.call(e),i.brush()}return null!==a&&null!==o?c:c(1)})):e.each((function(){var e=this,r=arguments,i=e.__brush,a=t.input("function"==typeof n?n.apply(e,r):n,i.extent),o=l(e,r).beforestart();or(e),i.selection=null===a?null:a,u.call(e),o.start().brush().end()}))},c.clear=function(t){c.move(t,null)},h.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting?(this.starting=!1,this.emit("start")):this.emit("brush"),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(e){pe(new Kr(c,e,t.output(this.state.selection)),o.apply,o,[e,this.that,this.args])}},c.extent=function(t){return arguments.length?(n="function"==typeof t?t:Jr(oi(t)),c):n},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:Jr(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:Jr(!!t),c):i},c.handleSize=function(t){return arguments.length?(s=+t,c):s},c.keyModifiers=function(t){return arguments.length?(a=!!t,c):a},c.on=function(){var t=o.on.apply(o,arguments);return t===o?c:t},c}var Ai=Math.cos,Si=Math.sin,Mi=Math.PI,Oi=Mi/2,Di=2*Mi,Ni=Math.max;function Bi(t){return function(e,n){return t(e.source.value+e.target.value,n.source.value+n.target.value)}}var Li=function(){var t=0,e=null,n=null,r=null;function i(i){var a,o,s,c,u,l,h=i.length,f=[],d=k(h),p=[],g=[],y=g.groups=new Array(h),v=new Array(h*h);for(a=0,u=-1;++u<h;){for(o=0,l=-1;++l<h;)o+=i[u][l];f.push(o),p.push(k(h)),a+=o}for(e&&d.sort((function(t,n){return e(f[t],f[n])})),n&&p.forEach((function(t,e){t.sort((function(t,r){return n(i[e][t],i[e][r])}))})),c=(a=Ni(0,Di-t*h)/a)?t:Di/h,o=0,u=-1;++u<h;){for(s=o,l=-1;++l<h;){var m=d[u],b=p[m][l],x=i[m][b],_=o,w=o+=x*a;v[b*h+m]={index:m,subindex:b,startAngle:_,endAngle:w,value:x}}y[m]={index:m,startAngle:s,endAngle:o,value:f[m]},o+=c}for(u=-1;++u<h;)for(l=u-1;++l<h;){var E=v[l*h+u],T=v[u*h+l];(E.value||T.value)&&g.push(E.value<T.value?{source:T,target:E}:{source:E,target:T})}return r?g.sort(r):g}return i.padAngle=function(e){return arguments.length?(t=Ni(0,e),i):t},i.sortGroups=function(t){return arguments.length?(e=t,i):e},i.sortSubgroups=function(t){return arguments.length?(n=t,i):n},i.sortChords=function(t){return arguments.length?(null==t?r=null:(r=Bi(t))._=t,i):r&&r._},i},Pi=Array.prototype.slice,Ii=function(t){return function(){return t}},Fi=Math.PI,ji=2*Fi,Ri=ji-1e-6;function Yi(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function zi(){return new Yi}Yi.prototype=zi.prototype={constructor:Yi,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,r){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+r)},bezierCurveTo:function(t,e,n,r,i,a){this._+="C"+ +t+","+ +e+","+ +n+","+ +r+","+(this._x1=+i)+","+(this._y1=+a)},arcTo:function(t,e,n,r,i){t=+t,e=+e,n=+n,r=+r,i=+i;var a=this._x1,o=this._y1,s=n-t,c=r-e,u=a-t,l=o-e,h=u*u+l*l;if(i<0)throw new Error("negative radius: "+i);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>1e-6)if(Math.abs(l*s-c*u)>1e-6&&i){var f=n-a,d=r-o,p=s*s+c*c,g=f*f+d*d,y=Math.sqrt(p),v=Math.sqrt(h),m=i*Math.tan((Fi-Math.acos((p+h-g)/(2*y*v)))/2),b=m/v,x=m/y;Math.abs(b-1)>1e-6&&(this._+="L"+(t+b*u)+","+(e+b*l)),this._+="A"+i+","+i+",0,0,"+ +(l*f>u*d)+","+(this._x1=t+x*s)+","+(this._y1=e+x*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e);else;},arc:function(t,e,n,r,i,a){t=+t,e=+e,a=!!a;var o=(n=+n)*Math.cos(r),s=n*Math.sin(r),c=t+o,u=e+s,l=1^a,h=a?r-i:i-r;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+u:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-u)>1e-6)&&(this._+="L"+c+","+u),n&&(h<0&&(h=h%ji+ji),h>Ri?this._+="A"+n+","+n+",0,1,"+l+","+(t-o)+","+(e-s)+"A"+n+","+n+",0,1,"+l+","+(this._x1=c)+","+(this._y1=u):h>1e-6&&(this._+="A"+n+","+n+",0,"+ +(h>=Fi)+","+l+","+(this._x1=t+n*Math.cos(i))+","+(this._y1=e+n*Math.sin(i))))},rect:function(t,e,n,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};var Ui=zi;function $i(t){return t.source}function Wi(t){return t.target}function Hi(t){return t.radius}function Vi(t){return t.startAngle}function Gi(t){return t.endAngle}var qi=function(){var t=$i,e=Wi,n=Hi,r=Vi,i=Gi,a=null;function o(){var o,s=Pi.call(arguments),c=t.apply(this,s),u=e.apply(this,s),l=+n.apply(this,(s[0]=c,s)),h=r.apply(this,s)-Oi,f=i.apply(this,s)-Oi,d=l*Ai(h),p=l*Si(h),g=+n.apply(this,(s[0]=u,s)),y=r.apply(this,s)-Oi,v=i.apply(this,s)-Oi;if(a||(a=o=Ui()),a.moveTo(d,p),a.arc(0,0,l,h,f),h===y&&f===v||(a.quadraticCurveTo(0,0,g*Ai(y),g*Si(y)),a.arc(0,0,g,y,v)),a.quadraticCurveTo(0,0,d,p),a.closePath(),o)return a=null,o+""||null}return o.radius=function(t){return arguments.length?(n="function"==typeof t?t:Ii(+t),o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:Ii(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:Ii(+t),o):i},o.source=function(e){return arguments.length?(t=e,o):t},o.target=function(t){return arguments.length?(e=t,o):e},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o};function Xi(){}function Zi(t,e){var n=new Xi;if(t instanceof Xi)t.each((function(t,e){n.set(e,t)}));else if(Array.isArray(t)){var r,i=-1,a=t.length;if(null==e)for(;++i<a;)n.set(i,t[i]);else for(;++i<a;)n.set(e(r=t[i],i,t),r)}else if(t)for(var o in t)n.set(o,t[o]);return n}Xi.prototype=Zi.prototype={constructor:Xi,has:function(t){return"$"+t in this},get:function(t){return this["$"+t]},set:function(t,e){return this["$"+t]=e,this},remove:function(t){var e="$"+t;return e in this&&delete this[e]},clear:function(){for(var t in this)"$"===t[0]&&delete this[t]},keys:function(){var t=[];for(var e in this)"$"===e[0]&&t.push(e.slice(1));return t},values:function(){var t=[];for(var e in this)"$"===e[0]&&t.push(this[e]);return t},entries:function(){var t=[];for(var e in this)"$"===e[0]&&t.push({key:e.slice(1),value:this[e]});return t},size:function(){var t=0;for(var e in this)"$"===e[0]&&++t;return t},empty:function(){for(var t in this)if("$"===t[0])return!1;return!0},each:function(t){for(var e in this)"$"===e[0]&&t(this[e],e.slice(1),this)}};var Ji=Zi,Ki=function(){var t,e,n,r=[],i=[];function a(n,i,o,s){if(i>=r.length)return null!=t&&n.sort(t),null!=e?e(n):n;for(var c,u,l,h=-1,f=n.length,d=r[i++],p=Ji(),g=o();++h<f;)(l=p.get(c=d(u=n[h])+""))?l.push(u):p.set(c,[u]);return p.each((function(t,e){s(g,e,a(t,i,o,s))})),g}return n={object:function(t){return a(t,0,Qi,ta)},map:function(t){return a(t,0,ea,na)},entries:function(t){return function t(n,a){if(++a>r.length)return n;var o,s=i[a-1];return null!=e&&a>=r.length?o=n.entries():(o=[],n.each((function(e,n){o.push({key:n,values:t(e,a)})}))),null!=s?o.sort((function(t,e){return s(t.key,e.key)})):o}(a(t,0,ea,na),0)},key:function(t){return r.push(t),n},sortKeys:function(t){return i[r.length-1]=t,n},sortValues:function(e){return t=e,n},rollup:function(t){return e=t,n}}};function Qi(){return{}}function ta(t,e,n){t[e]=n}function ea(){return Ji()}function na(t,e,n){t.set(e,n)}function ra(){}var ia=Ji.prototype;function aa(t,e){var n=new ra;if(t instanceof ra)t.each((function(t){n.add(t)}));else if(t){var r=-1,i=t.length;if(null==e)for(;++r<i;)n.add(t[r]);else for(;++r<i;)n.add(e(t[r],r,t))}return n}ra.prototype=aa.prototype={constructor:ra,has:ia.has,add:function(t){return this["$"+(t+="")]=t,this},remove:ia.remove,clear:ia.clear,values:ia.keys,size:ia.size,empty:ia.empty,each:ia.each};var oa=aa,sa=function(t){var e=[];for(var n in t)e.push(n);return e},ca=function(t){var e=[];for(var n in t)e.push(t[n]);return e},ua=function(t){var e=[];for(var n in t)e.push({key:n,value:t[n]});return e},la=Math.PI/180,ha=180/Math.PI;function fa(t){if(t instanceof ga)return new ga(t.l,t.a,t.b,t.opacity);if(t instanceof wa)return Ea(t);t instanceof qe||(t=Ve(t));var e,n,r=ba(t.r),i=ba(t.g),a=ba(t.b),o=ya((.2225045*r+.7168786*i+.0606169*a)/1);return r===i&&i===a?e=n=o:(e=ya((.4360747*r+.3850649*i+.1430804*a)/.96422),n=ya((.0139322*r+.0971045*i+.7141733*a)/.82521)),new ga(116*o-16,500*(e-o),200*(o-n),t.opacity)}function da(t,e){return new ga(t,0,0,null==e?1:e)}function pa(t,e,n,r){return 1===arguments.length?fa(t):new ga(t,e,n,null==r?1:r)}function ga(t,e,n,r){this.l=+t,this.a=+e,this.b=+n,this.opacity=+r}function ya(t){return t>6/29*(6/29)*(6/29)?Math.pow(t,1/3):t/(6/29*3*(6/29))+4/29}function va(t){return t>6/29?t*t*t:6/29*3*(6/29)*(t-4/29)}function ma(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function ba(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function xa(t){if(t instanceof wa)return new wa(t.h,t.c,t.l,t.opacity);if(t instanceof ga||(t=fa(t)),0===t.a&&0===t.b)return new wa(NaN,0<t.l&&t.l<100?0:NaN,t.l,t.opacity);var e=Math.atan2(t.b,t.a)*ha;return new wa(e<0?e+360:e,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function _a(t,e,n,r){return 1===arguments.length?xa(t):new wa(n,e,t,null==r?1:r)}function ka(t,e,n,r){return 1===arguments.length?xa(t):new wa(t,e,n,null==r?1:r)}function wa(t,e,n,r){this.h=+t,this.c=+e,this.l=+n,this.opacity=+r}function Ea(t){if(isNaN(t.h))return new ga(t.l,0,0,t.opacity);var e=t.h*la;return new ga(t.l,Math.cos(e)*t.c,Math.sin(e)*t.c,t.opacity)}Ae(ga,pa,Se(Me,{brighter:function(t){return new ga(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new ga(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this.b)?t:t-this.b/200;return new qe(ma(3.1338561*(e=.96422*va(e))-1.6168667*(t=1*va(t))-.4906146*(n=.82521*va(n))),ma(-.9787684*e+1.9161415*t+.033454*n),ma(.0719453*e-.2289914*t+1.4052427*n),this.opacity)}})),Ae(wa,ka,Se(Me,{brighter:function(t){return new wa(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new wa(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return Ea(this).rgb()}}));var Ta=-.29227,Ca=-1.7884503806,Aa=3.5172982438,Sa=-.6557636667999999;function Ma(t){if(t instanceof Da)return new Da(t.h,t.s,t.l,t.opacity);t instanceof qe||(t=Ve(t));var e=t.r/255,n=t.g/255,r=t.b/255,i=(Sa*r+Ca*e-Aa*n)/(Sa+Ca-Aa),a=r-i,o=(1.97294*(n-i)-Ta*a)/-.90649,s=Math.sqrt(o*o+a*a)/(1.97294*i*(1-i)),c=s?Math.atan2(o,a)*ha-120:NaN;return new Da(c<0?c+360:c,s,i,t.opacity)}function Oa(t,e,n,r){return 1===arguments.length?Ma(t):new Da(t,e,n,null==r?1:r)}function Da(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}Ae(Da,Oa,Se(Me,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Da(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Da(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*la,e=+this.l,n=isNaN(this.s)?0:this.s*e*(1-e),r=Math.cos(t),i=Math.sin(t);return new qe(255*(e+n*(-.14861*r+1.78277*i)),255*(e+n*(Ta*r+-.90649*i)),255*(e+n*(1.97294*r)),this.opacity)}}));var Na=Array.prototype.slice,Ba=function(t,e){return t-e},La=function(t){return function(){return t}},Pa=function(t,e){for(var n,r=-1,i=e.length;++r<i;)if(n=Ia(t,e[r]))return n;return 0};function Ia(t,e){for(var n=e[0],r=e[1],i=-1,a=0,o=t.length,s=o-1;a<o;s=a++){var c=t[a],u=c[0],l=c[1],h=t[s],f=h[0],d=h[1];if(Fa(c,h,e))return 0;l>r!=d>r&&n<(f-u)*(r-l)/(d-l)+u&&(i=-i)}return i}function Fa(t,e,n){var r,i,a,o;return function(t,e,n){return(e[0]-t[0])*(n[1]-t[1])==(n[0]-t[0])*(e[1]-t[1])}(t,e,n)&&(i=t[r=+(t[0]===e[0])],a=n[r],o=e[r],i<=a&&a<=o||o<=a&&a<=i)}var ja=function(){},Ra=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]],Ya=function(){var t=1,e=1,n=M,r=s;function i(t){var e=n(t);if(Array.isArray(e))e=e.slice().sort(Ba);else{var r=y(t),i=r[0],o=r[1];e=S(i,o,e),e=k(Math.floor(i/e)*e,Math.floor(o/e)*e,e)}return e.map((function(e){return a(t,e)}))}function a(n,i){var a=[],s=[];return function(n,r,i){var a,s,c,u,l,h,f=new Array,d=new Array;a=s=-1,u=n[0]>=r,Ra[u<<1].forEach(p);for(;++a<t-1;)c=u,u=n[a+1]>=r,Ra[c|u<<1].forEach(p);Ra[u<<0].forEach(p);for(;++s<e-1;){for(a=-1,u=n[s*t+t]>=r,l=n[s*t]>=r,Ra[u<<1|l<<2].forEach(p);++a<t-1;)c=u,u=n[s*t+t+a+1]>=r,h=l,l=n[s*t+a+1]>=r,Ra[c|u<<1|l<<2|h<<3].forEach(p);Ra[u|l<<3].forEach(p)}a=-1,l=n[s*t]>=r,Ra[l<<2].forEach(p);for(;++a<t-1;)h=l,l=n[s*t+a+1]>=r,Ra[l<<2|h<<3].forEach(p);function p(t){var e,n,r=[t[0][0]+a,t[0][1]+s],c=[t[1][0]+a,t[1][1]+s],u=o(r),l=o(c);(e=d[u])?(n=f[l])?(delete d[e.end],delete f[n.start],e===n?(e.ring.push(c),i(e.ring)):f[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete d[e.end],e.ring.push(c),d[e.end=l]=e):(e=f[l])?(n=d[u])?(delete f[e.start],delete d[n.end],e===n?(e.ring.push(c),i(e.ring)):f[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete f[e.start],e.ring.unshift(r),f[e.start=u]=e):f[u]=d[l]={start:u,end:l,ring:[r,c]}}Ra[l<<3].forEach(p)}(n,i,(function(t){r(t,n,i),function(t){for(var e=0,n=t.length,r=t[n-1][1]*t[0][0]-t[n-1][0]*t[0][1];++e<n;)r+=t[e-1][1]*t[e][0]-t[e-1][0]*t[e][1];return r}(t)>0?a.push([t]):s.push(t)})),s.forEach((function(t){for(var e,n=0,r=a.length;n<r;++n)if(-1!==Pa((e=a[n])[0],t))return void e.push(t)})),{type:"MultiPolygon",value:i,coordinates:a}}function o(e){return 2*e[0]+e[1]*(t+1)*4}function s(n,r,i){n.forEach((function(n){var a,o=n[0],s=n[1],c=0|o,u=0|s,l=r[u*t+c];o>0&&o<t&&c===o&&(a=r[u*t+c-1],n[0]=o+(i-a)/(l-a)-.5),s>0&&s<e&&u===s&&(a=r[(u-1)*t+c],n[1]=s+(i-a)/(l-a)-.5)}))}return i.contour=a,i.size=function(n){if(!arguments.length)return[t,e];var r=Math.ceil(n[0]),a=Math.ceil(n[1]);if(!(r>0&&a>0))throw new Error("invalid size");return t=r,e=a,i},i.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?La(Na.call(t)):La(t),i):n},i.smooth=function(t){return arguments.length?(r=t?s:ja,i):r===s},i};function za(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<i;++o)for(var s=0,c=0;s<r+n;++s)s<r&&(c+=t.data[s+o*r]),s>=n&&(s>=a&&(c-=t.data[s-a+o*r]),e.data[s-n+o*r]=c/Math.min(s+1,r-1+a-s,a))}function Ua(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<r;++o)for(var s=0,c=0;s<i+n;++s)s<i&&(c+=t.data[o+s*r]),s>=n&&(s>=a&&(c-=t.data[o+(s-a)*r]),e.data[o+(s-n)*r]=c/Math.min(s+1,i-1+a-s,a))}function $a(t){return t[0]}function Wa(t){return t[1]}function Ha(){return 1}var Va=function(){var t=$a,e=Wa,n=Ha,r=960,i=500,a=20,o=2,s=3*a,c=r+2*s>>o,u=i+2*s>>o,l=La(20);function h(r){var i=new Float32Array(c*u),h=new Float32Array(c*u);r.forEach((function(r,a,l){var h=+t(r,a,l)+s>>o,f=+e(r,a,l)+s>>o,d=+n(r,a,l);h>=0&&h<c&&f>=0&&f<u&&(i[h+f*c]+=d)})),za({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),Ua({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),za({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),Ua({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),za({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),Ua({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o);var d=l(i);if(!Array.isArray(d)){var p=L(i);d=S(0,p,d),(d=k(0,Math.floor(p/d)*d,d)).shift()}return Ya().thresholds(d).size([c,u])(i).map(f)}function f(t){return t.value*=Math.pow(2,-2*o),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(g)}function g(t){t[0]=t[0]*Math.pow(2,o)-s,t[1]=t[1]*Math.pow(2,o)-s}function y(){return c=r+2*(s=3*a)>>o,u=i+2*s>>o,h}return h.x=function(e){return arguments.length?(t="function"==typeof e?e:La(+e),h):t},h.y=function(t){return arguments.length?(e="function"==typeof t?t:La(+t),h):e},h.weight=function(t){return arguments.length?(n="function"==typeof t?t:La(+t),h):n},h.size=function(t){if(!arguments.length)return[r,i];var e=Math.ceil(t[0]),n=Math.ceil(t[1]);if(!(e>=0||e>=0))throw new Error("invalid size");return r=e,i=n,y()},h.cellSize=function(t){if(!arguments.length)return 1<<o;if(!((t=+t)>=1))throw new Error("invalid cell size");return o=Math.floor(Math.log(t)/Math.LN2),y()},h.thresholds=function(t){return arguments.length?(l="function"==typeof t?t:Array.isArray(t)?La(Na.call(t)):La(t),h):l},h.bandwidth=function(t){if(!arguments.length)return Math.sqrt(a*(a+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return a=Math.round((Math.sqrt(4*t*t+1)-1)/2),y()},h},Ga=function(t){return function(){return t}};function qa(t,e,n,r,i,a,o,s,c,u){this.target=t,this.type=e,this.subject=n,this.identifier=r,this.active=i,this.x=a,this.y=o,this.dx=s,this.dy=c,this._=u}function Xa(){return!ce.ctrlKey&&!ce.button}function Za(){return this.parentNode}function Ja(t){return null==t?{x:ce.x,y:ce.y}:t}function Ka(){return navigator.maxTouchPoints||"ontouchstart"in this}qa.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var Qa=function(){var t,e,n,r,i=Xa,a=Za,o=Ja,s=Ka,c={},u=lt("start","drag","end"),l=0,h=0;function f(t){t.on("mousedown.drag",d).filter(s).on("touchstart.drag",y).on("touchmove.drag",v).on("touchend.drag touchcancel.drag",m).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(){if(!r&&i.apply(this,arguments)){var o=b("mouse",a.apply(this,arguments),Nn,this,arguments);o&&(ke(ce.view).on("mousemove.drag",p,!0).on("mouseup.drag",g,!0),Te(ce.view),we(),n=!1,t=ce.clientX,e=ce.clientY,o("start"))}}function p(){if(Ee(),!n){var r=ce.clientX-t,i=ce.clientY-e;n=r*r+i*i>h}c.mouse("drag")}function g(){ke(ce.view).on("mousemove.drag mouseup.drag",null),Ce(ce.view,n),Ee(),c.mouse("end")}function y(){if(i.apply(this,arguments)){var t,e,n=ce.changedTouches,r=a.apply(this,arguments),o=n.length;for(t=0;t<o;++t)(e=b(n[t].identifier,r,Dn,this,arguments))&&(we(),e("start"))}}function v(){var t,e,n=ce.changedTouches,r=n.length;for(t=0;t<r;++t)(e=c[n[t].identifier])&&(Ee(),e("drag"))}function m(){var t,e,n=ce.changedTouches,i=n.length;for(r&&clearTimeout(r),r=setTimeout((function(){r=null}),500),t=0;t<i;++t)(e=c[n[t].identifier])&&(we(),e("end"))}function b(t,e,n,r,i){var a,s,h,d=n(e,t),p=u.copy();if(pe(new qa(f,"beforestart",a,t,l,d[0],d[1],0,0,p),(function(){return null!=(ce.subject=a=o.apply(r,i))&&(s=a.x-d[0]||0,h=a.y-d[1]||0,!0)})))return function o(u){var g,y=d;switch(u){case"start":c[t]=o,g=l++;break;case"end":delete c[t],--l;case"drag":d=n(e,t),g=l}pe(new qa(f,u,a,t,g,d[0]+s,d[1]+h,d[0]-y[0],d[1]-y[1],p),p.apply,p,[u,r,i])}}return f.filter=function(t){return arguments.length?(i="function"==typeof t?t:Ga(!!t),f):i},f.container=function(t){return arguments.length?(a="function"==typeof t?t:Ga(t),f):a},f.subject=function(t){return arguments.length?(o="function"==typeof t?t:Ga(t),f):o},f.touchable=function(t){return arguments.length?(s="function"==typeof t?t:Ga(!!t),f):s},f.on=function(){var t=u.on.apply(u,arguments);return t===u?f:t},f.clickDistance=function(t){return arguments.length?(h=(t=+t)*t,f):Math.sqrt(h)},f},to={},eo={};function no(t){return new Function("d","return {"+t.map((function(t,e){return JSON.stringify(t)+": d["+e+'] || ""'})).join(",")+"}")}function ro(t){var e=Object.create(null),n=[];return t.forEach((function(t){for(var r in t)r in e||n.push(e[r]=r)})),n}function io(t,e){var n=t+"",r=n.length;return r<e?new Array(e-r+1).join(0)+n:n}function ao(t){var e,n=t.getUTCHours(),r=t.getUTCMinutes(),i=t.getUTCSeconds(),a=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":((e=t.getUTCFullYear())<0?"-"+io(-e,6):e>9999?"+"+io(e,6):io(e,4))+"-"+io(t.getUTCMonth()+1,2)+"-"+io(t.getUTCDate(),2)+(a?"T"+io(n,2)+":"+io(r,2)+":"+io(i,2)+"."+io(a,3)+"Z":i?"T"+io(n,2)+":"+io(r,2)+":"+io(i,2)+"Z":r||n?"T"+io(n,2)+":"+io(r,2)+"Z":"")}var oo=function(t){var e=new RegExp('["'+t+"\n\r]"),n=t.charCodeAt(0);function r(t,e){var r,i=[],a=t.length,o=0,s=0,c=a<=0,u=!1;function l(){if(c)return eo;if(u)return u=!1,to;var e,r,i=o;if(34===t.charCodeAt(i)){for(;o++<a&&34!==t.charCodeAt(o)||34===t.charCodeAt(++o););return(e=o)>=a?c=!0:10===(r=t.charCodeAt(o++))?u=!0:13===r&&(u=!0,10===t.charCodeAt(o)&&++o),t.slice(i+1,e-1).replace(/""/g,'"')}for(;o<a;){if(10===(r=t.charCodeAt(e=o++)))u=!0;else if(13===r)u=!0,10===t.charCodeAt(o)&&++o;else if(r!==n)continue;return t.slice(i,e)}return c=!0,t.slice(i,a)}for(10===t.charCodeAt(a-1)&&--a,13===t.charCodeAt(a-1)&&--a;(r=l())!==eo;){for(var h=[];r!==to&&r!==eo;)h.push(r),r=l();e&&null==(h=e(h,s++))||i.push(h)}return i}function i(e,n){return e.map((function(e){return n.map((function(t){return o(e[t])})).join(t)}))}function a(e){return e.map(o).join(t)}function o(t){return null==t?"":t instanceof Date?ao(t):e.test(t+="")?'"'+t.replace(/"/g,'""')+'"':t}return{parse:function(t,e){var n,i,a=r(t,(function(t,r){if(n)return n(t,r-1);i=t,n=e?function(t,e){var n=no(t);return function(r,i){return e(n(r),i,t)}}(t,e):no(t)}));return a.columns=i||[],a},parseRows:r,format:function(e,n){return null==n&&(n=ro(e)),[n.map(o).join(t)].concat(i(e,n)).join("\n")},formatBody:function(t,e){return null==e&&(e=ro(t)),i(t,e).join("\n")},formatRows:function(t){return t.map(a).join("\n")},formatRow:a,formatValue:o}},so=oo(","),co=so.parse,uo=so.parseRows,lo=so.format,ho=so.formatBody,fo=so.formatRows,po=so.formatRow,go=so.formatValue,yo=oo("\t"),vo=yo.parse,mo=yo.parseRows,bo=yo.format,xo=yo.formatBody,_o=yo.formatRows,ko=yo.formatRow,wo=yo.formatValue;function Eo(t){for(var e in t){var n,r,i=t[e].trim();if(i)if("true"===i)i=!0;else if("false"===i)i=!1;else if("NaN"===i)i=NaN;else if(isNaN(n=+i)){if(!(r=i.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/)))continue;To&&r[4]&&!r[7]&&(i=i.replace(/-/g,"/").replace(/T/," ")),i=new Date(i)}else i=n;else i=null;t[e]=i}return t}var To=new Date("2019-01-01T00:00").getHours()||new Date("2019-07-01T00:00").getHours();function Co(t){return+t}function Ao(t){return t*t}function So(t){return t*(2-t)}function Mo(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}var Oo=function t(e){function n(t){return Math.pow(t,e)}return e=+e,n.exponent=t,n}(3),Do=function t(e){function n(t){return 1-Math.pow(1-t,e)}return e=+e,n.exponent=t,n}(3),No=function t(e){function n(t){return((t*=2)<=1?Math.pow(t,e):2-Math.pow(2-t,e))/2}return e=+e,n.exponent=t,n}(3),Bo=Math.PI,Lo=Bo/2;function Po(t){return 1-Math.cos(t*Lo)}function Io(t){return Math.sin(t*Lo)}function Fo(t){return(1-Math.cos(Bo*t))/2}function jo(t){return Math.pow(2,10*t-10)}function Ro(t){return 1-Math.pow(2,-10*t)}function Yo(t){return((t*=2)<=1?Math.pow(2,10*t-10):2-Math.pow(2,10-10*t))/2}function zo(t){return 1-Math.sqrt(1-t*t)}function Uo(t){return Math.sqrt(1- --t*t)}function $o(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}function Wo(t){return 1-Ho(1-t)}function Ho(t){return(t=+t)<4/11?7.5625*t*t:t<8/11?7.5625*(t-=6/11)*t+.75:t<10/11?7.5625*(t-=9/11)*t+.9375:7.5625*(t-=21/22)*t+63/64}function Vo(t){return((t*=2)<=1?1-Ho(1-t):Ho(t-1)+1)/2}var Go=function t(e){function n(t){return t*t*((e+1)*t-e)}return e=+e,n.overshoot=t,n}(1.70158),qo=function t(e){function n(t){return--t*t*((e+1)*t+e)+1}return e=+e,n.overshoot=t,n}(1.70158),Xo=function t(e){function n(t){return((t*=2)<1?t*t*((e+1)*t-e):(t-=2)*t*((e+1)*t+e)+2)/2}return e=+e,n.overshoot=t,n}(1.70158),Zo=2*Math.PI,Jo=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=Zo);function i(t){return e*Math.pow(2,10*--t)*Math.sin((r-t)/n)}return i.amplitude=function(e){return t(e,n*Zo)},i.period=function(n){return t(e,n)},i}(1,.3),Ko=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=Zo);function i(t){return 1-e*Math.pow(2,-10*(t=+t))*Math.sin((t+r)/n)}return i.amplitude=function(e){return t(e,n*Zo)},i.period=function(n){return t(e,n)},i}(1,.3),Qo=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=Zo);function i(t){return((t=2*t-1)<0?e*Math.pow(2,10*t)*Math.sin((r-t)/n):2-e*Math.pow(2,-10*t)*Math.sin((r+t)/n))/2}return i.amplitude=function(e){return t(e,n*Zo)},i.period=function(n){return t(e,n)},i}(1,.3);function ts(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.blob()}var es=function(t,e){return fetch(t,e).then(ts)};function ns(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.arrayBuffer()}var rs=function(t,e){return fetch(t,e).then(ns)};function is(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.text()}var as=function(t,e){return fetch(t,e).then(is)};function os(t){return function(e,n,r){return 2===arguments.length&&"function"==typeof n&&(r=n,n=void 0),as(e,n).then((function(e){return t(e,r)}))}}function ss(t,e,n,r){3===arguments.length&&"function"==typeof n&&(r=n,n=void 0);var i=oo(t);return as(e,n).then((function(t){return i.parse(t,r)}))}var cs=os(co),us=os(vo),ls=function(t,e){return new Promise((function(n,r){var i=new Image;for(var a in e)i[a]=e[a];i.onerror=r,i.onload=function(){n(i)},i.src=t}))};function hs(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.json()}var fs=function(t,e){return fetch(t,e).then(hs)};function ds(t){return function(e,n){return as(e,n).then((function(e){return(new DOMParser).parseFromString(e,t)}))}}var ps=ds("application/xml"),gs=ds("text/html"),ys=ds("image/svg+xml"),vs=function(t,e){var n;function r(){var r,i,a=n.length,o=0,s=0;for(r=0;r<a;++r)o+=(i=n[r]).x,s+=i.y;for(o=o/a-t,s=s/a-e,r=0;r<a;++r)(i=n[r]).x-=o,i.y-=s}return null==t&&(t=0),null==e&&(e=0),r.initialize=function(t){n=t},r.x=function(e){return arguments.length?(t=+e,r):t},r.y=function(t){return arguments.length?(e=+t,r):e},r},ms=function(t){return function(){return t}},bs=function(){return 1e-6*(Math.random()-.5)};function xs(t,e,n,r){if(isNaN(e)||isNaN(n))return t;var i,a,o,s,c,u,l,h,f,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,m=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((u=e>=(a=(g+v)/2))?g=a:v=a,(l=n>=(o=(y+m)/2))?y=o:m=o,i=d,!(d=d[h=l<<1|u]))return i[h]=p,t;if(s=+t._x.call(null,d.data),c=+t._y.call(null,d.data),e===s&&n===c)return p.next=d,i?i[h]=p:t._root=p,t;do{i=i?i[h]=new Array(4):t._root=new Array(4),(u=e>=(a=(g+v)/2))?g=a:v=a,(l=n>=(o=(y+m)/2))?y=o:m=o}while((h=l<<1|u)==(f=(c>=o)<<1|s>=a));return i[f]=d,i[h]=p,t}var _s=function(t,e,n,r,i){this.node=t,this.x0=e,this.y0=n,this.x1=r,this.y1=i};function ks(t){return t[0]}function ws(t){return t[1]}function Es(t,e,n){var r=new Ts(null==e?ks:e,null==n?ws:n,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Ts(t,e,n,r,i,a){this._x=t,this._y=e,this._x0=n,this._y0=r,this._x1=i,this._y1=a,this._root=void 0}function Cs(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var As=Es.prototype=Ts.prototype;function Ss(t){return t.x+t.vx}function Ms(t){return t.y+t.vy}As.copy=function(){var t,e,n=new Ts(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return n;if(!r.length)return n._root=Cs(r),n;for(t=[{source:r,target:n._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(e=r.source[i])&&(e.length?t.push({source:e,target:r.target[i]=new Array(4)}):r.target[i]=Cs(e));return n},As.add=function(t){var e=+this._x.call(null,t),n=+this._y.call(null,t);return xs(this.cover(e,n),e,n,t)},As.addAll=function(t){var e,n,r,i,a=t.length,o=new Array(a),s=new Array(a),c=1/0,u=1/0,l=-1/0,h=-1/0;for(n=0;n<a;++n)isNaN(r=+this._x.call(null,e=t[n]))||isNaN(i=+this._y.call(null,e))||(o[n]=r,s[n]=i,r<c&&(c=r),r>l&&(l=r),i<u&&(u=i),i>h&&(h=i));if(c>l||u>h)return this;for(this.cover(c,u).cover(l,h),n=0;n<a;++n)xs(this,o[n],s[n],t[n]);return this},As.cover=function(t,e){if(isNaN(t=+t)||isNaN(e=+e))return this;var n=this._x0,r=this._y0,i=this._x1,a=this._y1;if(isNaN(n))i=(n=Math.floor(t))+1,a=(r=Math.floor(e))+1;else{for(var o,s,c=i-n,u=this._root;n>t||t>=i||r>e||e>=a;)switch(s=(e<r)<<1|t<n,(o=new Array(4))[s]=u,u=o,c*=2,s){case 0:i=n+c,a=r+c;break;case 1:n=i-c,a=r+c;break;case 2:i=n+c,r=a-c;break;case 3:n=i-c,r=a-c}this._root&&this._root.length&&(this._root=u)}return this._x0=n,this._y0=r,this._x1=i,this._y1=a,this},As.data=function(){var t=[];return this.visit((function(e){if(!e.length)do{t.push(e.data)}while(e=e.next)})),t},As.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},As.find=function(t,e,n){var r,i,a,o,s,c,u,l=this._x0,h=this._y0,f=this._x1,d=this._y1,p=[],g=this._root;for(g&&p.push(new _s(g,l,h,f,d)),null==n?n=1/0:(l=t-n,h=e-n,f=t+n,d=e+n,n*=n);c=p.pop();)if(!(!(g=c.node)||(i=c.x0)>f||(a=c.y0)>d||(o=c.x1)<l||(s=c.y1)<h))if(g.length){var y=(i+o)/2,v=(a+s)/2;p.push(new _s(g[3],y,v,o,s),new _s(g[2],i,v,y,s),new _s(g[1],y,a,o,v),new _s(g[0],i,a,y,v)),(u=(e>=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-u],p[p.length-1-u]=c)}else{var m=t-+this._x.call(null,g.data),b=e-+this._y.call(null,g.data),x=m*m+b*b;if(x<n){var _=Math.sqrt(n=x);l=t-_,h=e-_,f=t+_,d=e+_,r=g.data}}return r},As.remove=function(t){if(isNaN(a=+this._x.call(null,t))||isNaN(o=+this._y.call(null,t)))return this;var e,n,r,i,a,o,s,c,u,l,h,f,d=this._root,p=this._x0,g=this._y0,y=this._x1,v=this._y1;if(!d)return this;if(d.length)for(;;){if((u=a>=(s=(p+y)/2))?p=s:y=s,(l=o>=(c=(g+v)/2))?g=c:v=c,e=d,!(d=d[h=l<<1|u]))return this;if(!d.length)break;(e[h+1&3]||e[h+2&3]||e[h+3&3])&&(n=e,f=h)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):e?(i?e[h]=i:delete e[h],(d=e[0]||e[1]||e[2]||e[3])&&d===(e[3]||e[2]||e[1]||e[0])&&!d.length&&(n?n[f]=d:this._root=d),this):(this._root=i,this)},As.removeAll=function(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this},As.root=function(){return this._root},As.size=function(){var t=0;return this.visit((function(e){if(!e.length)do{++t}while(e=e.next)})),t},As.visit=function(t){var e,n,r,i,a,o,s=[],c=this._root;for(c&&s.push(new _s(c,this._x0,this._y0,this._x1,this._y1));e=s.pop();)if(!t(c=e.node,r=e.x0,i=e.y0,a=e.x1,o=e.y1)&&c.length){var u=(r+a)/2,l=(i+o)/2;(n=c[3])&&s.push(new _s(n,u,l,a,o)),(n=c[2])&&s.push(new _s(n,r,l,u,o)),(n=c[1])&&s.push(new _s(n,u,i,a,l)),(n=c[0])&&s.push(new _s(n,r,i,u,l))}return this},As.visitAfter=function(t){var e,n=[],r=[];for(this._root&&n.push(new _s(this._root,this._x0,this._y0,this._x1,this._y1));e=n.pop();){var i=e.node;if(i.length){var a,o=e.x0,s=e.y0,c=e.x1,u=e.y1,l=(o+c)/2,h=(s+u)/2;(a=i[0])&&n.push(new _s(a,o,s,l,h)),(a=i[1])&&n.push(new _s(a,l,s,c,h)),(a=i[2])&&n.push(new _s(a,o,h,l,u)),(a=i[3])&&n.push(new _s(a,l,h,c,u))}r.push(e)}for(;e=r.pop();)t(e.node,e.x0,e.y0,e.x1,e.y1);return this},As.x=function(t){return arguments.length?(this._x=t,this):this._x},As.y=function(t){return arguments.length?(this._y=t,this):this._y};var Os=function(t){var e,n,r=1,i=1;function a(){for(var t,a,s,c,u,l,h,f=e.length,d=0;d<i;++d)for(a=Es(e,Ss,Ms).visitAfter(o),t=0;t<f;++t)s=e[t],l=n[s.index],h=l*l,c=s.x+s.vx,u=s.y+s.vy,a.visit(p);function p(t,e,n,i,a){var o=t.data,f=t.r,d=l+f;if(!o)return e>c+d||i<c-d||n>u+d||a<u-d;if(o.index>s.index){var p=c-o.x-o.vx,g=u-o.y-o.vy,y=p*p+g*g;y<d*d&&(0===p&&(y+=(p=bs())*p),0===g&&(y+=(g=bs())*g),y=(d-(y=Math.sqrt(y)))/y*r,s.vx+=(p*=y)*(d=(f*=f)/(h+f)),s.vy+=(g*=y)*d,o.vx-=p*(d=1-d),o.vy-=g*d)}}}function o(t){if(t.data)return t.r=n[t.data.index];for(var e=t.r=0;e<4;++e)t[e]&&t[e].r>t.r&&(t.r=t[e].r)}function s(){if(e){var r,i,a=e.length;for(n=new Array(a),r=0;r<a;++r)i=e[r],n[i.index]=+t(i,r,e)}}return"function"!=typeof t&&(t=ms(null==t?1:+t)),a.initialize=function(t){e=t,s()},a.iterations=function(t){return arguments.length?(i=+t,a):i},a.strength=function(t){return arguments.length?(r=+t,a):r},a.radius=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),s(),a):t},a};function Ds(t){return t.index}function Ns(t,e){var n=t.get(e);if(!n)throw new Error("missing: "+e);return n}var Bs=function(t){var e,n,r,i,a,o=Ds,s=function(t){return 1/Math.min(i[t.source.index],i[t.target.index])},c=ms(30),u=1;function l(r){for(var i=0,o=t.length;i<u;++i)for(var s,c,l,h,f,d,p,g=0;g<o;++g)c=(s=t[g]).source,h=(l=s.target).x+l.vx-c.x-c.vx||bs(),f=l.y+l.vy-c.y-c.vy||bs(),h*=d=((d=Math.sqrt(h*h+f*f))-n[g])/d*r*e[g],f*=d,l.vx-=h*(p=a[g]),l.vy-=f*p,c.vx+=h*(p=1-p),c.vy+=f*p}function h(){if(r){var s,c,u=r.length,l=t.length,h=Ji(r,o);for(s=0,i=new Array(u);s<l;++s)(c=t[s]).index=s,"object"!=typeof c.source&&(c.source=Ns(h,c.source)),"object"!=typeof c.target&&(c.target=Ns(h,c.target)),i[c.source.index]=(i[c.source.index]||0)+1,i[c.target.index]=(i[c.target.index]||0)+1;for(s=0,a=new Array(l);s<l;++s)c=t[s],a[s]=i[c.source.index]/(i[c.source.index]+i[c.target.index]);e=new Array(l),f(),n=new Array(l),d()}}function f(){if(r)for(var n=0,i=t.length;n<i;++n)e[n]=+s(t[n],n,t)}function d(){if(r)for(var e=0,i=t.length;e<i;++e)n[e]=+c(t[e],e,t)}return null==t&&(t=[]),l.initialize=function(t){r=t,h()},l.links=function(e){return arguments.length?(t=e,h(),l):t},l.id=function(t){return arguments.length?(o=t,l):o},l.iterations=function(t){return arguments.length?(u=+t,l):u},l.strength=function(t){return arguments.length?(s="function"==typeof t?t:ms(+t),f(),l):s},l.distance=function(t){return arguments.length?(c="function"==typeof t?t:ms(+t),d(),l):c},l};function Ls(t){return t.x}function Ps(t){return t.y}var Is=Math.PI*(3-Math.sqrt(5)),Fs=function(t){var e,n=1,r=.001,i=1-Math.pow(r,1/300),a=0,o=.6,s=Ji(),c=Wn(l),u=lt("tick","end");function l(){h(),u.call("tick",e),n<r&&(c.stop(),u.call("end",e))}function h(r){var c,u,l=t.length;void 0===r&&(r=1);for(var h=0;h<r;++h)for(n+=(a-n)*i,s.each((function(t){t(n)})),c=0;c<l;++c)null==(u=t[c]).fx?u.x+=u.vx*=o:(u.x=u.fx,u.vx=0),null==u.fy?u.y+=u.vy*=o:(u.y=u.fy,u.vy=0);return e}function f(){for(var e,n=0,r=t.length;n<r;++n){if((e=t[n]).index=n,null!=e.fx&&(e.x=e.fx),null!=e.fy&&(e.y=e.fy),isNaN(e.x)||isNaN(e.y)){var i=10*Math.sqrt(n),a=n*Is;e.x=i*Math.cos(a),e.y=i*Math.sin(a)}(isNaN(e.vx)||isNaN(e.vy))&&(e.vx=e.vy=0)}}function d(e){return e.initialize&&e.initialize(t),e}return null==t&&(t=[]),f(),e={tick:h,restart:function(){return c.restart(l),e},stop:function(){return c.stop(),e},nodes:function(n){return arguments.length?(t=n,f(),s.each(d),e):t},alpha:function(t){return arguments.length?(n=+t,e):n},alphaMin:function(t){return arguments.length?(r=+t,e):r},alphaDecay:function(t){return arguments.length?(i=+t,e):+i},alphaTarget:function(t){return arguments.length?(a=+t,e):a},velocityDecay:function(t){return arguments.length?(o=1-t,e):1-o},force:function(t,n){return arguments.length>1?(null==n?s.remove(t):s.set(t,d(n)),e):s.get(t)},find:function(e,n,r){var i,a,o,s,c,u=0,l=t.length;for(null==r?r=1/0:r*=r,u=0;u<l;++u)(o=(i=e-(s=t[u]).x)*i+(a=n-s.y)*a)<r&&(c=s,r=o);return c},on:function(t,n){return arguments.length>1?(u.on(t,n),e):u.on(t)}}},js=function(){var t,e,n,r,i=ms(-30),a=1,o=1/0,s=.81;function c(r){var i,a=t.length,o=Es(t,Ls,Ps).visitAfter(l);for(n=r,i=0;i<a;++i)e=t[i],o.visit(h)}function u(){if(t){var e,n,a=t.length;for(r=new Array(a),e=0;e<a;++e)n=t[e],r[n.index]=+i(n,e,t)}}function l(t){var e,n,i,a,o,s=0,c=0;if(t.length){for(i=a=o=0;o<4;++o)(e=t[o])&&(n=Math.abs(e.value))&&(s+=e.value,c+=n,i+=n*e.x,a+=n*e.y);t.x=i/c,t.y=a/c}else{(e=t).x=e.data.x,e.y=e.data.y;do{s+=r[e.data.index]}while(e=e.next)}t.value=s}function h(t,i,c,u){if(!t.value)return!0;var l=t.x-e.x,h=t.y-e.y,f=u-i,d=l*l+h*h;if(f*f/s<d)return d<o&&(0===l&&(d+=(l=bs())*l),0===h&&(d+=(h=bs())*h),d<a&&(d=Math.sqrt(a*d)),e.vx+=l*t.value*n/d,e.vy+=h*t.value*n/d),!0;if(!(t.length||d>=o)){(t.data!==e||t.next)&&(0===l&&(d+=(l=bs())*l),0===h&&(d+=(h=bs())*h),d<a&&(d=Math.sqrt(a*d)));do{t.data!==e&&(f=r[t.data.index]*n/d,e.vx+=l*f,e.vy+=h*f)}while(t=t.next)}}return c.initialize=function(e){t=e,u()},c.strength=function(t){return arguments.length?(i="function"==typeof t?t:ms(+t),u(),c):i},c.distanceMin=function(t){return arguments.length?(a=t*t,c):Math.sqrt(a)},c.distanceMax=function(t){return arguments.length?(o=t*t,c):Math.sqrt(o)},c.theta=function(t){return arguments.length?(s=t*t,c):Math.sqrt(s)},c},Rs=function(t,e,n){var r,i,a,o=ms(.1);function s(t){for(var o=0,s=r.length;o<s;++o){var c=r[o],u=c.x-e||1e-6,l=c.y-n||1e-6,h=Math.sqrt(u*u+l*l),f=(a[o]-h)*i[o]*t/h;c.vx+=u*f,c.vy+=l*f}}function c(){if(r){var e,n=r.length;for(i=new Array(n),a=new Array(n),e=0;e<n;++e)a[e]=+t(r[e],e,r),i[e]=isNaN(a[e])?0:+o(r[e],e,r)}}return"function"!=typeof t&&(t=ms(+t)),null==e&&(e=0),null==n&&(n=0),s.initialize=function(t){r=t,c()},s.strength=function(t){return arguments.length?(o="function"==typeof t?t:ms(+t),c(),s):o},s.radius=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),c(),s):t},s.x=function(t){return arguments.length?(e=+t,s):e},s.y=function(t){return arguments.length?(n=+t,s):n},s},Ys=function(t){var e,n,r,i=ms(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vx+=(r[a]-i.x)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=ms(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:ms(+t),o(),a):i},a.x=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),o(),a):t},a},zs=function(t){var e,n,r,i=ms(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vy+=(r[a]-i.y)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=ms(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:ms(+t),o(),a):i},a.y=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),o(),a):t},a},Us=function(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,r=t.slice(0,n);return[r.length>1?r[0]+r.slice(2):r,+t.slice(n+1)]},$s=function(t){return(t=Us(Math.abs(t)))?t[1]:NaN},Ws=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Hs(t){if(!(e=Ws.exec(t)))throw new Error("invalid format: "+t);var e;return new Vs({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}function Vs(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}Hs.prototype=Vs.prototype,Vs.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var Gs,qs,Xs,Zs,Js=function(t,e){var n=Us(t,e);if(!n)return t+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")},Ks={"%":function(t,e){return(100*t).toFixed(e)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,e){return t.toExponential(e)},f:function(t,e){return t.toFixed(e)},g:function(t,e){return t.toPrecision(e)},o:function(t){return Math.round(t).toString(8)},p:function(t,e){return Js(100*t,e)},r:Js,s:function(t,e){var n=Us(t,e);if(!n)return t+"";var r=n[0],i=n[1],a=i-(Gs=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,o=r.length;return a===o?r:a>o?r+new Array(a-o+1).join("0"):a>0?r.slice(0,a)+"."+r.slice(a):"0."+new Array(1-a).join("0")+Us(t,Math.max(0,e+a-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},Qs=function(t){return t},tc=Array.prototype.map,ec=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"],nc=function(t){var e,n,r=void 0===t.grouping||void 0===t.thousands?Qs:(e=tc.call(t.grouping,Number),n=t.thousands+"",function(t,r){for(var i=t.length,a=[],o=0,s=e[0],c=0;i>0&&s>0&&(c+s+1>r&&(s=Math.max(1,r-c)),a.push(t.substring(i-=s,i+s)),!((c+=s+1)>r));)s=e[o=(o+1)%e.length];return a.reverse().join(n)}),i=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",o=void 0===t.decimal?".":t.decimal+"",s=void 0===t.numerals?Qs:function(t){return function(e){return e.replace(/[0-9]/g,(function(e){return t[+e]}))}}(tc.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",u=void 0===t.minus?"-":t.minus+"",l=void 0===t.nan?"NaN":t.nan+"";function h(t){var e=(t=Hs(t)).fill,n=t.align,h=t.sign,f=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,m=t.type;"n"===m?(g=!0,m="g"):Ks[m]||(void 0===y&&(y=12),v=!0,m="g"),(d||"0"===e&&"="===n)&&(d=!0,e="0",n="=");var b="$"===f?i:"#"===f&&/[boxX]/.test(m)?"0"+m.toLowerCase():"",x="$"===f?a:/[%p]/.test(m)?c:"",_=Ks[m],k=/[defgprs%]/.test(m);function w(t){var i,a,c,f=b,w=x;if("c"===m)w=_(t)+w,t="";else{var E=(t=+t)<0;if(t=isNaN(t)?l:_(Math.abs(t),y),v&&(t=function(t){t:for(var e,n=t.length,r=1,i=-1;r<n;++r)switch(t[r]){case".":i=e=r;break;case"0":0===i&&(i=r),e=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(e+1):t}(t)),E&&0==+t&&(E=!1),f=(E?"("===h?h:u:"-"===h||"("===h?"":h)+f,w=("s"===m?ec[8+Gs/3]:"")+w+(E&&"("===h?")":""),k)for(i=-1,a=t.length;++i<a;)if(48>(c=t.charCodeAt(i))||c>57){w=(46===c?o+t.slice(i+1):t.slice(i))+w,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var T=f.length+t.length+w.length,C=T<p?new Array(p-T+1).join(e):"";switch(g&&d&&(t=r(C+t,C.length?p-w.length:1/0),C=""),n){case"<":t=f+t+w+C;break;case"=":t=f+C+t+w;break;case"^":t=C.slice(0,T=C.length>>1)+f+t+w+C.slice(T);break;default:t=C+f+t+w}return s(t)}return y=void 0===y?6:/[gprs]/.test(m)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),w.toString=function(){return t+""},w}return{format:h,formatPrefix:function(t,e){var n=h(((t=Hs(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor($s(e)/3))),i=Math.pow(10,-r),a=ec[8+r/3];return function(t){return n(i*t)+a}}}};function rc(t){return qs=nc(t),Xs=qs.format,Zs=qs.formatPrefix,qs}rc({decimal:".",thousands:",",grouping:[3],currency:["$",""],minus:"-"});var ic=function(t){return Math.max(0,-$s(Math.abs(t)))},ac=function(t,e){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor($s(e)/3)))-$s(Math.abs(t)))},oc=function(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,$s(e)-$s(t))+1},sc=function(){return new cc};function cc(){this.reset()}cc.prototype={constructor:cc,reset:function(){this.s=this.t=0},add:function(t){lc(uc,t,this.t),lc(this,uc.s,this.s),this.s?this.t+=uc.t:this.s=uc.t},valueOf:function(){return this.s}};var uc=new cc;function lc(t,e,n){var r=t.s=e+n,i=r-e,a=r-i;t.t=e-a+(n-i)}var hc=Math.PI,fc=hc/2,dc=hc/4,pc=2*hc,gc=180/hc,yc=hc/180,vc=Math.abs,mc=Math.atan,bc=Math.atan2,xc=Math.cos,_c=Math.ceil,kc=Math.exp,wc=(Math.floor,Math.log),Ec=Math.pow,Tc=Math.sin,Cc=Math.sign||function(t){return t>0?1:t<0?-1:0},Ac=Math.sqrt,Sc=Math.tan;function Mc(t){return t>1?0:t<-1?hc:Math.acos(t)}function Oc(t){return t>1?fc:t<-1?-fc:Math.asin(t)}function Dc(t){return(t=Tc(t/2))*t}function Nc(){}function Bc(t,e){t&&Pc.hasOwnProperty(t.type)&&Pc[t.type](t,e)}var Lc={Feature:function(t,e){Bc(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)Bc(n[r].geometry,e)}},Pc={Sphere:function(t,e){e.sphere()},Point:function(t,e){t=t.coordinates,e.point(t[0],t[1],t[2])},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)t=n[r],e.point(t[0],t[1],t[2])},LineString:function(t,e){Ic(t.coordinates,e,0)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)Ic(n[r],e,0)},Polygon:function(t,e){Fc(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)Fc(n[r],e)},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)Bc(n[r],e)}};function Ic(t,e,n){var r,i=-1,a=t.length-n;for(e.lineStart();++i<a;)r=t[i],e.point(r[0],r[1],r[2]);e.lineEnd()}function Fc(t,e){var n=-1,r=t.length;for(e.polygonStart();++n<r;)Ic(t[n],e,1);e.polygonEnd()}var jc,Rc,Yc,zc,Uc,$c=function(t,e){t&&Lc.hasOwnProperty(t.type)?Lc[t.type](t,e):Bc(t,e)},Wc=sc(),Hc=sc(),Vc={point:Nc,lineStart:Nc,lineEnd:Nc,polygonStart:function(){Wc.reset(),Vc.lineStart=Gc,Vc.lineEnd=qc},polygonEnd:function(){var t=+Wc;Hc.add(t<0?pc+t:t),this.lineStart=this.lineEnd=this.point=Nc},sphere:function(){Hc.add(pc)}};function Gc(){Vc.point=Xc}function qc(){Zc(jc,Rc)}function Xc(t,e){Vc.point=Zc,jc=t,Rc=e,Yc=t*=yc,zc=xc(e=(e*=yc)/2+dc),Uc=Tc(e)}function Zc(t,e){var n=(t*=yc)-Yc,r=n>=0?1:-1,i=r*n,a=xc(e=(e*=yc)/2+dc),o=Tc(e),s=Uc*o,c=zc*a+s*xc(i),u=s*r*Tc(i);Wc.add(bc(u,c)),Yc=t,zc=a,Uc=o}var Jc=function(t){return Hc.reset(),$c(t,Vc),2*Hc};function Kc(t){return[bc(t[1],t[0]),Oc(t[2])]}function Qc(t){var e=t[0],n=t[1],r=xc(n);return[r*xc(e),r*Tc(e),Tc(n)]}function tu(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function eu(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function nu(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}function ru(t,e){return[t[0]*e,t[1]*e,t[2]*e]}function iu(t){var e=Ac(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t[2]/=e}var au,ou,su,cu,uu,lu,hu,fu,du,pu,gu=sc(),yu={point:vu,lineStart:bu,lineEnd:xu,polygonStart:function(){yu.point=_u,yu.lineStart=ku,yu.lineEnd=wu,gu.reset(),Vc.polygonStart()},polygonEnd:function(){Vc.polygonEnd(),yu.point=vu,yu.lineStart=bu,yu.lineEnd=xu,Wc<0?(au=-(su=180),ou=-(cu=90)):gu>1e-6?cu=90:gu<-1e-6&&(ou=-90),pu[0]=au,pu[1]=su},sphere:function(){au=-(su=180),ou=-(cu=90)}};function vu(t,e){du.push(pu=[au=t,su=t]),e<ou&&(ou=e),e>cu&&(cu=e)}function mu(t,e){var n=Qc([t*yc,e*yc]);if(fu){var r=eu(fu,n),i=eu([r[1],-r[0],0],r);iu(i),i=Kc(i);var a,o=t-uu,s=o>0?1:-1,c=i[0]*gc*s,u=vc(o)>180;u^(s*uu<c&&c<s*t)?(a=i[1]*gc)>cu&&(cu=a):u^(s*uu<(c=(c+360)%360-180)&&c<s*t)?(a=-i[1]*gc)<ou&&(ou=a):(e<ou&&(ou=e),e>cu&&(cu=e)),u?t<uu?Eu(au,t)>Eu(au,su)&&(su=t):Eu(t,su)>Eu(au,su)&&(au=t):su>=au?(t<au&&(au=t),t>su&&(su=t)):t>uu?Eu(au,t)>Eu(au,su)&&(su=t):Eu(t,su)>Eu(au,su)&&(au=t)}else du.push(pu=[au=t,su=t]);e<ou&&(ou=e),e>cu&&(cu=e),fu=n,uu=t}function bu(){yu.point=mu}function xu(){pu[0]=au,pu[1]=su,yu.point=vu,fu=null}function _u(t,e){if(fu){var n=t-uu;gu.add(vc(n)>180?n+(n>0?360:-360):n)}else lu=t,hu=e;Vc.point(t,e),mu(t,e)}function ku(){Vc.lineStart()}function wu(){_u(lu,hu),Vc.lineEnd(),vc(gu)>1e-6&&(au=-(su=180)),pu[0]=au,pu[1]=su,fu=null}function Eu(t,e){return(e-=t)<0?e+360:e}function Tu(t,e){return t[0]-e[0]}function Cu(t,e){return t[0]<=t[1]?t[0]<=e&&e<=t[1]:e<t[0]||t[1]<e}var Au,Su,Mu,Ou,Du,Nu,Bu,Lu,Pu,Iu,Fu,ju,Ru,Yu,zu,Uu,$u=function(t){var e,n,r,i,a,o,s;if(cu=su=-(au=ou=1/0),du=[],$c(t,yu),n=du.length){for(du.sort(Tu),e=1,a=[r=du[0]];e<n;++e)Cu(r,(i=du[e])[0])||Cu(r,i[1])?(Eu(r[0],i[1])>Eu(r[0],r[1])&&(r[1]=i[1]),Eu(i[0],r[1])>Eu(r[0],r[1])&&(r[0]=i[0])):a.push(r=i);for(o=-1/0,e=0,r=a[n=a.length-1];e<=n;r=i,++e)i=a[e],(s=Eu(r[1],i[0]))>o&&(o=s,au=i[0],su=r[1])}return du=pu=null,au===1/0||ou===1/0?[[NaN,NaN],[NaN,NaN]]:[[au,ou],[su,cu]]},Wu={sphere:Nc,point:Hu,lineStart:Gu,lineEnd:Zu,polygonStart:function(){Wu.lineStart=Ju,Wu.lineEnd=Ku},polygonEnd:function(){Wu.lineStart=Gu,Wu.lineEnd=Zu}};function Hu(t,e){t*=yc;var n=xc(e*=yc);Vu(n*xc(t),n*Tc(t),Tc(e))}function Vu(t,e,n){++Au,Mu+=(t-Mu)/Au,Ou+=(e-Ou)/Au,Du+=(n-Du)/Au}function Gu(){Wu.point=qu}function qu(t,e){t*=yc;var n=xc(e*=yc);Yu=n*xc(t),zu=n*Tc(t),Uu=Tc(e),Wu.point=Xu,Vu(Yu,zu,Uu)}function Xu(t,e){t*=yc;var n=xc(e*=yc),r=n*xc(t),i=n*Tc(t),a=Tc(e),o=bc(Ac((o=zu*a-Uu*i)*o+(o=Uu*r-Yu*a)*o+(o=Yu*i-zu*r)*o),Yu*r+zu*i+Uu*a);Su+=o,Nu+=o*(Yu+(Yu=r)),Bu+=o*(zu+(zu=i)),Lu+=o*(Uu+(Uu=a)),Vu(Yu,zu,Uu)}function Zu(){Wu.point=Hu}function Ju(){Wu.point=Qu}function Ku(){tl(ju,Ru),Wu.point=Hu}function Qu(t,e){ju=t,Ru=e,t*=yc,e*=yc,Wu.point=tl;var n=xc(e);Yu=n*xc(t),zu=n*Tc(t),Uu=Tc(e),Vu(Yu,zu,Uu)}function tl(t,e){t*=yc;var n=xc(e*=yc),r=n*xc(t),i=n*Tc(t),a=Tc(e),o=zu*a-Uu*i,s=Uu*r-Yu*a,c=Yu*i-zu*r,u=Ac(o*o+s*s+c*c),l=Oc(u),h=u&&-l/u;Pu+=h*o,Iu+=h*s,Fu+=h*c,Su+=l,Nu+=l*(Yu+(Yu=r)),Bu+=l*(zu+(zu=i)),Lu+=l*(Uu+(Uu=a)),Vu(Yu,zu,Uu)}var el=function(t){Au=Su=Mu=Ou=Du=Nu=Bu=Lu=Pu=Iu=Fu=0,$c(t,Wu);var e=Pu,n=Iu,r=Fu,i=e*e+n*n+r*r;return i<1e-12&&(e=Nu,n=Bu,r=Lu,Su<1e-6&&(e=Mu,n=Ou,r=Du),(i=e*e+n*n+r*r)<1e-12)?[NaN,NaN]:[bc(n,e)*gc,Oc(r/Ac(i))*gc]},nl=function(t){return function(){return t}},rl=function(t,e){function n(n,r){return n=t(n,r),e(n[0],n[1])}return t.invert&&e.invert&&(n.invert=function(n,r){return(n=e.invert(n,r))&&t.invert(n[0],n[1])}),n};function il(t,e){return[vc(t)>hc?t+Math.round(-t/pc)*pc:t,e]}function al(t,e,n){return(t%=pc)?e||n?rl(sl(t),cl(e,n)):sl(t):e||n?cl(e,n):il}function ol(t){return function(e,n){return[(e+=t)>hc?e-pc:e<-hc?e+pc:e,n]}}function sl(t){var e=ol(t);return e.invert=ol(-t),e}function cl(t,e){var n=xc(t),r=Tc(t),i=xc(e),a=Tc(e);function o(t,e){var o=xc(e),s=xc(t)*o,c=Tc(t)*o,u=Tc(e),l=u*n+s*r;return[bc(c*i-l*a,s*n-u*r),Oc(l*i+c*a)]}return o.invert=function(t,e){var o=xc(e),s=xc(t)*o,c=Tc(t)*o,u=Tc(e),l=u*i-c*a;return[bc(c*i+u*a,s*n+l*r),Oc(l*n-s*r)]},o}il.invert=il;var ul=function(t){function e(e){return(e=t(e[0]*yc,e[1]*yc))[0]*=gc,e[1]*=gc,e}return t=al(t[0]*yc,t[1]*yc,t.length>2?t[2]*yc:0),e.invert=function(e){return(e=t.invert(e[0]*yc,e[1]*yc))[0]*=gc,e[1]*=gc,e},e};function ll(t,e,n,r,i,a){if(n){var o=xc(e),s=Tc(e),c=r*n;null==i?(i=e+r*pc,a=e-c/2):(i=hl(o,i),a=hl(o,a),(r>0?i<a:i>a)&&(i+=r*pc));for(var u,l=i;r>0?l>a:l<a;l-=c)u=Kc([o,-s*xc(l),-s*Tc(l)]),t.point(u[0],u[1])}}function hl(t,e){(e=Qc(e))[0]-=t,iu(e);var n=Mc(-e[1]);return((-e[2]<0?-n:n)+pc-1e-6)%pc}var fl=function(){var t,e,n=nl([0,0]),r=nl(90),i=nl(6),a={point:function(n,r){t.push(n=e(n,r)),n[0]*=gc,n[1]*=gc}};function o(){var o=n.apply(this,arguments),s=r.apply(this,arguments)*yc,c=i.apply(this,arguments)*yc;return t=[],e=al(-o[0]*yc,-o[1]*yc,0).invert,ll(a,s,c,1),o={type:"Polygon",coordinates:[t]},t=e=null,o}return o.center=function(t){return arguments.length?(n="function"==typeof t?t:nl([+t[0],+t[1]]),o):n},o.radius=function(t){return arguments.length?(r="function"==typeof t?t:nl(+t),o):r},o.precision=function(t){return arguments.length?(i="function"==typeof t?t:nl(+t),o):i},o},dl=function(){var t,e=[];return{point:function(e,n){t.push([e,n])},lineStart:function(){e.push(t=[])},lineEnd:Nc,rejoin:function(){e.length>1&&e.push(e.pop().concat(e.shift()))},result:function(){var n=e;return e=[],t=null,n}}},pl=function(t,e){return vc(t[0]-e[0])<1e-6&&vc(t[1]-e[1])<1e-6};function gl(t,e,n,r){this.x=t,this.z=e,this.o=n,this.e=r,this.v=!1,this.n=this.p=null}var yl=function(t,e,n,r,i){var a,o,s=[],c=[];if(t.forEach((function(t){if(!((e=t.length-1)<=0)){var e,n,r=t[0],o=t[e];if(pl(r,o)){for(i.lineStart(),a=0;a<e;++a)i.point((r=t[a])[0],r[1]);i.lineEnd()}else s.push(n=new gl(r,t,null,!0)),c.push(n.o=new gl(r,null,n,!1)),s.push(n=new gl(o,t,null,!1)),c.push(n.o=new gl(o,null,n,!0))}})),s.length){for(c.sort(e),vl(s),vl(c),a=0,o=c.length;a<o;++a)c[a].e=n=!n;for(var u,l,h=s[0];;){for(var f=h,d=!0;f.v;)if((f=f.n)===h)return;u=f.z,i.lineStart();do{if(f.v=f.o.v=!0,f.e){if(d)for(a=0,o=u.length;a<o;++a)i.point((l=u[a])[0],l[1]);else r(f.x,f.n.x,1,i);f=f.n}else{if(d)for(u=f.p.z,a=u.length-1;a>=0;--a)i.point((l=u[a])[0],l[1]);else r(f.x,f.p.x,-1,i);f=f.p}u=(f=f.o).z,d=!d}while(!f.v);i.lineEnd()}}};function vl(t){if(e=t.length){for(var e,n,r=0,i=t[0];++r<e;)i.n=n=t[r],n.p=i,i=n;i.n=n=t[0],n.p=i}}var ml=sc();function bl(t){return vc(t[0])<=hc?t[0]:Cc(t[0])*((vc(t[0])+hc)%pc-hc)}var xl=function(t,e){var n=bl(e),r=e[1],i=Tc(r),a=[Tc(n),-xc(n),0],o=0,s=0;ml.reset(),1===i?r=fc+1e-6:-1===i&&(r=-fc-1e-6);for(var c=0,u=t.length;c<u;++c)if(h=(l=t[c]).length)for(var l,h,f=l[h-1],d=bl(f),p=f[1]/2+dc,g=Tc(p),y=xc(p),v=0;v<h;++v,d=b,g=_,y=k,f=m){var m=l[v],b=bl(m),x=m[1]/2+dc,_=Tc(x),k=xc(x),w=b-d,E=w>=0?1:-1,T=E*w,C=T>hc,A=g*_;if(ml.add(bc(A*E*Tc(T),y*k+A*xc(T))),o+=C?w+E*pc:w,C^d>=n^b>=n){var S=eu(Qc(f),Qc(m));iu(S);var M=eu(a,S);iu(M);var O=(C^w>=0?-1:1)*Oc(M[2]);(r>O||r===O&&(S[0]||S[1]))&&(s+=C^w>=0?1:-1)}}return(o<-1e-6||o<1e-6&&ml<-1e-6)^1&s},_l=function(t,e,n,r){return function(i){var a,o,s,c=e(i),u=dl(),l=e(u),h=!1,f={point:d,lineStart:g,lineEnd:y,polygonStart:function(){f.point=v,f.lineStart=m,f.lineEnd=b,o=[],a=[]},polygonEnd:function(){f.point=d,f.lineStart=g,f.lineEnd=y,o=F(o);var t=xl(a,r);o.length?(h||(i.polygonStart(),h=!0),yl(o,wl,t,n,i)):t&&(h||(i.polygonStart(),h=!0),i.lineStart(),n(null,null,1,i),i.lineEnd()),h&&(i.polygonEnd(),h=!1),o=a=null},sphere:function(){i.polygonStart(),i.lineStart(),n(null,null,1,i),i.lineEnd(),i.polygonEnd()}};function d(e,n){t(e,n)&&i.point(e,n)}function p(t,e){c.point(t,e)}function g(){f.point=p,c.lineStart()}function y(){f.point=d,c.lineEnd()}function v(t,e){s.push([t,e]),l.point(t,e)}function m(){l.lineStart(),s=[]}function b(){v(s[0][0],s[0][1]),l.lineEnd();var t,e,n,r,c=l.clean(),f=u.result(),d=f.length;if(s.pop(),a.push(s),s=null,d)if(1&c){if((e=(n=f[0]).length-1)>0){for(h||(i.polygonStart(),h=!0),i.lineStart(),t=0;t<e;++t)i.point((r=n[t])[0],r[1]);i.lineEnd()}}else d>1&&2&c&&f.push(f.pop().concat(f.shift())),o.push(f.filter(kl))}return f}};function kl(t){return t.length>1}function wl(t,e){return((t=t.x)[0]<0?t[1]-fc-1e-6:fc-t[1])-((e=e.x)[0]<0?e[1]-fc-1e-6:fc-e[1])}var El=_l((function(){return!0}),(function(t){var e,n=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),e=1},point:function(a,o){var s=a>0?hc:-hc,c=vc(a-n);vc(c-hc)<1e-6?(t.point(n,r=(r+o)/2>0?fc:-fc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),t.point(a,r),e=0):i!==s&&c>=hc&&(vc(n-i)<1e-6&&(n-=1e-6*i),vc(a-s)<1e-6&&(a-=1e-6*s),r=function(t,e,n,r){var i,a,o=Tc(t-n);return vc(o)>1e-6?mc((Tc(e)*(a=xc(r))*Tc(n)-Tc(r)*(i=xc(e))*Tc(t))/(i*a*o)):(e+r)/2}(n,r,a,o),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),e=0),t.point(n=a,r=o),i=s},lineEnd:function(){t.lineEnd(),n=r=NaN},clean:function(){return 2-e}}}),(function(t,e,n,r){var i;if(null==t)i=n*fc,r.point(-hc,i),r.point(0,i),r.point(hc,i),r.point(hc,0),r.point(hc,-i),r.point(0,-i),r.point(-hc,-i),r.point(-hc,0),r.point(-hc,i);else if(vc(t[0]-e[0])>1e-6){var a=t[0]<e[0]?hc:-hc;i=n*a/2,r.point(-a,i),r.point(0,i),r.point(a,i)}else r.point(e[0],e[1])}),[-hc,-fc]);var Tl=function(t){var e=xc(t),n=6*yc,r=e>0,i=vc(e)>1e-6;function a(t,n){return xc(t)*xc(n)>e}function o(t,n,r){var i=[1,0,0],a=eu(Qc(t),Qc(n)),o=tu(a,a),s=a[0],c=o-s*s;if(!c)return!r&&t;var u=e*o/c,l=-e*s/c,h=eu(i,a),f=ru(i,u);nu(f,ru(a,l));var d=h,p=tu(f,d),g=tu(d,d),y=p*p-g*(tu(f,f)-1);if(!(y<0)){var v=Ac(y),m=ru(d,(-p-v)/g);if(nu(m,f),m=Kc(m),!r)return m;var b,x=t[0],_=n[0],k=t[1],w=n[1];_<x&&(b=x,x=_,_=b);var E=_-x,T=vc(E-hc)<1e-6;if(!T&&w<k&&(b=k,k=w,w=b),T||E<1e-6?T?k+w>0^m[1]<(vc(m[0]-x)<1e-6?k:w):k<=m[1]&&m[1]<=w:E>hc^(x<=m[0]&&m[0]<=_)){var C=ru(d,(-p+v)/g);return nu(C,f),[m,Kc(C)]}}}function s(e,n){var i=r?t:hc-t,a=0;return e<-i?a|=1:e>i&&(a|=2),n<-i?a|=4:n>i&&(a|=8),a}return _l(a,(function(t){var e,n,c,u,l;return{lineStart:function(){u=c=!1,l=1},point:function(h,f){var d,p=[h,f],g=a(h,f),y=r?g?0:s(h,f):g?s(h+(h<0?hc:-hc),f):0;if(!e&&(u=c=g)&&t.lineStart(),g!==c&&(!(d=o(e,p))||pl(e,d)||pl(p,d))&&(p[0]+=1e-6,p[1]+=1e-6,g=a(p[0],p[1])),g!==c)l=0,g?(t.lineStart(),d=o(p,e),t.point(d[0],d[1])):(d=o(e,p),t.point(d[0],d[1]),t.lineEnd()),e=d;else if(i&&e&&r^g){var v;y&n||!(v=o(p,e,!0))||(l=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1])))}!g||e&&pl(e,p)||t.point(p[0],p[1]),e=p,c=g,n=y},lineEnd:function(){c&&t.lineEnd(),e=null},clean:function(){return l|(u&&c)<<1}}}),(function(e,r,i,a){ll(a,t,n,i,e,r)}),r?[0,-t]:[-hc,t-hc])};function Cl(t,e,n,r){function i(i,a){return t<=i&&i<=n&&e<=a&&a<=r}function a(i,a,s,u){var l=0,h=0;if(null==i||(l=o(i,s))!==(h=o(a,s))||c(i,a)<0^s>0)do{u.point(0===l||3===l?t:n,l>1?r:e)}while((l=(l+s+4)%4)!==h);else u.point(a[0],a[1])}function o(r,i){return vc(r[0]-t)<1e-6?i>0?0:3:vc(r[0]-n)<1e-6?i>0?2:1:vc(r[1]-e)<1e-6?i>0?1:0:i>0?3:2}function s(t,e){return c(t.x,e.x)}function c(t,e){var n=o(t,1),r=o(e,1);return n!==r?n-r:0===n?e[1]-t[1]:1===n?t[0]-e[0]:2===n?t[1]-e[1]:e[0]-t[0]}return function(o){var c,u,l,h,f,d,p,g,y,v,m,b=o,x=dl(),_={point:k,lineStart:function(){_.point=w,u&&u.push(l=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(w(h,f),d&&y&&x.rejoin(),c.push(x.result()));_.point=k,y&&b.lineEnd()},polygonStart:function(){b=x,c=[],u=[],m=!0},polygonEnd:function(){var e=function(){for(var e=0,n=0,i=u.length;n<i;++n)for(var a,o,s=u[n],c=1,l=s.length,h=s[0],f=h[0],d=h[1];c<l;++c)a=f,o=d,h=s[c],f=h[0],d=h[1],o<=r?d>r&&(f-a)*(r-o)>(d-o)*(t-a)&&++e:d<=r&&(f-a)*(r-o)<(d-o)*(t-a)&&--e;return e}(),n=m&&e,i=(c=F(c)).length;(n||i)&&(o.polygonStart(),n&&(o.lineStart(),a(null,null,1,o),o.lineEnd()),i&&yl(c,s,e,a,o),o.polygonEnd());b=o,c=u=l=null}};function k(t,e){i(t,e)&&b.point(t,e)}function w(a,o){var s=i(a,o);if(u&&l.push([a,o]),v)h=a,f=o,d=s,v=!1,s&&(b.lineStart(),b.point(a,o));else if(s&&y)b.point(a,o);else{var c=[p=Math.max(-1e9,Math.min(1e9,p)),g=Math.max(-1e9,Math.min(1e9,g))],x=[a=Math.max(-1e9,Math.min(1e9,a)),o=Math.max(-1e9,Math.min(1e9,o))];!function(t,e,n,r,i,a){var o,s=t[0],c=t[1],u=0,l=1,h=e[0]-s,f=e[1]-c;if(o=n-s,h||!(o>0)){if(o/=h,h<0){if(o<u)return;o<l&&(l=o)}else if(h>0){if(o>l)return;o>u&&(u=o)}if(o=i-s,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>u&&(u=o)}else if(h>0){if(o<u)return;o<l&&(l=o)}if(o=r-c,f||!(o>0)){if(o/=f,f<0){if(o<u)return;o<l&&(l=o)}else if(f>0){if(o>l)return;o>u&&(u=o)}if(o=a-c,f||!(o<0)){if(o/=f,f<0){if(o>l)return;o>u&&(u=o)}else if(f>0){if(o<u)return;o<l&&(l=o)}return u>0&&(t[0]=s+u*h,t[1]=c+u*f),l<1&&(e[0]=s+l*h,e[1]=c+l*f),!0}}}}}(c,x,t,e,n,r)?s&&(b.lineStart(),b.point(a,o),m=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(x[0],x[1]),s||b.lineEnd(),m=!1)}p=a,g=o,y=s}return _}}var Al,Sl,Ml,Ol=function(){var t,e,n,r=0,i=0,a=960,o=500;return n={stream:function(n){return t&&e===n?t:t=Cl(r,i,a,o)(e=n)},extent:function(s){return arguments.length?(r=+s[0][0],i=+s[0][1],a=+s[1][0],o=+s[1][1],t=e=null,n):[[r,i],[a,o]]}}},Dl=sc(),Nl={sphere:Nc,point:Nc,lineStart:function(){Nl.point=Ll,Nl.lineEnd=Bl},lineEnd:Nc,polygonStart:Nc,polygonEnd:Nc};function Bl(){Nl.point=Nl.lineEnd=Nc}function Ll(t,e){Al=t*=yc,Sl=Tc(e*=yc),Ml=xc(e),Nl.point=Pl}function Pl(t,e){t*=yc;var n=Tc(e*=yc),r=xc(e),i=vc(t-Al),a=xc(i),o=r*Tc(i),s=Ml*n-Sl*r*a,c=Sl*n+Ml*r*a;Dl.add(bc(Ac(o*o+s*s),c)),Al=t,Sl=n,Ml=r}var Il=function(t){return Dl.reset(),$c(t,Nl),+Dl},Fl=[null,null],jl={type:"LineString",coordinates:Fl},Rl=function(t,e){return Fl[0]=t,Fl[1]=e,Il(jl)},Yl={Feature:function(t,e){return Ul(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)if(Ul(n[r].geometry,e))return!0;return!1}},zl={Sphere:function(){return!0},Point:function(t,e){return $l(t.coordinates,e)},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if($l(n[r],e))return!0;return!1},LineString:function(t,e){return Wl(t.coordinates,e)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(Wl(n[r],e))return!0;return!1},Polygon:function(t,e){return Hl(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(Hl(n[r],e))return!0;return!1},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)if(Ul(n[r],e))return!0;return!1}};function Ul(t,e){return!(!t||!zl.hasOwnProperty(t.type))&&zl[t.type](t,e)}function $l(t,e){return 0===Rl(t,e)}function Wl(t,e){for(var n,r,i,a=0,o=t.length;a<o;a++){if(0===(r=Rl(t[a],e)))return!0;if(a>0&&(i=Rl(t[a],t[a-1]))>0&&n<=i&&r<=i&&(n+r-i)*(1-Math.pow((n-r)/i,2))<1e-12*i)return!0;n=r}return!1}function Hl(t,e){return!!xl(t.map(Vl),Gl(e))}function Vl(t){return(t=t.map(Gl)).pop(),t}function Gl(t){return[t[0]*yc,t[1]*yc]}var ql=function(t,e){return(t&&Yl.hasOwnProperty(t.type)?Yl[t.type]:Ul)(t,e)};function Xl(t,e,n){var r=k(t,e-1e-6,n).concat(e);return function(t){return r.map((function(e){return[t,e]}))}}function Zl(t,e,n){var r=k(t,e-1e-6,n).concat(e);return function(t){return r.map((function(e){return[e,t]}))}}function Jl(){var t,e,n,r,i,a,o,s,c,u,l,h,f=10,d=f,p=90,g=360,y=2.5;function v(){return{type:"MultiLineString",coordinates:m()}}function m(){return k(_c(r/p)*p,n,p).map(l).concat(k(_c(s/g)*g,o,g).map(h)).concat(k(_c(e/f)*f,t,f).filter((function(t){return vc(t%p)>1e-6})).map(c)).concat(k(_c(a/d)*d,i,d).filter((function(t){return vc(t%g)>1e-6})).map(u))}return v.lines=function(){return m().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[l(r).concat(h(o).slice(1),l(n).reverse().slice(1),h(s).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],n=+t[1][0],s=+t[0][1],o=+t[1][1],r>n&&(t=r,r=n,n=t),s>o&&(t=s,s=o,o=t),v.precision(y)):[[r,s],[n,o]]},v.extentMinor=function(n){return arguments.length?(e=+n[0][0],t=+n[1][0],a=+n[0][1],i=+n[1][1],e>t&&(n=e,e=t,t=n),a>i&&(n=a,a=i,i=n),v.precision(y)):[[e,a],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(f=+t[0],d=+t[1],v):[f,d]},v.precision=function(f){return arguments.length?(y=+f,c=Xl(a,i,90),u=Zl(e,t,y),l=Xl(s,o,90),h=Zl(r,n,y),v):y},v.extentMajor([[-180,1e-6-90],[180,90-1e-6]]).extentMinor([[-180,-80-1e-6],[180,80+1e-6]])}function Kl(){return Jl()()}var Ql,th,eh,nh,rh=function(t,e){var n=t[0]*yc,r=t[1]*yc,i=e[0]*yc,a=e[1]*yc,o=xc(r),s=Tc(r),c=xc(a),u=Tc(a),l=o*xc(n),h=o*Tc(n),f=c*xc(i),d=c*Tc(i),p=2*Oc(Ac(Dc(a-r)+o*c*Dc(i-n))),g=Tc(p),y=p?function(t){var e=Tc(t*=p)/g,n=Tc(p-t)/g,r=n*l+e*f,i=n*h+e*d,a=n*s+e*u;return[bc(i,r)*gc,bc(a,Ac(r*r+i*i))*gc]}:function(){return[n*gc,r*gc]};return y.distance=p,y},ih=function(t){return t},ah=sc(),oh=sc(),sh={point:Nc,lineStart:Nc,lineEnd:Nc,polygonStart:function(){sh.lineStart=ch,sh.lineEnd=hh},polygonEnd:function(){sh.lineStart=sh.lineEnd=sh.point=Nc,ah.add(vc(oh)),oh.reset()},result:function(){var t=ah/2;return ah.reset(),t}};function ch(){sh.point=uh}function uh(t,e){sh.point=lh,Ql=eh=t,th=nh=e}function lh(t,e){oh.add(nh*t-eh*e),eh=t,nh=e}function hh(){lh(Ql,th)}var fh=sh,dh=1/0,ph=dh,gh=-dh,yh=gh;var vh,mh,bh,xh,_h={point:function(t,e){t<dh&&(dh=t);t>gh&&(gh=t);e<ph&&(ph=e);e>yh&&(yh=e)},lineStart:Nc,lineEnd:Nc,polygonStart:Nc,polygonEnd:Nc,result:function(){var t=[[dh,ph],[gh,yh]];return gh=yh=-(ph=dh=1/0),t}},kh=0,wh=0,Eh=0,Th=0,Ch=0,Ah=0,Sh=0,Mh=0,Oh=0,Dh={point:Nh,lineStart:Bh,lineEnd:Ih,polygonStart:function(){Dh.lineStart=Fh,Dh.lineEnd=jh},polygonEnd:function(){Dh.point=Nh,Dh.lineStart=Bh,Dh.lineEnd=Ih},result:function(){var t=Oh?[Sh/Oh,Mh/Oh]:Ah?[Th/Ah,Ch/Ah]:Eh?[kh/Eh,wh/Eh]:[NaN,NaN];return kh=wh=Eh=Th=Ch=Ah=Sh=Mh=Oh=0,t}};function Nh(t,e){kh+=t,wh+=e,++Eh}function Bh(){Dh.point=Lh}function Lh(t,e){Dh.point=Ph,Nh(bh=t,xh=e)}function Ph(t,e){var n=t-bh,r=e-xh,i=Ac(n*n+r*r);Th+=i*(bh+t)/2,Ch+=i*(xh+e)/2,Ah+=i,Nh(bh=t,xh=e)}function Ih(){Dh.point=Nh}function Fh(){Dh.point=Rh}function jh(){Yh(vh,mh)}function Rh(t,e){Dh.point=Yh,Nh(vh=bh=t,mh=xh=e)}function Yh(t,e){var n=t-bh,r=e-xh,i=Ac(n*n+r*r);Th+=i*(bh+t)/2,Ch+=i*(xh+e)/2,Ah+=i,Sh+=(i=xh*t-bh*e)*(bh+t),Mh+=i*(xh+e),Oh+=3*i,Nh(bh=t,xh=e)}var zh=Dh;function Uh(t){this._context=t}Uh.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._context.moveTo(t,e),this._point=1;break;case 1:this._context.lineTo(t,e);break;default:this._context.moveTo(t+this._radius,e),this._context.arc(t,e,this._radius,0,pc)}},result:Nc};var $h,Wh,Hh,Vh,Gh,qh=sc(),Xh={point:Nc,lineStart:function(){Xh.point=Zh},lineEnd:function(){$h&&Jh(Wh,Hh),Xh.point=Nc},polygonStart:function(){$h=!0},polygonEnd:function(){$h=null},result:function(){var t=+qh;return qh.reset(),t}};function Zh(t,e){Xh.point=Jh,Wh=Vh=t,Hh=Gh=e}function Jh(t,e){Vh-=t,Gh-=e,qh.add(Ac(Vh*Vh+Gh*Gh)),Vh=t,Gh=e}var Kh=Xh;function Qh(){this._string=[]}function tf(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}Qh.prototype={_radius:4.5,_circle:tf(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._string.push("M",t,",",e),this._point=1;break;case 1:this._string.push("L",t,",",e);break;default:null==this._circle&&(this._circle=tf(this._radius)),this._string.push("M",t,",",e,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}};var ef=function(t,e){var n,r,i=4.5;function a(t){return t&&("function"==typeof i&&r.pointRadius(+i.apply(this,arguments)),$c(t,n(r))),r.result()}return a.area=function(t){return $c(t,n(fh)),fh.result()},a.measure=function(t){return $c(t,n(Kh)),Kh.result()},a.bounds=function(t){return $c(t,n(_h)),_h.result()},a.centroid=function(t){return $c(t,n(zh)),zh.result()},a.projection=function(e){return arguments.length?(n=null==e?(t=null,ih):(t=e).stream,a):t},a.context=function(t){return arguments.length?(r=null==t?(e=null,new Qh):new Uh(e=t),"function"!=typeof i&&r.pointRadius(i),a):e},a.pointRadius=function(t){return arguments.length?(i="function"==typeof t?t:(r.pointRadius(+t),+t),a):i},a.projection(t).context(e)},nf=function(t){return{stream:rf(t)}};function rf(t){return function(e){var n=new af;for(var r in t)n[r]=t[r];return n.stream=e,n}}function af(){}function of(t,e,n){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),$c(n,t.stream(_h)),e(_h.result()),null!=r&&t.clipExtent(r),t}function sf(t,e,n){return of(t,(function(n){var r=e[1][0]-e[0][0],i=e[1][1]-e[0][1],a=Math.min(r/(n[1][0]-n[0][0]),i/(n[1][1]-n[0][1])),o=+e[0][0]+(r-a*(n[1][0]+n[0][0]))/2,s=+e[0][1]+(i-a*(n[1][1]+n[0][1]))/2;t.scale(150*a).translate([o,s])}),n)}function cf(t,e,n){return sf(t,[[0,0],e],n)}function uf(t,e,n){return of(t,(function(n){var r=+e,i=r/(n[1][0]-n[0][0]),a=(r-i*(n[1][0]+n[0][0]))/2,o=-i*n[0][1];t.scale(150*i).translate([a,o])}),n)}function lf(t,e,n){return of(t,(function(n){var r=+e,i=r/(n[1][1]-n[0][1]),a=-i*n[0][0],o=(r-i*(n[1][1]+n[0][1]))/2;t.scale(150*i).translate([a,o])}),n)}af.prototype={constructor:af,point:function(t,e){this.stream.point(t,e)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var hf=xc(30*yc),ff=function(t,e){return+e?function(t,e){function n(r,i,a,o,s,c,u,l,h,f,d,p,g,y){var v=u-r,m=l-i,b=v*v+m*m;if(b>4*e&&g--){var x=o+f,_=s+d,k=c+p,w=Ac(x*x+_*_+k*k),E=Oc(k/=w),T=vc(vc(k)-1)<1e-6||vc(a-h)<1e-6?(a+h)/2:bc(_,x),C=t(T,E),A=C[0],S=C[1],M=A-r,O=S-i,D=m*M-v*O;(D*D/b>e||vc((v*M+m*O)/b-.5)>.3||o*f+s*d+c*p<hf)&&(n(r,i,a,o,s,c,A,S,T,x/=w,_/=w,k,g,y),y.point(A,S),n(A,S,T,x,_,k,u,l,h,f,d,p,g,y))}}return function(e){var r,i,a,o,s,c,u,l,h,f,d,p,g={point:y,lineStart:v,lineEnd:b,polygonStart:function(){e.polygonStart(),g.lineStart=x},polygonEnd:function(){e.polygonEnd(),g.lineStart=v}};function y(n,r){n=t(n,r),e.point(n[0],n[1])}function v(){l=NaN,g.point=m,e.lineStart()}function m(r,i){var a=Qc([r,i]),o=t(r,i);n(l,h,u,f,d,p,l=o[0],h=o[1],u=r,f=a[0],d=a[1],p=a[2],16,e),e.point(l,h)}function b(){g.point=y,e.lineEnd()}function x(){v(),g.point=_,g.lineEnd=k}function _(t,e){m(r=t,e),i=l,a=h,o=f,s=d,c=p,g.point=m}function k(){n(l,h,u,f,d,p,i,a,r,o,s,c,16,e),g.lineEnd=b,b()}return g}}(t,e):function(t){return rf({point:function(e,n){e=t(e,n),this.stream.point(e[0],e[1])}})}(t)};var df=rf({point:function(t,e){this.stream.point(t*yc,e*yc)}});function pf(t,e,n){function r(r,i){return[e+t*r,n-t*i]}return r.invert=function(r,i){return[(r-e)/t,(n-i)/t]},r}function gf(t,e,n,r){var i=xc(r),a=Tc(r),o=i*t,s=a*t,c=i/t,u=a/t,l=(a*n-i*e)/t,h=(a*e+i*n)/t;function f(t,r){return[o*t-s*r+e,n-s*t-o*r]}return f.invert=function(t,e){return[c*t-u*e+l,h-u*t-c*e]},f}function yf(t){return vf((function(){return t}))()}function vf(t){var e,n,r,i,a,o,s,c,u,l,h=150,f=480,d=250,p=0,g=0,y=0,v=0,m=0,b=0,x=null,_=El,k=null,w=ih,E=.5;function T(t){return c(t[0]*yc,t[1]*yc)}function C(t){return(t=c.invert(t[0],t[1]))&&[t[0]*gc,t[1]*gc]}function A(){var t=gf(h,0,0,b).apply(null,e(p,g)),r=(b?gf:pf)(h,f-t[0],d-t[1],b);return n=al(y,v,m),s=rl(e,r),c=rl(n,s),o=ff(s,E),S()}function S(){return u=l=null,T}return T.stream=function(t){return u&&l===t?u:u=df(function(t){return rf({point:function(e,n){var r=t(e,n);return this.stream.point(r[0],r[1])}})}(n)(_(o(w(l=t)))))},T.preclip=function(t){return arguments.length?(_=t,x=void 0,S()):_},T.postclip=function(t){return arguments.length?(w=t,k=r=i=a=null,S()):w},T.clipAngle=function(t){return arguments.length?(_=+t?Tl(x=t*yc):(x=null,El),S()):x*gc},T.clipExtent=function(t){return arguments.length?(w=null==t?(k=r=i=a=null,ih):Cl(k=+t[0][0],r=+t[0][1],i=+t[1][0],a=+t[1][1]),S()):null==k?null:[[k,r],[i,a]]},T.scale=function(t){return arguments.length?(h=+t,A()):h},T.translate=function(t){return arguments.length?(f=+t[0],d=+t[1],A()):[f,d]},T.center=function(t){return arguments.length?(p=t[0]%360*yc,g=t[1]%360*yc,A()):[p*gc,g*gc]},T.rotate=function(t){return arguments.length?(y=t[0]%360*yc,v=t[1]%360*yc,m=t.length>2?t[2]%360*yc:0,A()):[y*gc,v*gc,m*gc]},T.angle=function(t){return arguments.length?(b=t%360*yc,A()):b*gc},T.precision=function(t){return arguments.length?(o=ff(s,E=t*t),S()):Ac(E)},T.fitExtent=function(t,e){return sf(T,t,e)},T.fitSize=function(t,e){return cf(T,t,e)},T.fitWidth=function(t,e){return uf(T,t,e)},T.fitHeight=function(t,e){return lf(T,t,e)},function(){return e=t.apply(this,arguments),T.invert=e.invert&&C,A()}}function mf(t){var e=0,n=hc/3,r=vf(t),i=r(e,n);return i.parallels=function(t){return arguments.length?r(e=t[0]*yc,n=t[1]*yc):[e*gc,n*gc]},i}function bf(t,e){var n=Tc(t),r=(n+Tc(e))/2;if(vc(r)<1e-6)return function(t){var e=xc(t);function n(t,n){return[t*e,Tc(n)/e]}return n.invert=function(t,n){return[t/e,Oc(n*e)]},n}(t);var i=1+n*(2*r-n),a=Ac(i)/r;function o(t,e){var n=Ac(i-2*r*Tc(e))/r;return[n*Tc(t*=r),a-n*xc(t)]}return o.invert=function(t,e){var n=a-e;return[bc(t,vc(n))/r*Cc(n),Oc((i-(t*t+n*n)*r*r)/(2*r))]},o}var xf=function(){return mf(bf).scale(155.424).center([0,33.6442])},_f=function(){return xf().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])};var kf=function(){var t,e,n,r,i,a,o=_f(),s=xf().rotate([154,0]).center([-2,58.5]).parallels([55,65]),c=xf().rotate([157,0]).center([-3,19.9]).parallels([8,18]),u={point:function(t,e){a=[t,e]}};function l(t){var e=t[0],o=t[1];return a=null,n.point(e,o),a||(r.point(e,o),a)||(i.point(e,o),a)}function h(){return t=e=null,l}return l.invert=function(t){var e=o.scale(),n=o.translate(),r=(t[0]-n[0])/e,i=(t[1]-n[1])/e;return(i>=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:o).invert(t)},l.stream=function(n){return t&&e===n?t:(r=[o.stream(e=n),s.stream(n),c.stream(n)],i=r.length,t={point:function(t,e){for(var n=-1;++n<i;)r[n].point(t,e)},sphere:function(){for(var t=-1;++t<i;)r[t].sphere()},lineStart:function(){for(var t=-1;++t<i;)r[t].lineStart()},lineEnd:function(){for(var t=-1;++t<i;)r[t].lineEnd()},polygonStart:function(){for(var t=-1;++t<i;)r[t].polygonStart()},polygonEnd:function(){for(var t=-1;++t<i;)r[t].polygonEnd()}});var r,i},l.precision=function(t){return arguments.length?(o.precision(t),s.precision(t),c.precision(t),h()):o.precision()},l.scale=function(t){return arguments.length?(o.scale(t),s.scale(.35*t),c.scale(t),l.translate(o.translate())):o.scale()},l.translate=function(t){if(!arguments.length)return o.translate();var e=o.scale(),a=+t[0],l=+t[1];return n=o.translate(t).clipExtent([[a-.455*e,l-.238*e],[a+.455*e,l+.238*e]]).stream(u),r=s.translate([a-.307*e,l+.201*e]).clipExtent([[a-.425*e+1e-6,l+.12*e+1e-6],[a-.214*e-1e-6,l+.234*e-1e-6]]).stream(u),i=c.translate([a-.205*e,l+.212*e]).clipExtent([[a-.214*e+1e-6,l+.166*e+1e-6],[a-.115*e-1e-6,l+.234*e-1e-6]]).stream(u),h()},l.fitExtent=function(t,e){return sf(l,t,e)},l.fitSize=function(t,e){return cf(l,t,e)},l.fitWidth=function(t,e){return uf(l,t,e)},l.fitHeight=function(t,e){return lf(l,t,e)},l.scale(1070)};function wf(t){return function(e,n){var r=xc(e),i=xc(n),a=t(r*i);return[a*i*Tc(e),a*Tc(n)]}}function Ef(t){return function(e,n){var r=Ac(e*e+n*n),i=t(r),a=Tc(i),o=xc(i);return[bc(e*a,r*o),Oc(r&&n*a/r)]}}var Tf=wf((function(t){return Ac(2/(1+t))}));Tf.invert=Ef((function(t){return 2*Oc(t/2)}));var Cf=function(){return yf(Tf).scale(124.75).clipAngle(179.999)},Af=wf((function(t){return(t=Mc(t))&&t/Tc(t)}));Af.invert=Ef((function(t){return t}));var Sf=function(){return yf(Af).scale(79.4188).clipAngle(179.999)};function Mf(t,e){return[t,wc(Sc((fc+e)/2))]}Mf.invert=function(t,e){return[t,2*mc(kc(e))-fc]};var Of=function(){return Df(Mf).scale(961/pc)};function Df(t){var e,n,r,i=yf(t),a=i.center,o=i.scale,s=i.translate,c=i.clipExtent,u=null;function l(){var a=hc*o(),s=i(ul(i.rotate()).invert([0,0]));return c(null==u?[[s[0]-a,s[1]-a],[s[0]+a,s[1]+a]]:t===Mf?[[Math.max(s[0]-a,u),e],[Math.min(s[0]+a,n),r]]:[[u,Math.max(s[1]-a,e)],[n,Math.min(s[1]+a,r)]])}return i.scale=function(t){return arguments.length?(o(t),l()):o()},i.translate=function(t){return arguments.length?(s(t),l()):s()},i.center=function(t){return arguments.length?(a(t),l()):a()},i.clipExtent=function(t){return arguments.length?(null==t?u=e=n=r=null:(u=+t[0][0],e=+t[0][1],n=+t[1][0],r=+t[1][1]),l()):null==u?null:[[u,e],[n,r]]},l()}function Nf(t){return Sc((fc+t)/2)}function Bf(t,e){var n=xc(t),r=t===e?Tc(t):wc(n/xc(e))/wc(Nf(e)/Nf(t)),i=n*Ec(Nf(t),r)/r;if(!r)return Mf;function a(t,e){i>0?e<1e-6-fc&&(e=1e-6-fc):e>fc-1e-6&&(e=fc-1e-6);var n=i/Ec(Nf(e),r);return[n*Tc(r*t),i-n*xc(r*t)]}return a.invert=function(t,e){var n=i-e,a=Cc(r)*Ac(t*t+n*n);return[bc(t,vc(n))/r*Cc(n),2*mc(Ec(i/a,1/r))-fc]},a}var Lf=function(){return mf(Bf).scale(109.5).parallels([30,30])};function Pf(t,e){return[t,e]}Pf.invert=Pf;var If=function(){return yf(Pf).scale(152.63)};function Ff(t,e){var n=xc(t),r=t===e?Tc(t):(n-xc(e))/(e-t),i=n/r+t;if(vc(r)<1e-6)return Pf;function a(t,e){var n=i-e,a=r*t;return[n*Tc(a),i-n*xc(a)]}return a.invert=function(t,e){var n=i-e;return[bc(t,vc(n))/r*Cc(n),i-Cc(r)*Ac(t*t+n*n)]},a}var jf=function(){return mf(Ff).scale(131.154).center([0,13.9389])},Rf=1.340264,Yf=-.081106,zf=893e-6,Uf=.003796,$f=Ac(3)/2;function Wf(t,e){var n=Oc($f*Tc(e)),r=n*n,i=r*r*r;return[t*xc(n)/($f*(Rf+3*Yf*r+i*(7*zf+9*Uf*r))),n*(Rf+Yf*r+i*(zf+Uf*r))]}Wf.invert=function(t,e){for(var n,r=e,i=r*r,a=i*i*i,o=0;o<12&&(a=(i=(r-=n=(r*(Rf+Yf*i+a*(zf+Uf*i))-e)/(Rf+3*Yf*i+a*(7*zf+9*Uf*i)))*r)*i*i,!(vc(n)<1e-12));++o);return[$f*t*(Rf+3*Yf*i+a*(7*zf+9*Uf*i))/xc(r),Oc(Tc(r)/$f)]};var Hf=function(){return yf(Wf).scale(177.158)};function Vf(t,e){var n=xc(e),r=xc(t)*n;return[n*Tc(t)/r,Tc(e)/r]}Vf.invert=Ef(mc);var Gf=function(){return yf(Vf).scale(144.049).clipAngle(60)};function qf(t,e,n,r){return 1===t&&1===e&&0===n&&0===r?ih:rf({point:function(i,a){this.stream.point(i*t+n,a*e+r)}})}var Xf=function(){var t,e,n,r,i,a,o=1,s=0,c=0,u=1,l=1,h=ih,f=null,d=ih;function p(){return r=i=null,a}return a={stream:function(t){return r&&i===t?r:r=h(d(i=t))},postclip:function(r){return arguments.length?(d=r,f=t=e=n=null,p()):d},clipExtent:function(r){return arguments.length?(d=null==r?(f=t=e=n=null,ih):Cl(f=+r[0][0],t=+r[0][1],e=+r[1][0],n=+r[1][1]),p()):null==f?null:[[f,t],[e,n]]},scale:function(t){return arguments.length?(h=qf((o=+t)*u,o*l,s,c),p()):o},translate:function(t){return arguments.length?(h=qf(o*u,o*l,s=+t[0],c=+t[1]),p()):[s,c]},reflectX:function(t){return arguments.length?(h=qf(o*(u=t?-1:1),o*l,s,c),p()):u<0},reflectY:function(t){return arguments.length?(h=qf(o*u,o*(l=t?-1:1),s,c),p()):l<0},fitExtent:function(t,e){return sf(a,t,e)},fitSize:function(t,e){return cf(a,t,e)},fitWidth:function(t,e){return uf(a,t,e)},fitHeight:function(t,e){return lf(a,t,e)}}};function Zf(t,e){var n=e*e,r=n*n;return[t*(.8707-.131979*n+r*(r*(.003971*n-.001529*r)-.013791)),e*(1.007226+n*(.015085+r*(.028874*n-.044475-.005916*r)))]}Zf.invert=function(t,e){var n,r=e,i=25;do{var a=r*r,o=a*a;r-=n=(r*(1.007226+a*(.015085+o*(.028874*a-.044475-.005916*o)))-e)/(1.007226+a*(.045255+o*(.259866*a-.311325-.005916*11*o)))}while(vc(n)>1e-6&&--i>0);return[t/(.8707+(a=r*r)*(a*(a*a*a*(.003971-.001529*a)-.013791)-.131979)),r]};var Jf=function(){return yf(Zf).scale(175.295)};function Kf(t,e){return[xc(e)*Tc(t),Tc(e)]}Kf.invert=Ef(Oc);var Qf=function(){return yf(Kf).scale(249.5).clipAngle(90+1e-6)};function td(t,e){var n=xc(e),r=1+xc(t)*n;return[n*Tc(t)/r,Tc(e)/r]}td.invert=Ef((function(t){return 2*mc(t)}));var ed=function(){return yf(td).scale(250).clipAngle(142)};function nd(t,e){return[wc(Sc((fc+e)/2)),-t]}nd.invert=function(t,e){return[-e,2*mc(kc(t))-fc]};var rd=function(){var t=Df(nd),e=t.center,n=t.rotate;return t.center=function(t){return arguments.length?e([-t[1],t[0]]):[(t=e())[1],-t[0]]},t.rotate=function(t){return arguments.length?n([t[0],t[1],t.length>2?t[2]+90:90]):[(t=n())[0],t[1],t[2]-90]},n([0,0,90]).scale(159.155)};function id(t,e){return t.parent===e.parent?1:2}function ad(t,e){return t+e.x}function od(t,e){return Math.max(t,e.y)}var sd=function(){var t=id,e=1,n=1,r=!1;function i(i){var a,o=0;i.eachAfter((function(e){var n=e.children;n?(e.x=function(t){return t.reduce(ad,0)/t.length}(n),e.y=function(t){return 1+t.reduce(od,0)}(n)):(e.x=a?o+=t(e,a):0,e.y=0,a=e)}));var s=function(t){for(var e;e=t.children;)t=e[0];return t}(i),c=function(t){for(var e;e=t.children;)t=e[e.length-1];return t}(i),u=s.x-t(s,c)/2,l=c.x+t(c,s)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*e,t.y=(i.y-t.y)*n}:function(t){t.x=(t.x-u)/(l-u)*e,t.y=(1-(i.y?t.y/i.y:1))*n})}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i};function cd(t){var e=0,n=t.children,r=n&&n.length;if(r)for(;--r>=0;)e+=n[r].value;else e=1;t.value=e}function ud(t,e){var n,r,i,a,o,s=new dd(t),c=+t.value&&(s.value=t.value),u=[s];for(null==e&&(e=ld);n=u.pop();)if(c&&(n.value=+n.data.value),(i=e(n.data))&&(o=i.length))for(n.children=new Array(o),a=o-1;a>=0;--a)u.push(r=n.children[a]=new dd(i[a])),r.parent=n,r.depth=n.depth+1;return s.eachBefore(fd)}function ld(t){return t.children}function hd(t){t.data=t.data.data}function fd(t){var e=0;do{t.height=e}while((t=t.parent)&&t.height<++e)}function dd(t){this.data=t,this.depth=this.height=0,this.parent=null}dd.prototype=ud.prototype={constructor:dd,count:function(){return this.eachAfter(cd)},each:function(t){var e,n,r,i,a=this,o=[a];do{for(e=o.reverse(),o=[];a=e.pop();)if(t(a),n=a.children)for(r=0,i=n.length;r<i;++r)o.push(n[r])}while(o.length);return this},eachAfter:function(t){for(var e,n,r,i=this,a=[i],o=[];i=a.pop();)if(o.push(i),e=i.children)for(n=0,r=e.length;n<r;++n)a.push(e[n]);for(;i=o.pop();)t(i);return this},eachBefore:function(t){for(var e,n,r=this,i=[r];r=i.pop();)if(t(r),e=r.children)for(n=e.length-1;n>=0;--n)i.push(e[n]);return this},sum:function(t){return this.eachAfter((function(e){for(var n=+t(e.data)||0,r=e.children,i=r&&r.length;--i>=0;)n+=r[i].value;e.value=n}))},sort:function(t){return this.eachBefore((function(e){e.children&&e.children.sort(t)}))},path:function(t){for(var e=this,n=function(t,e){if(t===e)return t;var n=t.ancestors(),r=e.ancestors(),i=null;t=n.pop(),e=r.pop();for(;t===e;)i=t,t=n.pop(),e=r.pop();return i}(e,t),r=[e];e!==n;)e=e.parent,r.push(e);for(var i=r.length;t!==n;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e},descendants:function(){var t=[];return this.each((function(e){t.push(e)})),t},leaves:function(){var t=[];return this.eachBefore((function(e){e.children||t.push(e)})),t},links:function(){var t=this,e=[];return t.each((function(n){n!==t&&e.push({source:n.parent,target:n})})),e},copy:function(){return ud(this).eachBefore(hd)}};var pd=Array.prototype.slice;var gd=function(t){for(var e,n,r=0,i=(t=function(t){for(var e,n,r=t.length;r;)n=Math.random()*r--|0,e=t[r],t[r]=t[n],t[n]=e;return t}(pd.call(t))).length,a=[];r<i;)e=t[r],n&&md(n,e)?++r:(n=xd(a=yd(a,e)),r=0);return n};function yd(t,e){var n,r;if(bd(e,t))return[e];for(n=0;n<t.length;++n)if(vd(e,t[n])&&bd(_d(t[n],e),t))return[t[n],e];for(n=0;n<t.length-1;++n)for(r=n+1;r<t.length;++r)if(vd(_d(t[n],t[r]),e)&&vd(_d(t[n],e),t[r])&&vd(_d(t[r],e),t[n])&&bd(kd(t[n],t[r],e),t))return[t[n],t[r],e];throw new Error}function vd(t,e){var n=t.r-e.r,r=e.x-t.x,i=e.y-t.y;return n<0||n*n<r*r+i*i}function md(t,e){var n=t.r-e.r+1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function bd(t,e){for(var n=0;n<e.length;++n)if(!md(t,e[n]))return!1;return!0}function xd(t){switch(t.length){case 1:return{x:(e=t[0]).x,y:e.y,r:e.r};case 2:return _d(t[0],t[1]);case 3:return kd(t[0],t[1],t[2])}var e}function _d(t,e){var n=t.x,r=t.y,i=t.r,a=e.x,o=e.y,s=e.r,c=a-n,u=o-r,l=s-i,h=Math.sqrt(c*c+u*u);return{x:(n+a+c/h*l)/2,y:(r+o+u/h*l)/2,r:(h+i+s)/2}}function kd(t,e,n){var r=t.x,i=t.y,a=t.r,o=e.x,s=e.y,c=e.r,u=n.x,l=n.y,h=n.r,f=r-o,d=r-u,p=i-s,g=i-l,y=c-a,v=h-a,m=r*r+i*i-a*a,b=m-o*o-s*s+c*c,x=m-u*u-l*l+h*h,_=d*p-f*g,k=(p*x-g*b)/(2*_)-r,w=(g*y-p*v)/_,E=(d*b-f*x)/(2*_)-i,T=(f*v-d*y)/_,C=w*w+T*T-1,A=2*(a+k*w+E*T),S=k*k+E*E-a*a,M=-(C?(A+Math.sqrt(A*A-4*C*S))/(2*C):S/A);return{x:r+k+w*M,y:i+E+T*M,r:M}}function wd(t,e,n){var r,i,a,o,s=t.x-e.x,c=t.y-e.y,u=s*s+c*c;u?(i=e.r+n.r,i*=i,o=t.r+n.r,i>(o*=o)?(r=(u+o-i)/(2*u),a=Math.sqrt(Math.max(0,o/u-r*r)),n.x=t.x-r*s-a*c,n.y=t.y-r*c+a*s):(r=(u+i-o)/(2*u),a=Math.sqrt(Math.max(0,i/u-r*r)),n.x=e.x+r*s-a*c,n.y=e.y+r*c+a*s)):(n.x=e.x+n.r,n.y=e.y)}function Ed(t,e){var n=t.r+e.r-1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function Td(t){var e=t._,n=t.next._,r=e.r+n.r,i=(e.x*n.r+n.x*e.r)/r,a=(e.y*n.r+n.y*e.r)/r;return i*i+a*a}function Cd(t){this._=t,this.next=null,this.previous=null}function Ad(t){if(!(i=t.length))return 0;var e,n,r,i,a,o,s,c,u,l,h;if((e=t[0]).x=0,e.y=0,!(i>1))return e.r;if(n=t[1],e.x=-n.r,n.x=e.r,n.y=0,!(i>2))return e.r+n.r;wd(n,e,r=t[2]),e=new Cd(e),n=new Cd(n),r=new Cd(r),e.next=r.previous=n,n.next=e.previous=r,r.next=n.previous=e;t:for(s=3;s<i;++s){wd(e._,n._,r=t[s]),r=new Cd(r),c=n.next,u=e.previous,l=n._.r,h=e._.r;do{if(l<=h){if(Ed(c._,r._)){n=c,e.next=n,n.previous=e,--s;continue t}l+=c._.r,c=c.next}else{if(Ed(u._,r._)){(e=u).next=n,n.previous=e,--s;continue t}h+=u._.r,u=u.previous}}while(c!==u.next);for(r.previous=e,r.next=n,e.next=n.previous=n=r,a=Td(e);(r=r.next)!==n;)(o=Td(r))<a&&(e=r,a=o);n=e.next}for(e=[n._],r=n;(r=r.next)!==n;)e.push(r._);for(r=gd(e),s=0;s<i;++s)(e=t[s]).x-=r.x,e.y-=r.y;return r.r}var Sd=function(t){return Ad(t),t};function Md(t){return null==t?null:Od(t)}function Od(t){if("function"!=typeof t)throw new Error;return t}function Dd(){return 0}var Nd=function(t){return function(){return t}};function Bd(t){return Math.sqrt(t.value)}var Ld=function(){var t=null,e=1,n=1,r=Dd;function i(i){return i.x=e/2,i.y=n/2,t?i.eachBefore(Pd(t)).eachAfter(Id(r,.5)).eachBefore(Fd(1)):i.eachBefore(Pd(Bd)).eachAfter(Id(Dd,1)).eachAfter(Id(r,i.r/Math.min(e,n))).eachBefore(Fd(Math.min(e,n)/(2*i.r))),i}return i.radius=function(e){return arguments.length?(t=Md(e),i):t},i.size=function(t){return arguments.length?(e=+t[0],n=+t[1],i):[e,n]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:Nd(+t),i):r},i};function Pd(t){return function(e){e.children||(e.r=Math.max(0,+t(e)||0))}}function Id(t,e){return function(n){if(r=n.children){var r,i,a,o=r.length,s=t(n)*e||0;if(s)for(i=0;i<o;++i)r[i].r+=s;if(a=Ad(r),s)for(i=0;i<o;++i)r[i].r-=s;n.r=a+s}}}function Fd(t){return function(e){var n=e.parent;e.r*=t,n&&(e.x=n.x+t*e.x,e.y=n.y+t*e.y)}}var jd=function(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)},Rd=function(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(r-e)/t.value;++s<c;)(a=o[s]).y0=n,a.y1=i,a.x0=e,a.x1=e+=a.value*u},Yd=function(){var t=1,e=1,n=0,r=!1;function i(i){var a=i.height+1;return i.x0=i.y0=n,i.x1=t,i.y1=e/a,i.eachBefore(function(t,e){return function(r){r.children&&Rd(r,r.x0,t*(r.depth+1)/e,r.x1,t*(r.depth+2)/e);var i=r.x0,a=r.y0,o=r.x1-n,s=r.y1-n;o<i&&(i=o=(i+o)/2),s<a&&(a=s=(a+s)/2),r.x0=i,r.y0=a,r.x1=o,r.y1=s}}(e,a)),r&&i.eachBefore(jd),i}return i.round=function(t){return arguments.length?(r=!!t,i):r},i.size=function(n){return arguments.length?(t=+n[0],e=+n[1],i):[t,e]},i.padding=function(t){return arguments.length?(n=+t,i):n},i},zd={depth:-1},Ud={};function $d(t){return t.id}function Wd(t){return t.parentId}var Hd=function(){var t=$d,e=Wd;function n(n){var r,i,a,o,s,c,u,l=n.length,h=new Array(l),f={};for(i=0;i<l;++i)r=n[i],s=h[i]=new dd(r),null!=(c=t(r,i,n))&&(c+="")&&(f[u="$"+(s.id=c)]=u in f?Ud:s);for(i=0;i<l;++i)if(s=h[i],null!=(c=e(n[i],i,n))&&(c+="")){if(!(o=f["$"+c]))throw new Error("missing: "+c);if(o===Ud)throw new Error("ambiguous: "+c);o.children?o.children.push(s):o.children=[s],s.parent=o}else{if(a)throw new Error("multiple roots");a=s}if(!a)throw new Error("no root");if(a.parent=zd,a.eachBefore((function(t){t.depth=t.parent.depth+1,--l})).eachBefore(fd),a.parent=null,l>0)throw new Error("cycle");return a}return n.id=function(e){return arguments.length?(t=Od(e),n):t},n.parentId=function(t){return arguments.length?(e=Od(t),n):e},n};function Vd(t,e){return t.parent===e.parent?1:2}function Gd(t){var e=t.children;return e?e[0]:t.t}function qd(t){var e=t.children;return e?e[e.length-1]:t.t}function Xd(t,e,n){var r=n/(e.i-t.i);e.c-=r,e.s+=n,t.c+=r,e.z+=n,e.m+=n}function Zd(t,e,n){return t.a.parent===e.parent?t.a:n}function Jd(t,e){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=e}Jd.prototype=Object.create(dd.prototype);var Kd=function(){var t=Vd,e=1,n=1,r=null;function i(i){var c=function(t){for(var e,n,r,i,a,o=new Jd(t,0),s=[o];e=s.pop();)if(r=e._.children)for(e.children=new Array(a=r.length),i=a-1;i>=0;--i)s.push(n=e.children[i]=new Jd(r[i],i)),n.parent=e;return(o.parent=new Jd(null,0)).children=[o],o}(i);if(c.eachAfter(a),c.parent.m=-c.z,c.eachBefore(o),r)i.eachBefore(s);else{var u=i,l=i,h=i;i.eachBefore((function(t){t.x<u.x&&(u=t),t.x>l.x&&(l=t),t.depth>h.depth&&(h=t)}));var f=u===l?1:t(u,l)/2,d=f-u.x,p=e/(l.x+f+d),g=n/(h.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function a(e){var n=e.children,r=e.parent.children,i=e.i?r[e.i-1]:null;if(n){!function(t){for(var e,n=0,r=0,i=t.children,a=i.length;--a>=0;)(e=i[a]).z+=n,e.m+=n,n+=e.s+(r+=e.c)}(e);var a=(n[0].z+n[n.length-1].z)/2;i?(e.z=i.z+t(e._,i._),e.m=e.z-a):e.z=a}else i&&(e.z=i.z+t(e._,i._));e.parent.A=function(e,n,r){if(n){for(var i,a=e,o=e,s=n,c=a.parent.children[0],u=a.m,l=o.m,h=s.m,f=c.m;s=qd(s),a=Gd(a),s&&a;)c=Gd(c),(o=qd(o)).a=e,(i=s.z+h-a.z-u+t(s._,a._))>0&&(Xd(Zd(s,e,r),e,i),u+=i,l+=i),h+=s.m,u+=a.m,f+=c.m,l+=o.m;s&&!qd(o)&&(o.t=s,o.m+=h-l),a&&!Gd(c)&&(c.t=a,c.m+=u-f,r=e)}return r}(e,i,e.parent.A||r[0])}function o(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function s(t){t.x*=e,t.y=t.depth*n}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i},Qd=function(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(i-n)/t.value;++s<c;)(a=o[s]).x0=e,a.x1=r,a.y0=n,a.y1=n+=a.value*u},tp=(1+Math.sqrt(5))/2;function ep(t,e,n,r,i,a){for(var o,s,c,u,l,h,f,d,p,g,y,v=[],m=e.children,b=0,x=0,_=m.length,k=e.value;b<_;){c=i-n,u=a-r;do{l=m[x++].value}while(!l&&x<_);for(h=f=l,y=l*l*(g=Math.max(u/c,c/u)/(k*t)),p=Math.max(f/y,y/h);x<_;++x){if(l+=s=m[x].value,s<h&&(h=s),s>f&&(f=s),y=l*l*g,(d=Math.max(f/y,y/h))>p){l-=s;break}p=d}v.push(o={value:l,dice:c<u,children:m.slice(b,x)}),o.dice?Rd(o,n,r,i,k?r+=u*l/k:a):Qd(o,n,r,k?n+=c*l/k:i,a),k-=l,b=x}return v}var np=function t(e){function n(t,n,r,i,a){ep(e,t,n,r,i,a)}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(tp),rp=function(){var t=np,e=!1,n=1,r=1,i=[0],a=Dd,o=Dd,s=Dd,c=Dd,u=Dd;function l(t){return t.x0=t.y0=0,t.x1=n,t.y1=r,t.eachBefore(h),i=[0],e&&t.eachBefore(jd),t}function h(e){var n=i[e.depth],r=e.x0+n,l=e.y0+n,h=e.x1-n,f=e.y1-n;h<r&&(r=h=(r+h)/2),f<l&&(l=f=(l+f)/2),e.x0=r,e.y0=l,e.x1=h,e.y1=f,e.children&&(n=i[e.depth+1]=a(e)/2,r+=u(e)-n,l+=o(e)-n,(h-=s(e)-n)<r&&(r=h=(r+h)/2),(f-=c(e)-n)<l&&(l=f=(l+f)/2),t(e,r,l,h,f))}return l.round=function(t){return arguments.length?(e=!!t,l):e},l.size=function(t){return arguments.length?(n=+t[0],r=+t[1],l):[n,r]},l.tile=function(e){return arguments.length?(t=Od(e),l):t},l.padding=function(t){return arguments.length?l.paddingInner(t).paddingOuter(t):l.paddingInner()},l.paddingInner=function(t){return arguments.length?(a="function"==typeof t?t:Nd(+t),l):a},l.paddingOuter=function(t){return arguments.length?l.paddingTop(t).paddingRight(t).paddingBottom(t).paddingLeft(t):l.paddingTop()},l.paddingTop=function(t){return arguments.length?(o="function"==typeof t?t:Nd(+t),l):o},l.paddingRight=function(t){return arguments.length?(s="function"==typeof t?t:Nd(+t),l):s},l.paddingBottom=function(t){return arguments.length?(c="function"==typeof t?t:Nd(+t),l):c},l.paddingLeft=function(t){return arguments.length?(u="function"==typeof t?t:Nd(+t),l):u},l},ip=function(t,e,n,r,i){var a,o,s=t.children,c=s.length,u=new Array(c+1);for(u[0]=o=a=0;a<c;++a)u[a+1]=o+=s[a].value;!function t(e,n,r,i,a,o,c){if(e>=n-1){var l=s[e];return l.x0=i,l.y0=a,l.x1=o,void(l.y1=c)}var h=u[e],f=r/2+h,d=e+1,p=n-1;for(;d<p;){var g=d+p>>>1;u[g]<f?d=g+1:p=g}f-u[d-1]<u[d]-f&&e+1<d&&--d;var y=u[d]-h,v=r-y;if(o-i>c-a){var m=(i*v+o*y)/r;t(e,d,y,i,a,m,c),t(d,n,v,m,a,o,c)}else{var b=(a*v+c*y)/r;t(e,d,y,i,a,o,b),t(d,n,v,i,b,o,c)}}(0,c,t.value,e,n,r,i)},ap=function(t,e,n,r,i){(1&t.depth?Qd:Rd)(t,e,n,r,i)},op=function t(e){function n(t,n,r,i,a){if((o=t._squarify)&&o.ratio===e)for(var o,s,c,u,l,h=-1,f=o.length,d=t.value;++h<f;){for(c=(s=o[h]).children,u=s.value=0,l=c.length;u<l;++u)s.value+=c[u].value;s.dice?Rd(s,n,r,i,r+=(a-r)*s.value/d):Qd(s,n,r,n+=(i-n)*s.value/d,a),d-=s.value}else t._squarify=o=ep(e,t,n,r,i,a),o.ratio=e}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(tp),sp=function(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}},cp=function(t,e){var n=un(+t,+e);return function(t){var e=n(t);return e-360*Math.floor(e/360)}},up=function(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}},lp=Math.SQRT2;function hp(t){return((t=Math.exp(t))+1/t)/2}var fp=function(t,e){var n,r,i=t[0],a=t[1],o=t[2],s=e[0],c=e[1],u=e[2],l=s-i,h=c-a,f=l*l+h*h;if(f<1e-12)r=Math.log(u/o)/lp,n=function(t){return[i+t*l,a+t*h,o*Math.exp(lp*t*r)]};else{var d=Math.sqrt(f),p=(u*u-o*o+4*f)/(2*o*2*d),g=(u*u-o*o-4*f)/(2*u*2*d),y=Math.log(Math.sqrt(p*p+1)-p),v=Math.log(Math.sqrt(g*g+1)-g);r=(v-y)/lp,n=function(t){var e,n=t*r,s=hp(y),c=o/(2*d)*(s*(e=lp*n+y,((e=Math.exp(2*e))-1)/(e+1))-function(t){return((t=Math.exp(t))-1/t)/2}(y));return[i+c*l,a+c*h,o*s/hp(lp*n+y)]}}return n.duration=1e3*r,n};function dp(t){return function(e,n){var r=t((e=tn(e)).h,(n=tn(n)).h),i=hn(e.s,n.s),a=hn(e.l,n.l),o=hn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.s=i(t),e.l=a(t),e.opacity=o(t),e+""}}}var pp=dp(un),gp=dp(hn);function yp(t,e){var n=hn((t=pa(t)).l,(e=pa(e)).l),r=hn(t.a,e.a),i=hn(t.b,e.b),a=hn(t.opacity,e.opacity);return function(e){return t.l=n(e),t.a=r(e),t.b=i(e),t.opacity=a(e),t+""}}function vp(t){return function(e,n){var r=t((e=ka(e)).h,(n=ka(n)).h),i=hn(e.c,n.c),a=hn(e.l,n.l),o=hn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.c=i(t),e.l=a(t),e.opacity=o(t),e+""}}}var mp=vp(un),bp=vp(hn);function xp(t){return function e(n){function r(e,r){var i=t((e=Oa(e)).h,(r=Oa(r)).h),a=hn(e.s,r.s),o=hn(e.l,r.l),s=hn(e.opacity,r.opacity);return function(t){return e.h=i(t),e.s=a(t),e.l=o(Math.pow(t,n)),e.opacity=s(t),e+""}}return n=+n,r.gamma=e,r}(1)}var _p=xp(un),kp=xp(hn);function wp(t,e){for(var n=0,r=e.length-1,i=e[0],a=new Array(r<0?0:r);n<r;)a[n]=t(i,i=e[++n]);return function(t){var e=Math.max(0,Math.min(r-1,Math.floor(t*=r)));return a[e](t-e)}}var Ep=function(t,e){for(var n=new Array(e),r=0;r<e;++r)n[r]=t(r/(e-1));return n},Tp=function(t){for(var e,n=-1,r=t.length,i=t[r-1],a=0;++n<r;)e=i,i=t[n],a+=e[1]*i[0]-e[0]*i[1];return a/2},Cp=function(t){for(var e,n,r=-1,i=t.length,a=0,o=0,s=t[i-1],c=0;++r<i;)e=s,s=t[r],c+=n=e[0]*s[1]-s[0]*e[1],a+=(e[0]+s[0])*n,o+=(e[1]+s[1])*n;return[a/(c*=3),o/c]};function Ap(t,e){return t[0]-e[0]||t[1]-e[1]}function Sp(t){for(var e,n,r,i=t.length,a=[0,1],o=2,s=2;s<i;++s){for(;o>1&&(e=t[a[o-2]],n=t[a[o-1]],r=t[s],(n[0]-e[0])*(r[1]-e[1])-(n[1]-e[1])*(r[0]-e[0])<=0);)--o;a[o++]=s}return a.slice(0,o)}var Mp=function(t){if((n=t.length)<3)return null;var e,n,r=new Array(n),i=new Array(n);for(e=0;e<n;++e)r[e]=[+t[e][0],+t[e][1],e];for(r.sort(Ap),e=0;e<n;++e)i[e]=[r[e][0],-r[e][1]];var a=Sp(r),o=Sp(i),s=o[0]===a[0],c=o[o.length-1]===a[a.length-1],u=[];for(e=a.length-1;e>=0;--e)u.push(t[r[a[e]][2]]);for(e=+s;e<o.length-c;++e)u.push(t[r[o[e]][2]]);return u},Op=function(t,e){for(var n,r,i=t.length,a=t[i-1],o=e[0],s=e[1],c=a[0],u=a[1],l=!1,h=0;h<i;++h)n=(a=t[h])[0],(r=a[1])>s!=u>s&&o<(c-n)*(s-r)/(u-r)+n&&(l=!l),c=n,u=r;return l},Dp=function(t){for(var e,n,r=-1,i=t.length,a=t[i-1],o=a[0],s=a[1],c=0;++r<i;)e=o,n=s,e-=o=(a=t[r])[0],n-=s=a[1],c+=Math.sqrt(e*e+n*n);return c},Np=function(){return Math.random()},Bp=function t(e){function n(t,n){return t=null==t?0:+t,n=null==n?1:+n,1===arguments.length?(n=t,t=0):n-=t,function(){return e()*n+t}}return n.source=t,n}(Np),Lp=function t(e){function n(t,n){var r,i;return t=null==t?0:+t,n=null==n?1:+n,function(){var a;if(null!=r)a=r,r=null;else do{r=2*e()-1,a=2*e()-1,i=r*r+a*a}while(!i||i>1);return t+n*a*Math.sqrt(-2*Math.log(i)/i)}}return n.source=t,n}(Np),Pp=function t(e){function n(){var t=Lp.source(e).apply(this,arguments);return function(){return Math.exp(t())}}return n.source=t,n}(Np),Ip=function t(e){function n(t){return function(){for(var n=0,r=0;r<t;++r)n+=e();return n}}return n.source=t,n}(Np),Fp=function t(e){function n(t){var n=Ip.source(e)(t);return function(){return n()/t}}return n.source=t,n}(Np),jp=function t(e){function n(t){return function(){return-Math.log(1-e())/t}}return n.source=t,n}(Np);function Rp(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t)}return this}function Yp(t,e){switch(arguments.length){case 0:break;case 1:this.interpolator(t);break;default:this.interpolator(e).domain(t)}return this}var zp=Array.prototype,Up=zp.map,$p=zp.slice,Wp={name:"implicit"};function Hp(){var t=Ji(),e=[],n=[],r=Wp;function i(i){var a=i+"",o=t.get(a);if(!o){if(r!==Wp)return r;t.set(a,o=e.push(i))}return n[(o-1)%n.length]}return i.domain=function(n){if(!arguments.length)return e.slice();e=[],t=Ji();for(var r,a,o=-1,s=n.length;++o<s;)t.has(a=(r=n[o])+"")||t.set(a,e.push(r));return i},i.range=function(t){return arguments.length?(n=$p.call(t),i):n.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return Hp(e,n).unknown(r)},Rp.apply(i,arguments),i}function Vp(){var t,e,n=Hp().unknown(void 0),r=n.domain,i=n.range,a=[0,1],o=!1,s=0,c=0,u=.5;function l(){var n=r().length,l=a[1]<a[0],h=a[l-0],f=a[1-l];t=(f-h)/Math.max(1,n-s+2*c),o&&(t=Math.floor(t)),h+=(f-h-t*(n-s))*u,e=t*(1-s),o&&(h=Math.round(h),e=Math.round(e));var d=k(n).map((function(e){return h+t*e}));return i(l?d.reverse():d)}return delete n.unknown,n.domain=function(t){return arguments.length?(r(t),l()):r()},n.range=function(t){return arguments.length?(a=[+t[0],+t[1]],l()):a.slice()},n.rangeRound=function(t){return a=[+t[0],+t[1]],o=!0,l()},n.bandwidth=function(){return e},n.step=function(){return t},n.round=function(t){return arguments.length?(o=!!t,l()):o},n.padding=function(t){return arguments.length?(s=Math.min(1,c=+t),l()):s},n.paddingInner=function(t){return arguments.length?(s=Math.min(1,t),l()):s},n.paddingOuter=function(t){return arguments.length?(c=+t,l()):c},n.align=function(t){return arguments.length?(u=Math.max(0,Math.min(1,t)),l()):u},n.copy=function(){return Vp(r(),a).round(o).paddingInner(s).paddingOuter(c).align(u)},Rp.apply(l(),arguments)}function Gp(t){var e=t.copy;return t.padding=t.paddingOuter,delete t.paddingInner,delete t.paddingOuter,t.copy=function(){return Gp(e())},t}function qp(){return Gp(Vp.apply(null,arguments).paddingInner(1))}var Xp=function(t){return+t},Zp=[0,1];function Jp(t){return t}function Kp(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:(n=isNaN(e)?NaN:.5,function(){return n});var n}function Qp(t){var e,n=t[0],r=t[t.length-1];return n>r&&(e=n,n=r,r=e),function(t){return Math.max(n,Math.min(r,t))}}function tg(t,e,n){var r=t[0],i=t[1],a=e[0],o=e[1];return i<r?(r=Kp(i,r),a=n(o,a)):(r=Kp(r,i),a=n(a,o)),function(t){return a(r(t))}}function eg(t,e,n){var r=Math.min(t.length,e.length)-1,i=new Array(r),a=new Array(r),o=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),e=e.slice().reverse());++o<r;)i[o]=Kp(t[o],t[o+1]),a[o]=n(e[o],e[o+1]);return function(e){var n=c(t,e,1,r)-1;return a[n](i[n](e))}}function ng(t,e){return e.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function rg(){var t,e,n,r,i,a,o=Zp,s=Zp,c=Sn,u=Jp;function l(){return r=Math.min(o.length,s.length)>2?eg:tg,i=a=null,h}function h(e){return isNaN(e=+e)?n:(i||(i=r(o.map(t),s,c)))(t(u(e)))}return h.invert=function(n){return u(e((a||(a=r(s,o.map(t),_n)))(n)))},h.domain=function(t){return arguments.length?(o=Up.call(t,Xp),u===Jp||(u=Qp(o)),l()):o.slice()},h.range=function(t){return arguments.length?(s=$p.call(t),l()):s.slice()},h.rangeRound=function(t){return s=$p.call(t),c=up,l()},h.clamp=function(t){return arguments.length?(u=t?Qp(o):Jp,h):u!==Jp},h.interpolate=function(t){return arguments.length?(c=t,l()):c},h.unknown=function(t){return arguments.length?(n=t,h):n},function(n,r){return t=n,e=r,l()}}function ig(t,e){return rg()(t,e)}var ag=function(t,e,n,r){var i,a=S(t,e,n);switch((r=Hs(null==r?",f":r)).type){case"s":var o=Math.max(Math.abs(t),Math.abs(e));return null!=r.precision||isNaN(i=ac(a,o))||(r.precision=i),Zs(r,o);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=oc(a,Math.max(Math.abs(t),Math.abs(e))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=ic(a))||(r.precision=i-2*("%"===r.type))}return Xs(r)};function og(t){var e=t.domain;return t.ticks=function(t){var n=e();return C(n[0],n[n.length-1],null==t?10:t)},t.tickFormat=function(t,n){var r=e();return ag(r[0],r[r.length-1],null==t?10:t,n)},t.nice=function(n){null==n&&(n=10);var r,i=e(),a=0,o=i.length-1,s=i[a],c=i[o];return c<s&&(r=s,s=c,c=r,r=a,a=o,o=r),(r=A(s,c,n))>0?r=A(s=Math.floor(s/r)*r,c=Math.ceil(c/r)*r,n):r<0&&(r=A(s=Math.ceil(s*r)/r,c=Math.floor(c*r)/r,n)),r>0?(i[a]=Math.floor(s/r)*r,i[o]=Math.ceil(c/r)*r,e(i)):r<0&&(i[a]=Math.ceil(s*r)/r,i[o]=Math.floor(c*r)/r,e(i)),t},t}function sg(){var t=ig(Jp,Jp);return t.copy=function(){return ng(t,sg())},Rp.apply(t,arguments),og(t)}function cg(t){var e;function n(t){return isNaN(t=+t)?e:t}return n.invert=n,n.domain=n.range=function(e){return arguments.length?(t=Up.call(e,Xp),n):t.slice()},n.unknown=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return cg(t).unknown(e)},t=arguments.length?Up.call(t,Xp):[0,1],og(n)}var ug=function(t,e){var n,r=0,i=(t=t.slice()).length-1,a=t[r],o=t[i];return o<a&&(n=r,r=i,i=n,n=a,a=o,o=n),t[r]=e.floor(a),t[i]=e.ceil(o),t};function lg(t){return Math.log(t)}function hg(t){return Math.exp(t)}function fg(t){return-Math.log(-t)}function dg(t){return-Math.exp(-t)}function pg(t){return isFinite(t)?+("1e"+t):t<0?0:t}function gg(t){return function(e){return-t(-e)}}function yg(t){var e,n,r=t(lg,hg),i=r.domain,a=10;function o(){return e=function(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),function(e){return Math.log(e)/t})}(a),n=function(t){return 10===t?pg:t===Math.E?Math.exp:function(e){return Math.pow(t,e)}}(a),i()[0]<0?(e=gg(e),n=gg(n),t(fg,dg)):t(lg,hg),r}return r.base=function(t){return arguments.length?(a=+t,o()):a},r.domain=function(t){return arguments.length?(i(t),o()):i()},r.ticks=function(t){var r,o=i(),s=o[0],c=o[o.length-1];(r=c<s)&&(f=s,s=c,c=f);var u,l,h,f=e(s),d=e(c),p=null==t?10:+t,g=[];if(!(a%1)&&d-f<p){if(f=Math.round(f)-1,d=Math.round(d)+1,s>0){for(;f<d;++f)for(l=1,u=n(f);l<a;++l)if(!((h=u*l)<s)){if(h>c)break;g.push(h)}}else for(;f<d;++f)for(l=a-1,u=n(f);l>=1;--l)if(!((h=u*l)<s)){if(h>c)break;g.push(h)}}else g=C(f,d,Math.min(d-f,p)).map(n);return r?g.reverse():g},r.tickFormat=function(t,i){if(null==i&&(i=10===a?".0e":","),"function"!=typeof i&&(i=Xs(i)),t===1/0)return i;null==t&&(t=10);var o=Math.max(1,a*t/r.ticks().length);return function(t){var r=t/n(Math.round(e(t)));return r*a<a-.5&&(r*=a),r<=o?i(t):""}},r.nice=function(){return i(ug(i(),{floor:function(t){return n(Math.floor(e(t)))},ceil:function(t){return n(Math.ceil(e(t)))}}))},r}function vg(){var t=yg(rg()).domain([1,10]);return t.copy=function(){return ng(t,vg()).base(t.base())},Rp.apply(t,arguments),t}function mg(t){return function(e){return Math.sign(e)*Math.log1p(Math.abs(e/t))}}function bg(t){return function(e){return Math.sign(e)*Math.expm1(Math.abs(e))*t}}function xg(t){var e=1,n=t(mg(e),bg(e));return n.constant=function(n){return arguments.length?t(mg(e=+n),bg(e)):e},og(n)}function _g(){var t=xg(rg());return t.copy=function(){return ng(t,_g()).constant(t.constant())},Rp.apply(t,arguments)}function kg(t){return function(e){return e<0?-Math.pow(-e,t):Math.pow(e,t)}}function wg(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function Eg(t){return t<0?-t*t:t*t}function Tg(t){var e=t(Jp,Jp),n=1;function r(){return 1===n?t(Jp,Jp):.5===n?t(wg,Eg):t(kg(n),kg(1/n))}return e.exponent=function(t){return arguments.length?(n=+t,r()):n},og(e)}function Cg(){var t=Tg(rg());return t.copy=function(){return ng(t,Cg()).exponent(t.exponent())},Rp.apply(t,arguments),t}function Ag(){return Cg.apply(null,arguments).exponent(.5)}function Sg(){var t,e=[],n=[],i=[];function a(){var t=0,r=Math.max(1,n.length);for(i=new Array(r-1);++t<r;)i[t-1]=D(e,t/r);return o}function o(e){return isNaN(e=+e)?t:n[c(i,e)]}return o.invertExtent=function(t){var r=n.indexOf(t);return r<0?[NaN,NaN]:[r>0?i[r-1]:e[0],r<i.length?i[r]:e[e.length-1]]},o.domain=function(t){if(!arguments.length)return e.slice();e=[];for(var n,i=0,o=t.length;i<o;++i)null==(n=t[i])||isNaN(n=+n)||e.push(n);return e.sort(r),a()},o.range=function(t){return arguments.length?(n=$p.call(t),a()):n.slice()},o.unknown=function(e){return arguments.length?(t=e,o):t},o.quantiles=function(){return i.slice()},o.copy=function(){return Sg().domain(e).range(n).unknown(t)},Rp.apply(o,arguments)}function Mg(){var t,e=0,n=1,r=1,i=[.5],a=[0,1];function o(e){return e<=e?a[c(i,e,0,r)]:t}function s(){var t=-1;for(i=new Array(r);++t<r;)i[t]=((t+1)*n-(t-r)*e)/(r+1);return o}return o.domain=function(t){return arguments.length?(e=+t[0],n=+t[1],s()):[e,n]},o.range=function(t){return arguments.length?(r=(a=$p.call(t)).length-1,s()):a.slice()},o.invertExtent=function(t){var o=a.indexOf(t);return o<0?[NaN,NaN]:o<1?[e,i[0]]:o>=r?[i[r-1],n]:[i[o-1],i[o]]},o.unknown=function(e){return arguments.length?(t=e,o):o},o.thresholds=function(){return i.slice()},o.copy=function(){return Mg().domain([e,n]).range(a).unknown(t)},Rp.apply(og(o),arguments)}function Og(){var t,e=[.5],n=[0,1],r=1;function i(i){return i<=i?n[c(e,i,0,r)]:t}return i.domain=function(t){return arguments.length?(e=$p.call(t),r=Math.min(e.length,n.length-1),i):e.slice()},i.range=function(t){return arguments.length?(n=$p.call(t),r=Math.min(e.length,n.length-1),i):n.slice()},i.invertExtent=function(t){var r=n.indexOf(t);return[e[r-1],e[r]]},i.unknown=function(e){return arguments.length?(t=e,i):t},i.copy=function(){return Og().domain(e).range(n).unknown(t)},Rp.apply(i,arguments)}var Dg=new Date,Ng=new Date;function Bg(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return Bg((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return Dg.setTime(+e),Ng.setTime(+r),t(Dg),t(Ng),Math.floor(n(Dg,Ng))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}var Lg=Bg((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));Lg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bg((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};var Pg=Lg,Ig=Lg.range,Fg=Bg((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,e){t.setMonth(t.getMonth()+e)}),(function(t,e){return e.getMonth()-t.getMonth()+12*(e.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})),jg=Fg,Rg=Fg.range;function Yg(t){return Bg((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/6048e5}))}var zg=Yg(0),Ug=Yg(1),$g=Yg(2),Wg=Yg(3),Hg=Yg(4),Vg=Yg(5),Gg=Yg(6),qg=zg.range,Xg=Ug.range,Zg=$g.range,Jg=Wg.range,Kg=Hg.range,Qg=Vg.range,ty=Gg.range,ey=Bg((function(t){t.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+e)}),(function(t,e){return(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/864e5}),(function(t){return t.getDate()-1})),ny=ey,ry=ey.range,iy=Bg((function(t){t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds()-6e4*t.getMinutes())}),(function(t,e){t.setTime(+t+36e5*e)}),(function(t,e){return(e-t)/36e5}),(function(t){return t.getHours()})),ay=iy,oy=iy.range,sy=Bg((function(t){t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds())}),(function(t,e){t.setTime(+t+6e4*e)}),(function(t,e){return(e-t)/6e4}),(function(t){return t.getMinutes()})),cy=sy,uy=sy.range,ly=Bg((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,e){t.setTime(+t+1e3*e)}),(function(t,e){return(e-t)/1e3}),(function(t){return t.getUTCSeconds()})),hy=ly,fy=ly.range,dy=Bg((function(){}),(function(t,e){t.setTime(+t+e)}),(function(t,e){return e-t}));dy.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Bg((function(e){e.setTime(Math.floor(e/t)*t)}),(function(e,n){e.setTime(+e+n*t)}),(function(e,n){return(n-e)/t})):dy:null};var py=dy,gy=dy.range;function yy(t){return Bg((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/6048e5}))}var vy=yy(0),my=yy(1),by=yy(2),xy=yy(3),_y=yy(4),ky=yy(5),wy=yy(6),Ey=vy.range,Ty=my.range,Cy=by.range,Ay=xy.range,Sy=_y.range,My=ky.range,Oy=wy.range,Dy=Bg((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/864e5}),(function(t){return t.getUTCDate()-1})),Ny=Dy,By=Dy.range,Ly=Bg((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));Ly.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bg((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};var Py=Ly,Iy=Ly.range;function Fy(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function jy(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Ry(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}function Yy(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=Ky(i),l=Qy(i),h=Ky(a),f=Qy(a),d=Ky(o),p=Qy(o),g=Ky(s),y=Qy(s),v=Ky(c),m=Qy(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:xv,e:xv,f:Tv,H:_v,I:kv,j:wv,L:Ev,m:Cv,M:Av,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:em,s:nm,S:Sv,u:Mv,U:Ov,V:Dv,w:Nv,W:Bv,x:null,X:null,y:Lv,Y:Pv,Z:Iv,"%":tm},x={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:Fv,e:Fv,f:Uv,H:jv,I:Rv,j:Yv,L:zv,m:$v,M:Wv,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:em,s:nm,S:Hv,u:Vv,U:Gv,V:qv,w:Xv,W:Zv,x:null,X:null,y:Jv,Y:Kv,Z:Qv,"%":tm},_={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p[r[0].toLowerCase()],n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f[r[0].toLowerCase()],n+r[0].length):-1},b:function(t,e,n){var r=v.exec(e.slice(n));return r?(t.m=m[r[0].toLowerCase()],n+r[0].length):-1},B:function(t,e,n){var r=g.exec(e.slice(n));return r?(t.m=y[r[0].toLowerCase()],n+r[0].length):-1},c:function(t,n,r){return E(t,e,n,r)},d:lv,e:lv,f:yv,H:fv,I:fv,j:hv,L:gv,m:uv,M:dv,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l[r[0].toLowerCase()],n+r[0].length):-1},q:cv,Q:mv,s:bv,S:pv,u:ev,U:nv,V:rv,w:tv,W:iv,x:function(t,e,r){return E(t,n,e,r)},X:function(t,e,n){return E(t,r,e,n)},y:ov,Y:av,Z:sv,"%":vv};function k(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=Vy[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function w(t,e){return function(n){var r,i,a=Ry(1900,void 0,1);if(E(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(!e||"Z"in a||(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=jy(Ry(a.y,0,1))).getUTCDay(),r=i>4||0===i?my.ceil(r):my(r),r=Ny.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=Fy(Ry(a.y,0,1))).getDay(),r=i>4||0===i?Ug.ceil(r):Ug(r),r=ny.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?jy(Ry(a.y,0,1)).getUTCDay():Fy(Ry(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,jy(a)):Fy(a)}}function E(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=_[i in Vy?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return(b.x=k(n,b),b.X=k(r,b),b.c=k(e,b),x.x=k(n,x),x.X=k(r,x),x.c=k(e,x),{format:function(t){var e=k(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=w(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=k(t+="",x);return e.toString=function(){return t},e},utcParse:function(t){var e=w(t+="",!0);return e.toString=function(){return t},e}})}var zy,Uy,$y,Wy,Hy,Vy={"-":"",_:" ",0:"0"},Gy=/^\s*\d+/,qy=/^%/,Xy=/[\\^$*+?|[\]().{}]/g;function Zy(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function Jy(t){return t.replace(Xy,"\\$&")}function Ky(t){return new RegExp("^(?:"+t.map(Jy).join("|")+")","i")}function Qy(t){for(var e={},n=-1,r=t.length;++n<r;)e[t[n].toLowerCase()]=n;return e}function tv(t,e,n){var r=Gy.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function ev(t,e,n){var r=Gy.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function nv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function rv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function iv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function av(t,e,n){var r=Gy.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function ov(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function sv(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function cv(t,e,n){var r=Gy.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function uv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function lv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function hv(t,e,n){var r=Gy.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function fv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function dv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function pv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function gv(t,e,n){var r=Gy.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function yv(t,e,n){var r=Gy.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function vv(t,e,n){var r=qy.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function mv(t,e,n){var r=Gy.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function bv(t,e,n){var r=Gy.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function xv(t,e){return Zy(t.getDate(),e,2)}function _v(t,e){return Zy(t.getHours(),e,2)}function kv(t,e){return Zy(t.getHours()%12||12,e,2)}function wv(t,e){return Zy(1+ny.count(Pg(t),t),e,3)}function Ev(t,e){return Zy(t.getMilliseconds(),e,3)}function Tv(t,e){return Ev(t,e)+"000"}function Cv(t,e){return Zy(t.getMonth()+1,e,2)}function Av(t,e){return Zy(t.getMinutes(),e,2)}function Sv(t,e){return Zy(t.getSeconds(),e,2)}function Mv(t){var e=t.getDay();return 0===e?7:e}function Ov(t,e){return Zy(zg.count(Pg(t)-1,t),e,2)}function Dv(t,e){var n=t.getDay();return t=n>=4||0===n?Hg(t):Hg.ceil(t),Zy(Hg.count(Pg(t),t)+(4===Pg(t).getDay()),e,2)}function Nv(t){return t.getDay()}function Bv(t,e){return Zy(Ug.count(Pg(t)-1,t),e,2)}function Lv(t,e){return Zy(t.getFullYear()%100,e,2)}function Pv(t,e){return Zy(t.getFullYear()%1e4,e,4)}function Iv(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Zy(e/60|0,"0",2)+Zy(e%60,"0",2)}function Fv(t,e){return Zy(t.getUTCDate(),e,2)}function jv(t,e){return Zy(t.getUTCHours(),e,2)}function Rv(t,e){return Zy(t.getUTCHours()%12||12,e,2)}function Yv(t,e){return Zy(1+Ny.count(Py(t),t),e,3)}function zv(t,e){return Zy(t.getUTCMilliseconds(),e,3)}function Uv(t,e){return zv(t,e)+"000"}function $v(t,e){return Zy(t.getUTCMonth()+1,e,2)}function Wv(t,e){return Zy(t.getUTCMinutes(),e,2)}function Hv(t,e){return Zy(t.getUTCSeconds(),e,2)}function Vv(t){var e=t.getUTCDay();return 0===e?7:e}function Gv(t,e){return Zy(vy.count(Py(t)-1,t),e,2)}function qv(t,e){var n=t.getUTCDay();return t=n>=4||0===n?_y(t):_y.ceil(t),Zy(_y.count(Py(t),t)+(4===Py(t).getUTCDay()),e,2)}function Xv(t){return t.getUTCDay()}function Zv(t,e){return Zy(my.count(Py(t)-1,t),e,2)}function Jv(t,e){return Zy(t.getUTCFullYear()%100,e,2)}function Kv(t,e){return Zy(t.getUTCFullYear()%1e4,e,4)}function Qv(){return"+0000"}function tm(){return"%"}function em(t){return+t}function nm(t){return Math.floor(+t/1e3)}function rm(t){return zy=Yy(t),Uy=zy.format,$y=zy.parse,Wy=zy.utcFormat,Hy=zy.utcParse,zy}rm({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function im(t){return new Date(t)}function am(t){return t instanceof Date?+t:+new Date(+t)}function om(t,e,n,r,a,o,s,c,u){var l=ig(Jp,Jp),h=l.invert,f=l.domain,d=u(".%L"),p=u(":%S"),g=u("%I:%M"),y=u("%I %p"),v=u("%a %d"),m=u("%b %d"),b=u("%B"),x=u("%Y"),_=[[s,1,1e3],[s,5,5e3],[s,15,15e3],[s,30,3e4],[o,1,6e4],[o,5,3e5],[o,15,9e5],[o,30,18e5],[a,1,36e5],[a,3,108e5],[a,6,216e5],[a,12,432e5],[r,1,864e5],[r,2,1728e5],[n,1,6048e5],[e,1,2592e6],[e,3,7776e6],[t,1,31536e6]];function k(i){return(s(i)<i?d:o(i)<i?p:a(i)<i?g:r(i)<i?y:e(i)<i?n(i)<i?v:m:t(i)<i?b:x)(i)}function w(e,n,r,a){if(null==e&&(e=10),"number"==typeof e){var o=Math.abs(r-n)/e,s=i((function(t){return t[2]})).right(_,o);s===_.length?(a=S(n/31536e6,r/31536e6,e),e=t):s?(a=(s=_[o/_[s-1][2]<_[s][2]/o?s-1:s])[1],e=s[0]):(a=Math.max(S(n,r,e),1),e=c)}return null==a?e:e.every(a)}return l.invert=function(t){return new Date(h(t))},l.domain=function(t){return arguments.length?f(Up.call(t,am)):f().map(im)},l.ticks=function(t,e){var n,r=f(),i=r[0],a=r[r.length-1],o=a<i;return o&&(n=i,i=a,a=n),n=(n=w(t,i,a,e))?n.range(i,a+1):[],o?n.reverse():n},l.tickFormat=function(t,e){return null==e?k:u(e)},l.nice=function(t,e){var n=f();return(t=w(t,n[0],n[n.length-1],e))?f(ug(n,t)):l},l.copy=function(){return ng(l,om(t,e,n,r,a,o,s,c,u))},l}var sm=function(){return Rp.apply(om(Pg,jg,zg,ny,ay,cy,hy,py,Uy).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},cm=Bg((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCMonth(t.getUTCMonth()+e)}),(function(t,e){return e.getUTCMonth()-t.getUTCMonth()+12*(e.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})),um=cm,lm=cm.range,hm=Bg((function(t){t.setUTCMinutes(0,0,0)}),(function(t,e){t.setTime(+t+36e5*e)}),(function(t,e){return(e-t)/36e5}),(function(t){return t.getUTCHours()})),fm=hm,dm=hm.range,pm=Bg((function(t){t.setUTCSeconds(0,0)}),(function(t,e){t.setTime(+t+6e4*e)}),(function(t,e){return(e-t)/6e4}),(function(t){return t.getUTCMinutes()})),gm=pm,ym=pm.range,vm=function(){return Rp.apply(om(Py,um,vy,Ny,fm,gm,hy,py,Wy).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)};function mm(){var t,e,n,r,i,a=0,o=1,s=Jp,c=!1;function u(e){return isNaN(e=+e)?i:s(0===n?.5:(e=(r(e)-t)*n,c?Math.max(0,Math.min(1,e)):e))}return u.domain=function(i){return arguments.length?(t=r(a=+i[0]),e=r(o=+i[1]),n=t===e?0:1/(e-t),u):[a,o]},u.clamp=function(t){return arguments.length?(c=!!t,u):c},u.interpolator=function(t){return arguments.length?(s=t,u):s},u.unknown=function(t){return arguments.length?(i=t,u):i},function(i){return r=i,t=i(a),e=i(o),n=t===e?0:1/(e-t),u}}function bm(t,e){return e.domain(t.domain()).interpolator(t.interpolator()).clamp(t.clamp()).unknown(t.unknown())}function xm(){var t=og(mm()(Jp));return t.copy=function(){return bm(t,xm())},Yp.apply(t,arguments)}function _m(){var t=yg(mm()).domain([1,10]);return t.copy=function(){return bm(t,_m()).base(t.base())},Yp.apply(t,arguments)}function km(){var t=xg(mm());return t.copy=function(){return bm(t,km()).constant(t.constant())},Yp.apply(t,arguments)}function wm(){var t=Tg(mm());return t.copy=function(){return bm(t,wm()).exponent(t.exponent())},Yp.apply(t,arguments)}function Em(){return wm.apply(null,arguments).exponent(.5)}function Tm(){var t=[],e=Jp;function n(n){if(!isNaN(n=+n))return e((c(t,n)-1)/(t.length-1))}return n.domain=function(e){if(!arguments.length)return t.slice();t=[];for(var i,a=0,o=e.length;a<o;++a)null==(i=e[a])||isNaN(i=+i)||t.push(i);return t.sort(r),n},n.interpolator=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return Tm(e).domain(t)},Yp.apply(n,arguments)}function Cm(){var t,e,n,r,i,a,o,s=0,c=.5,u=1,l=Jp,h=!1;function f(t){return isNaN(t=+t)?o:(t=.5+((t=+a(t))-e)*(t<e?r:i),l(h?Math.max(0,Math.min(1,t)):t))}return f.domain=function(o){return arguments.length?(t=a(s=+o[0]),e=a(c=+o[1]),n=a(u=+o[2]),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f):[s,c,u]},f.clamp=function(t){return arguments.length?(h=!!t,f):h},f.interpolator=function(t){return arguments.length?(l=t,f):l},f.unknown=function(t){return arguments.length?(o=t,f):o},function(o){return a=o,t=o(s),e=o(c),n=o(u),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f}}function Am(){var t=og(Cm()(Jp));return t.copy=function(){return bm(t,Am())},Yp.apply(t,arguments)}function Sm(){var t=yg(Cm()).domain([.1,1,10]);return t.copy=function(){return bm(t,Sm()).base(t.base())},Yp.apply(t,arguments)}function Mm(){var t=xg(Cm());return t.copy=function(){return bm(t,Mm()).constant(t.constant())},Yp.apply(t,arguments)}function Om(){var t=Tg(Cm());return t.copy=function(){return bm(t,Om()).exponent(t.exponent())},Yp.apply(t,arguments)}function Dm(){return Om.apply(null,arguments).exponent(.5)}var Nm=function(t){for(var e=t.length/6|0,n=new Array(e),r=0;r<e;)n[r]="#"+t.slice(6*r,6*++r);return n},Bm=Nm("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),Lm=Nm("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666"),Pm=Nm("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666"),Im=Nm("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928"),Fm=Nm("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2"),jm=Nm("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc"),Rm=Nm("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999"),Ym=Nm("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3"),zm=Nm("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f"),Um=Nm("4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab"),$m=function(t){return pn(t[t.length-1])},Wm=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(Nm),Hm=$m(Wm),Vm=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(Nm),Gm=$m(Vm),qm=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(Nm),Xm=$m(qm),Zm=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(Nm),Jm=$m(Zm),Km=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(Nm),Qm=$m(Km),tb=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(Nm),eb=$m(tb),nb=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(Nm),rb=$m(nb),ib=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(Nm),ab=$m(ib),ob=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(Nm),sb=$m(ob),cb=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(Nm),ub=$m(cb),lb=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(Nm),hb=$m(lb),fb=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(Nm),db=$m(fb),pb=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(Nm),gb=$m(pb),yb=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(Nm),vb=$m(yb),mb=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(Nm),bb=$m(mb),xb=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(Nm),_b=$m(xb),kb=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(Nm),wb=$m(kb),Eb=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(Nm),Tb=$m(Eb),Cb=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(Nm),Ab=$m(Cb),Sb=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(Nm),Mb=$m(Sb),Ob=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(Nm),Db=$m(Ob),Nb=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(Nm),Bb=$m(Nb),Lb=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(Nm),Pb=$m(Lb),Ib=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(Nm),Fb=$m(Ib),jb=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(Nm),Rb=$m(jb),Yb=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(Nm),zb=$m(Yb),Ub=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(Nm),$b=$m(Ub),Wb=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},Hb=kp(Oa(300,.5,0),Oa(-240,.5,1)),Vb=kp(Oa(-100,.75,.35),Oa(80,1.5,.8)),Gb=kp(Oa(260,.75,.35),Oa(80,1.5,.8)),qb=Oa(),Xb=function(t){(t<0||t>1)&&(t-=Math.floor(t));var e=Math.abs(t-.5);return qb.h=360*t-100,qb.s=1.5-1.5*e,qb.l=.8-.9*e,qb+""},Zb=Ge(),Jb=Math.PI/3,Kb=2*Math.PI/3,Qb=function(t){var e;return t=(.5-t)*Math.PI,Zb.r=255*(e=Math.sin(t))*e,Zb.g=255*(e=Math.sin(t+Jb))*e,Zb.b=255*(e=Math.sin(t+Kb))*e,Zb+""},tx=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"};function ex(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}}var nx=ex(Nm("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),rx=ex(Nm("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),ix=ex(Nm("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),ax=ex(Nm("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")),ox=function(t){return ke(ne(t).call(document.documentElement))},sx=0;function cx(){return new ux}function ux(){this._="@"+(++sx).toString(36)}ux.prototype=cx.prototype={constructor:ux,get:function(t){for(var e=this._;!(e in t);)if(!(t=t.parentNode))return;return t[e]},set:function(t,e){return t[this._]=e},remove:function(t){return this._ in t&&delete t[this._]},toString:function(){return this._}};var lx=function(t){return"string"==typeof t?new be([document.querySelectorAll(t)],[document.documentElement]):new be([null==t?[]:t],me)},hx=function(t,e){null==e&&(e=Mn().touches);for(var n=0,r=e?e.length:0,i=new Array(r);n<r;++n)i[n]=On(t,e[n]);return i},fx=function(t){return function(){return t}},dx=Math.abs,px=Math.atan2,gx=Math.cos,yx=Math.max,vx=Math.min,mx=Math.sin,bx=Math.sqrt,xx=Math.PI,_x=xx/2,kx=2*xx;function wx(t){return t>1?0:t<-1?xx:Math.acos(t)}function Ex(t){return t>=1?_x:t<=-1?-_x:Math.asin(t)}function Tx(t){return t.innerRadius}function Cx(t){return t.outerRadius}function Ax(t){return t.startAngle}function Sx(t){return t.endAngle}function Mx(t){return t&&t.padAngle}function Ox(t,e,n,r,i,a,o,s){var c=n-t,u=r-e,l=o-i,h=s-a,f=h*c-l*u;if(!(f*f<1e-12))return[t+(f=(l*(e-a)-h*(t-i))/f)*c,e+f*u]}function Dx(t,e,n,r,i,a,o){var s=t-n,c=e-r,u=(o?a:-a)/bx(s*s+c*c),l=u*c,h=-u*s,f=t+l,d=e+h,p=n+l,g=r+h,y=(f+p)/2,v=(d+g)/2,m=p-f,b=g-d,x=m*m+b*b,_=i-a,k=f*g-p*d,w=(b<0?-1:1)*bx(yx(0,_*_*x-k*k)),E=(k*b-m*w)/x,T=(-k*m-b*w)/x,C=(k*b+m*w)/x,A=(-k*m+b*w)/x,S=E-y,M=T-v,O=C-y,D=A-v;return S*S+M*M>O*O+D*D&&(E=C,T=A),{cx:E,cy:T,x01:-l,y01:-h,x11:E*(i/_-1),y11:T*(i/_-1)}}var Nx=function(){var t=Tx,e=Cx,n=fx(0),r=null,i=Ax,a=Sx,o=Mx,s=null;function c(){var c,u,l=+t.apply(this,arguments),h=+e.apply(this,arguments),f=i.apply(this,arguments)-_x,d=a.apply(this,arguments)-_x,p=dx(d-f),g=d>f;if(s||(s=c=Ui()),h<l&&(u=h,h=l,l=u),h>1e-12)if(p>kx-1e-12)s.moveTo(h*gx(f),h*mx(f)),s.arc(0,0,h,f,d,!g),l>1e-12&&(s.moveTo(l*gx(d),l*mx(d)),s.arc(0,0,l,d,f,g));else{var y,v,m=f,b=d,x=f,_=d,k=p,w=p,E=o.apply(this,arguments)/2,T=E>1e-12&&(r?+r.apply(this,arguments):bx(l*l+h*h)),C=vx(dx(h-l)/2,+n.apply(this,arguments)),A=C,S=C;if(T>1e-12){var M=Ex(T/l*mx(E)),O=Ex(T/h*mx(E));(k-=2*M)>1e-12?(x+=M*=g?1:-1,_-=M):(k=0,x=_=(f+d)/2),(w-=2*O)>1e-12?(m+=O*=g?1:-1,b-=O):(w=0,m=b=(f+d)/2)}var D=h*gx(m),N=h*mx(m),B=l*gx(_),L=l*mx(_);if(C>1e-12){var P,I=h*gx(b),F=h*mx(b),j=l*gx(x),R=l*mx(x);if(p<xx&&(P=Ox(D,N,j,R,I,F,B,L))){var Y=D-P[0],z=N-P[1],U=I-P[0],$=F-P[1],W=1/mx(wx((Y*U+z*$)/(bx(Y*Y+z*z)*bx(U*U+$*$)))/2),H=bx(P[0]*P[0]+P[1]*P[1]);A=vx(C,(l-H)/(W-1)),S=vx(C,(h-H)/(W+1))}}w>1e-12?S>1e-12?(y=Dx(j,R,D,N,h,S,g),v=Dx(I,F,B,L,h,S,g),s.moveTo(y.cx+y.x01,y.cy+y.y01),S<C?s.arc(y.cx,y.cy,S,px(y.y01,y.x01),px(v.y01,v.x01),!g):(s.arc(y.cx,y.cy,S,px(y.y01,y.x01),px(y.y11,y.x11),!g),s.arc(0,0,h,px(y.cy+y.y11,y.cx+y.x11),px(v.cy+v.y11,v.cx+v.x11),!g),s.arc(v.cx,v.cy,S,px(v.y11,v.x11),px(v.y01,v.x01),!g))):(s.moveTo(D,N),s.arc(0,0,h,m,b,!g)):s.moveTo(D,N),l>1e-12&&k>1e-12?A>1e-12?(y=Dx(B,L,I,F,l,-A,g),v=Dx(D,N,j,R,l,-A,g),s.lineTo(y.cx+y.x01,y.cy+y.y01),A<C?s.arc(y.cx,y.cy,A,px(y.y01,y.x01),px(v.y01,v.x01),!g):(s.arc(y.cx,y.cy,A,px(y.y01,y.x01),px(y.y11,y.x11),!g),s.arc(0,0,l,px(y.cy+y.y11,y.cx+y.x11),px(v.cy+v.y11,v.cx+v.x11),g),s.arc(v.cx,v.cy,A,px(v.y11,v.x11),px(v.y01,v.x01),!g))):s.arc(0,0,l,_,x,g):s.lineTo(B,L)}else s.moveTo(0,0);if(s.closePath(),c)return s=null,c+""||null}return c.centroid=function(){var n=(+t.apply(this,arguments)+ +e.apply(this,arguments))/2,r=(+i.apply(this,arguments)+ +a.apply(this,arguments))/2-xx/2;return[gx(r)*n,mx(r)*n]},c.innerRadius=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),c):t},c.outerRadius=function(t){return arguments.length?(e="function"==typeof t?t:fx(+t),c):e},c.cornerRadius=function(t){return arguments.length?(n="function"==typeof t?t:fx(+t),c):n},c.padRadius=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:fx(+t),c):r},c.startAngle=function(t){return arguments.length?(i="function"==typeof t?t:fx(+t),c):i},c.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:fx(+t),c):a},c.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:fx(+t),c):o},c.context=function(t){return arguments.length?(s=null==t?null:t,c):s},c};function Bx(t){this._context=t}Bx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:this._context.lineTo(t,e)}}};var Lx=function(t){return new Bx(t)};function Px(t){return t[0]}function Ix(t){return t[1]}var Fx=function(){var t=Px,e=Ix,n=fx(!0),r=null,i=Lx,a=null;function o(o){var s,c,u,l=o.length,h=!1;for(null==r&&(a=i(u=Ui())),s=0;s<=l;++s)!(s<l&&n(c=o[s],s,o))===h&&((h=!h)?a.lineStart():a.lineEnd()),h&&a.point(+t(c,s,o),+e(c,s,o));if(u)return a=null,u+""||null}return o.x=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),o):t},o.y=function(t){return arguments.length?(e="function"==typeof t?t:fx(+t),o):e},o.defined=function(t){return arguments.length?(n="function"==typeof t?t:fx(!!t),o):n},o.curve=function(t){return arguments.length?(i=t,null!=r&&(a=i(r)),o):i},o.context=function(t){return arguments.length?(null==t?r=a=null:a=i(r=t),o):r},o},jx=function(){var t=Px,e=null,n=fx(0),r=Ix,i=fx(!0),a=null,o=Lx,s=null;function c(c){var u,l,h,f,d,p=c.length,g=!1,y=new Array(p),v=new Array(p);for(null==a&&(s=o(d=Ui())),u=0;u<=p;++u){if(!(u<p&&i(f=c[u],u,c))===g)if(g=!g)l=u,s.areaStart(),s.lineStart();else{for(s.lineEnd(),s.lineStart(),h=u-1;h>=l;--h)s.point(y[h],v[h]);s.lineEnd(),s.areaEnd()}g&&(y[u]=+t(f,u,c),v[u]=+n(f,u,c),s.point(e?+e(f,u,c):y[u],r?+r(f,u,c):v[u]))}if(d)return s=null,d+""||null}function u(){return Fx().defined(i).curve(o).context(a)}return c.x=function(n){return arguments.length?(t="function"==typeof n?n:fx(+n),e=null,c):t},c.x0=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),c):t},c.x1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:fx(+t),c):e},c.y=function(t){return arguments.length?(n="function"==typeof t?t:fx(+t),r=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:fx(+t),c):n},c.y1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:fx(+t),c):r},c.lineX0=c.lineY0=function(){return u().x(t).y(n)},c.lineY1=function(){return u().x(t).y(r)},c.lineX1=function(){return u().x(e).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:fx(!!t),c):i},c.curve=function(t){return arguments.length?(o=t,null!=a&&(s=o(a)),c):o},c.context=function(t){return arguments.length?(null==t?a=s=null:s=o(a=t),c):a},c},Rx=function(t,e){return e<t?-1:e>t?1:e>=t?0:NaN},Yx=function(t){return t},zx=function(){var t=Yx,e=Rx,n=null,r=fx(0),i=fx(kx),a=fx(0);function o(o){var s,c,u,l,h,f=o.length,d=0,p=new Array(f),g=new Array(f),y=+r.apply(this,arguments),v=Math.min(kx,Math.max(-kx,i.apply(this,arguments)-y)),m=Math.min(Math.abs(v)/f,a.apply(this,arguments)),b=m*(v<0?-1:1);for(s=0;s<f;++s)(h=g[p[s]=s]=+t(o[s],s,o))>0&&(d+=h);for(null!=e?p.sort((function(t,n){return e(g[t],g[n])})):null!=n&&p.sort((function(t,e){return n(o[t],o[e])})),s=0,u=d?(v-f*b)/d:0;s<f;++s,y=l)c=p[s],l=y+((h=g[c])>0?h*u:0)+b,g[c]={data:o[c],index:s,value:h,startAngle:y,endAngle:l,padAngle:m};return g}return o.value=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),o):t},o.sortValues=function(t){return arguments.length?(e=t,n=null,o):e},o.sort=function(t){return arguments.length?(n=t,e=null,o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:fx(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:fx(+t),o):i},o.padAngle=function(t){return arguments.length?(a="function"==typeof t?t:fx(+t),o):a},o},Ux=Wx(Lx);function $x(t){this._curve=t}function Wx(t){function e(e){return new $x(t(e))}return e._curve=t,e}function Hx(t){var e=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?e(Wx(t)):e()._curve},t}$x.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,e){this._curve.point(e*Math.sin(t),e*-Math.cos(t))}};var Vx=function(){return Hx(Fx().curve(Ux))},Gx=function(){var t=jx().curve(Ux),e=t.curve,n=t.lineX0,r=t.lineX1,i=t.lineY0,a=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Hx(n())},delete t.lineX0,t.lineEndAngle=function(){return Hx(r())},delete t.lineX1,t.lineInnerRadius=function(){return Hx(i())},delete t.lineY0,t.lineOuterRadius=function(){return Hx(a())},delete t.lineY1,t.curve=function(t){return arguments.length?e(Wx(t)):e()._curve},t},qx=function(t,e){return[(e=+e)*Math.cos(t-=Math.PI/2),e*Math.sin(t)]},Xx=Array.prototype.slice;function Zx(t){return t.source}function Jx(t){return t.target}function Kx(t){var e=Zx,n=Jx,r=Px,i=Ix,a=null;function o(){var o,s=Xx.call(arguments),c=e.apply(this,s),u=n.apply(this,s);if(a||(a=o=Ui()),t(a,+r.apply(this,(s[0]=c,s)),+i.apply(this,s),+r.apply(this,(s[0]=u,s)),+i.apply(this,s)),o)return a=null,o+""||null}return o.source=function(t){return arguments.length?(e=t,o):e},o.target=function(t){return arguments.length?(n=t,o):n},o.x=function(t){return arguments.length?(r="function"==typeof t?t:fx(+t),o):r},o.y=function(t){return arguments.length?(i="function"==typeof t?t:fx(+t),o):i},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o}function Qx(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e=(e+r)/2,n,e,i,r,i)}function t_(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e,n=(n+i)/2,r,n,r,i)}function e_(t,e,n,r,i){var a=qx(e,n),o=qx(e,n=(n+i)/2),s=qx(r,n),c=qx(r,i);t.moveTo(a[0],a[1]),t.bezierCurveTo(o[0],o[1],s[0],s[1],c[0],c[1])}function n_(){return Kx(Qx)}function r_(){return Kx(t_)}function i_(){var t=Kx(e_);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t}var a_={draw:function(t,e){var n=Math.sqrt(e/xx);t.moveTo(n,0),t.arc(0,0,n,0,kx)}},o_={draw:function(t,e){var n=Math.sqrt(e/5)/2;t.moveTo(-3*n,-n),t.lineTo(-n,-n),t.lineTo(-n,-3*n),t.lineTo(n,-3*n),t.lineTo(n,-n),t.lineTo(3*n,-n),t.lineTo(3*n,n),t.lineTo(n,n),t.lineTo(n,3*n),t.lineTo(-n,3*n),t.lineTo(-n,n),t.lineTo(-3*n,n),t.closePath()}},s_=Math.sqrt(1/3),c_=2*s_,u_={draw:function(t,e){var n=Math.sqrt(e/c_),r=n*s_;t.moveTo(0,-n),t.lineTo(r,0),t.lineTo(0,n),t.lineTo(-r,0),t.closePath()}},l_=Math.sin(xx/10)/Math.sin(7*xx/10),h_=Math.sin(kx/10)*l_,f_=-Math.cos(kx/10)*l_,d_={draw:function(t,e){var n=Math.sqrt(.8908130915292852*e),r=h_*n,i=f_*n;t.moveTo(0,-n),t.lineTo(r,i);for(var a=1;a<5;++a){var o=kx*a/5,s=Math.cos(o),c=Math.sin(o);t.lineTo(c*n,-s*n),t.lineTo(s*r-c*i,c*r+s*i)}t.closePath()}},p_={draw:function(t,e){var n=Math.sqrt(e),r=-n/2;t.rect(r,r,n,n)}},g_=Math.sqrt(3),y_={draw:function(t,e){var n=-Math.sqrt(e/(3*g_));t.moveTo(0,2*n),t.lineTo(-g_*n,-n),t.lineTo(g_*n,-n),t.closePath()}},v_=Math.sqrt(3)/2,m_=1/Math.sqrt(12),b_=3*(m_/2+1),x_={draw:function(t,e){var n=Math.sqrt(e/b_),r=n/2,i=n*m_,a=r,o=n*m_+n,s=-a,c=o;t.moveTo(r,i),t.lineTo(a,o),t.lineTo(s,c),t.lineTo(-.5*r-v_*i,v_*r+-.5*i),t.lineTo(-.5*a-v_*o,v_*a+-.5*o),t.lineTo(-.5*s-v_*c,v_*s+-.5*c),t.lineTo(-.5*r+v_*i,-.5*i-v_*r),t.lineTo(-.5*a+v_*o,-.5*o-v_*a),t.lineTo(-.5*s+v_*c,-.5*c-v_*s),t.closePath()}},__=[a_,o_,u_,p_,d_,y_,x_],k_=function(){var t=fx(a_),e=fx(64),n=null;function r(){var r;if(n||(n=r=Ui()),t.apply(this,arguments).draw(n,+e.apply(this,arguments)),r)return n=null,r+""||null}return r.type=function(e){return arguments.length?(t="function"==typeof e?e:fx(e),r):t},r.size=function(t){return arguments.length?(e="function"==typeof t?t:fx(+t),r):e},r.context=function(t){return arguments.length?(n=null==t?null:t,r):n},r},w_=function(){};function E_(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function T_(t){this._context=t}T_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:E_(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:E_(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};var C_=function(t){return new T_(t)};function A_(t){this._context=t}A_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:E_(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};var S_=function(t){return new A_(t)};function M_(t){this._context=t}M_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:E_(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};var O_=function(t){return new M_(t)};function D_(t,e){this._basis=new T_(t),this._beta=e}D_.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,e=this._y,n=t.length-1;if(n>0)for(var r,i=t[0],a=e[0],o=t[n]-i,s=e[n]-a,c=-1;++c<=n;)r=c/n,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*o),this._beta*e[c]+(1-this._beta)*(a+r*s));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};var N_=function t(e){function n(t){return 1===e?new T_(t):new D_(t,e)}return n.beta=function(e){return t(+e)},n}(.85);function B_(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function L_(t,e){this._context=t,this._k=(1-e)/6}L_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:B_(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:B_(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var P_=function t(e){function n(t){return new L_(t,e)}return n.tension=function(e){return t(+e)},n}(0);function I_(t,e){this._context=t,this._k=(1-e)/6}I_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:B_(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var F_=function t(e){function n(t){return new I_(t,e)}return n.tension=function(e){return t(+e)},n}(0);function j_(t,e){this._context=t,this._k=(1-e)/6}j_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:B_(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var R_=function t(e){function n(t){return new j_(t,e)}return n.tension=function(e){return t(+e)},n}(0);function Y_(t,e,n){var r=t._x1,i=t._y1,a=t._x2,o=t._y2;if(t._l01_a>1e-12){var s=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*s-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*s-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>1e-12){var u=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,l=3*t._l23_a*(t._l23_a+t._l12_a);a=(a*u+t._x1*t._l23_2a-e*t._l12_2a)/l,o=(o*u+t._y1*t._l23_2a-n*t._l12_2a)/l}t._context.bezierCurveTo(r,i,a,o,t._x2,t._y2)}function z_(t,e){this._context=t,this._alpha=e}z_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:Y_(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var U_=function t(e){function n(t){return e?new z_(t,e):new L_(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function $_(t,e){this._context=t,this._alpha=e}$_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Y_(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var W_=function t(e){function n(t){return e?new $_(t,e):new I_(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function H_(t,e){this._context=t,this._alpha=e}H_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Y_(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var V_=function t(e){function n(t){return e?new H_(t,e):new j_(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function G_(t){this._context=t}G_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}};var q_=function(t){return new G_(t)};function X_(t){return t<0?-1:1}function Z_(t,e,n){var r=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(r||i<0&&-0),o=(n-t._y1)/(i||r<0&&-0),s=(a*i+o*r)/(r+i);return(X_(a)+X_(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(s))||0}function J_(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function K_(t,e,n){var r=t._x0,i=t._y0,a=t._x1,o=t._y1,s=(a-r)/3;t._context.bezierCurveTo(r+s,i+s*e,a-s,o-s*n,a,o)}function Q_(t){this._context=t}function tk(t){this._context=new ek(t)}function ek(t){this._context=t}function nk(t){return new Q_(t)}function rk(t){return new tk(t)}function ik(t){this._context=t}function ak(t){var e,n,r=t.length-1,i=new Array(r),a=new Array(r),o=new Array(r);for(i[0]=0,a[0]=2,o[0]=t[0]+2*t[1],e=1;e<r-1;++e)i[e]=1,a[e]=4,o[e]=4*t[e]+2*t[e+1];for(i[r-1]=2,a[r-1]=7,o[r-1]=8*t[r-1]+t[r],e=1;e<r;++e)n=i[e]/a[e-1],a[e]-=n,o[e]-=n*o[e-1];for(i[r-1]=o[r-1]/a[r-1],e=r-2;e>=0;--e)i[e]=(o[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e<r-1;++e)a[e]=2*t[e+1]-i[e+1];return[i,a]}Q_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:K_(this,this._t0,J_(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(e=+e,(t=+t)!==this._x1||e!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,K_(this,J_(this,n=Z_(this,t,e)),n);break;default:K_(this,this._t0,n=Z_(this,t,e))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}},(tk.prototype=Object.create(Q_.prototype)).point=function(t,e){Q_.prototype.point.call(this,e,t)},ek.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,r,i,a){this._context.bezierCurveTo(e,t,r,n,a,i)}},ik.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),2===n)this._context.lineTo(t[1],e[1]);else for(var r=ak(t),i=ak(e),a=0,o=1;o<n;++a,++o)this._context.bezierCurveTo(r[0][a],i[0][a],r[1][a],i[1][a],t[o],e[o]);(this._line||0!==this._line&&1===n)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(t,e){this._x.push(+t),this._y.push(+e)}};var ok=function(t){return new ik(t)};function sk(t,e){this._context=t,this._t=e}sk.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&2===this._point&&this._context.lineTo(this._x,this._y),(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var ck=function(t){return new sk(t,.5)};function uk(t){return new sk(t,0)}function lk(t){return new sk(t,1)}var hk=function(t,e){if((i=t.length)>1)for(var n,r,i,a=1,o=t[e[0]],s=o.length;a<i;++a)for(r=o,o=t[e[a]],n=0;n<s;++n)o[n][1]+=o[n][0]=isNaN(r[n][1])?r[n][0]:r[n][1]},fk=function(t){for(var e=t.length,n=new Array(e);--e>=0;)n[e]=e;return n};function dk(t,e){return t[e]}var pk=function(){var t=fx([]),e=fk,n=hk,r=dk;function i(i){var a,o,s=t.apply(this,arguments),c=i.length,u=s.length,l=new Array(u);for(a=0;a<u;++a){for(var h,f=s[a],d=l[a]=new Array(c),p=0;p<c;++p)d[p]=h=[0,+r(i[p],f,p,i)],h.data=i[p];d.key=f}for(a=0,o=e(l);a<u;++a)l[o[a]].index=a;return n(l,o),l}return i.keys=function(e){return arguments.length?(t="function"==typeof e?e:fx(Xx.call(e)),i):t},i.value=function(t){return arguments.length?(r="function"==typeof t?t:fx(+t),i):r},i.order=function(t){return arguments.length?(e=null==t?fk:"function"==typeof t?t:fx(Xx.call(t)),i):e},i.offset=function(t){return arguments.length?(n=null==t?hk:t,i):n},i},gk=function(t,e){if((r=t.length)>0){for(var n,r,i,a=0,o=t[0].length;a<o;++a){for(i=n=0;n<r;++n)i+=t[n][a][1]||0;if(i)for(n=0;n<r;++n)t[n][a][1]/=i}hk(t,e)}},yk=function(t,e){if((s=t.length)>0)for(var n,r,i,a,o,s,c=0,u=t[e[0]].length;c<u;++c)for(a=o=0,n=0;n<s;++n)(i=(r=t[e[n]][c])[1]-r[0])>0?(r[0]=a,r[1]=a+=i):i<0?(r[1]=o,r[0]=o+=i):(r[0]=0,r[1]=i)},vk=function(t,e){if((n=t.length)>0){for(var n,r=0,i=t[e[0]],a=i.length;r<a;++r){for(var o=0,s=0;o<n;++o)s+=t[o][r][1]||0;i[r][1]+=i[r][0]=-s/2}hk(t,e)}},mk=function(t,e){if((i=t.length)>0&&(r=(n=t[e[0]]).length)>0){for(var n,r,i,a=0,o=1;o<r;++o){for(var s=0,c=0,u=0;s<i;++s){for(var l=t[e[s]],h=l[o][1]||0,f=(h-(l[o-1][1]||0))/2,d=0;d<s;++d){var p=t[e[d]];f+=(p[o][1]||0)-(p[o-1][1]||0)}c+=h,u+=f*h}n[o-1][1]+=n[o-1][0]=a,c&&(a-=u/c)}n[o-1][1]+=n[o-1][0]=a,hk(t,e)}},bk=function(t){var e=t.map(xk);return fk(t).sort((function(t,n){return e[t]-e[n]}))};function xk(t){for(var e,n=-1,r=0,i=t.length,a=-1/0;++n<i;)(e=+t[n][1])>a&&(a=e,r=n);return r}var _k=function(t){var e=t.map(kk);return fk(t).sort((function(t,n){return e[t]-e[n]}))};function kk(t){for(var e,n=0,r=-1,i=t.length;++r<i;)(e=+t[r][1])&&(n+=e);return n}var wk=function(t){return _k(t).reverse()},Ek=function(t){var e,n,r=t.length,i=t.map(kk),a=bk(t),o=0,s=0,c=[],u=[];for(e=0;e<r;++e)n=a[e],o<s?(o+=i[n],c.push(n)):(s+=i[n],u.push(n));return u.reverse().concat(c)},Tk=function(t){return fk(t).reverse()};var Ck=Date.prototype.toISOString?function(t){return t.toISOString()}:Wy("%Y-%m-%dT%H:%M:%S.%LZ");var Ak=+new Date("2000-01-01T00:00:00.000Z")?function(t){var e=new Date(t);return isNaN(e)?null:e}:Hy("%Y-%m-%dT%H:%M:%S.%LZ"),Sk=function(t,e,n){var r=new $n,i=e;return null==e?(r.restart(t,e,n),r):(e=+e,n=null==n?zn():+n,r.restart((function a(o){o+=i,r.restart(a,i+=e,n),t(o)}),e,n),r)},Mk=function(t){return function(){return t}};function Ok(t){return t[0]}function Dk(t){return t[1]}function Nk(){this._=null}function Bk(t){t.U=t.C=t.L=t.R=t.P=t.N=null}function Lk(t,e){var n=e,r=e.R,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.R=r.L,n.R&&(n.R.U=n),r.L=n}function Pk(t,e){var n=e,r=e.L,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.L=r.R,n.L&&(n.L.U=n),r.R=n}function Ik(t){for(;t.L;)t=t.L;return t}Nk.prototype={constructor:Nk,insert:function(t,e){var n,r,i;if(t){if(e.P=t,e.N=t.N,t.N&&(t.N.P=e),t.N=e,t.R){for(t=t.R;t.L;)t=t.L;t.L=e}else t.R=e;n=t}else this._?(t=Ik(this._),e.P=null,e.N=t,t.P=t.L=e,n=t):(e.P=e.N=null,this._=e,n=null);for(e.L=e.R=null,e.U=n,e.C=!0,t=e;n&&n.C;)n===(r=n.U).L?(i=r.R)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.R&&(Lk(this,n),n=(t=n).U),n.C=!1,r.C=!0,Pk(this,r)):(i=r.L)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.L&&(Pk(this,n),n=(t=n).U),n.C=!1,r.C=!0,Lk(this,r)),n=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var e,n,r,i=t.U,a=t.L,o=t.R;if(n=a?o?Ik(o):a:o,i?i.L===t?i.L=n:i.R=n:this._=n,a&&o?(r=n.C,n.C=t.C,n.L=a,a.U=n,n!==o?(i=n.U,n.U=t.U,t=n.R,i.L=t,n.R=o,o.U=n):(n.U=i,i=n,t=n.R)):(r=t.C,t=n),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((e=i.R).C&&(e.C=!1,i.C=!0,Lk(this,i),e=i.R),e.L&&e.L.C||e.R&&e.R.C){e.R&&e.R.C||(e.L.C=!1,e.C=!0,Pk(this,e),e=i.R),e.C=i.C,i.C=e.R.C=!1,Lk(this,i),t=this._;break}}else if((e=i.L).C&&(e.C=!1,i.C=!0,Pk(this,i),e=i.L),e.L&&e.L.C||e.R&&e.R.C){e.L&&e.L.C||(e.R.C=!1,e.C=!0,Lk(this,e),e=i.L),e.C=i.C,i.C=e.L.C=!1,Pk(this,i),t=this._;break}e.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};var Fk=Nk;function jk(t,e,n,r){var i=[null,null],a=cw.push(i)-1;return i.left=t,i.right=e,n&&Yk(i,t,e,n),r&&Yk(i,e,t,r),ow[t.index].halfedges.push(a),ow[e.index].halfedges.push(a),i}function Rk(t,e,n){var r=[e,n];return r.left=t,r}function Yk(t,e,n,r){t[0]||t[1]?t.left===n?t[1]=r:t[0]=r:(t[0]=r,t.left=e,t.right=n)}function zk(t,e,n,r,i){var a,o=t[0],s=t[1],c=o[0],u=o[1],l=0,h=1,f=s[0]-c,d=s[1]-u;if(a=e-c,f||!(a>0)){if(a/=f,f<0){if(a<l)return;a<h&&(h=a)}else if(f>0){if(a>h)return;a>l&&(l=a)}if(a=r-c,f||!(a<0)){if(a/=f,f<0){if(a>h)return;a>l&&(l=a)}else if(f>0){if(a<l)return;a<h&&(h=a)}if(a=n-u,d||!(a>0)){if(a/=d,d<0){if(a<l)return;a<h&&(h=a)}else if(d>0){if(a>h)return;a>l&&(l=a)}if(a=i-u,d||!(a<0)){if(a/=d,d<0){if(a>h)return;a>l&&(l=a)}else if(d>0){if(a<l)return;a<h&&(h=a)}return!(l>0||h<1)||(l>0&&(t[0]=[c+l*f,u+l*d]),h<1&&(t[1]=[c+h*f,u+h*d]),!0)}}}}}function Uk(t,e,n,r,i){var a=t[1];if(a)return!0;var o,s,c=t[0],u=t.left,l=t.right,h=u[0],f=u[1],d=l[0],p=l[1],g=(h+d)/2,y=(f+p)/2;if(p===f){if(g<e||g>=r)return;if(h>d){if(c){if(c[1]>=i)return}else c=[g,n];a=[g,i]}else{if(c){if(c[1]<n)return}else c=[g,i];a=[g,n]}}else if(s=y-(o=(h-d)/(p-f))*g,o<-1||o>1)if(h>d){if(c){if(c[1]>=i)return}else c=[(n-s)/o,n];a=[(i-s)/o,i]}else{if(c){if(c[1]<n)return}else c=[(i-s)/o,i];a=[(n-s)/o,n]}else if(f<p){if(c){if(c[0]>=r)return}else c=[e,o*e+s];a=[r,o*r+s]}else{if(c){if(c[0]<e)return}else c=[r,o*r+s];a=[e,o*e+s]}return t[0]=c,t[1]=a,!0}function $k(t,e){var n=t.site,r=e.left,i=e.right;return n===i&&(i=r,r=n),i?Math.atan2(i[1]-r[1],i[0]-r[0]):(n===r?(r=e[1],i=e[0]):(r=e[0],i=e[1]),Math.atan2(r[0]-i[0],i[1]-r[1]))}function Wk(t,e){return e[+(e.left!==t.site)]}function Hk(t,e){return e[+(e.left===t.site)]}var Vk,Gk=[];function qk(){Bk(this),this.x=this.y=this.arc=this.site=this.cy=null}function Xk(t){var e=t.P,n=t.N;if(e&&n){var r=e.site,i=t.site,a=n.site;if(r!==a){var o=i[0],s=i[1],c=r[0]-o,u=r[1]-s,l=a[0]-o,h=a[1]-s,f=2*(c*h-u*l);if(!(f>=-lw)){var d=c*c+u*u,p=l*l+h*h,g=(h*d-u*p)/f,y=(c*p-l*d)/f,v=Gk.pop()||new qk;v.arc=t,v.site=i,v.x=g+o,v.y=(v.cy=y+s)+Math.sqrt(g*g+y*y),t.circle=v;for(var m=null,b=sw._;b;)if(v.y<b.y||v.y===b.y&&v.x<=b.x){if(!b.L){m=b.P;break}b=b.L}else{if(!b.R){m=b;break}b=b.R}sw.insert(m,v),m||(Vk=v)}}}}function Zk(t){var e=t.circle;e&&(e.P||(Vk=e.N),sw.remove(e),Gk.push(e),Bk(e),t.circle=null)}var Jk=[];function Kk(){Bk(this),this.edge=this.site=this.circle=null}function Qk(t){var e=Jk.pop()||new Kk;return e.site=t,e}function tw(t){Zk(t),aw.remove(t),Jk.push(t),Bk(t)}function ew(t){var e=t.circle,n=e.x,r=e.cy,i=[n,r],a=t.P,o=t.N,s=[t];tw(t);for(var c=a;c.circle&&Math.abs(n-c.circle.x)<uw&&Math.abs(r-c.circle.cy)<uw;)a=c.P,s.unshift(c),tw(c),c=a;s.unshift(c),Zk(c);for(var u=o;u.circle&&Math.abs(n-u.circle.x)<uw&&Math.abs(r-u.circle.cy)<uw;)o=u.N,s.push(u),tw(u),u=o;s.push(u),Zk(u);var l,h=s.length;for(l=1;l<h;++l)u=s[l],c=s[l-1],Yk(u.edge,c.site,u.site,i);c=s[0],(u=s[h-1]).edge=jk(c.site,u.site,null,i),Xk(c),Xk(u)}function nw(t){for(var e,n,r,i,a=t[0],o=t[1],s=aw._;s;)if((r=rw(s,o)-a)>uw)s=s.L;else{if(!((i=a-iw(s,o))>uw)){r>-uw?(e=s.P,n=s):i>-uw?(e=s,n=s.N):e=n=s;break}if(!s.R){e=s;break}s=s.R}!function(t){ow[t.index]={site:t,halfedges:[]}}(t);var c=Qk(t);if(aw.insert(e,c),e||n){if(e===n)return Zk(e),n=Qk(e.site),aw.insert(c,n),c.edge=n.edge=jk(e.site,c.site),Xk(e),void Xk(n);if(n){Zk(e),Zk(n);var u=e.site,l=u[0],h=u[1],f=t[0]-l,d=t[1]-h,p=n.site,g=p[0]-l,y=p[1]-h,v=2*(f*y-d*g),m=f*f+d*d,b=g*g+y*y,x=[(y*m-d*b)/v+l,(f*b-g*m)/v+h];Yk(n.edge,u,p,x),c.edge=jk(u,t,null,x),n.edge=jk(t,p,null,x),Xk(e),Xk(n)}else c.edge=jk(e.site,c.site)}}function rw(t,e){var n=t.site,r=n[0],i=n[1],a=i-e;if(!a)return r;var o=t.P;if(!o)return-1/0;var s=(n=o.site)[0],c=n[1],u=c-e;if(!u)return s;var l=s-r,h=1/a-1/u,f=l/u;return h?(-f+Math.sqrt(f*f-2*h*(l*l/(-2*u)-c+u/2+i-a/2)))/h+r:(r+s)/2}function iw(t,e){var n=t.N;if(n)return rw(n,e);var r=t.site;return r[1]===e?r[0]:1/0}var aw,ow,sw,cw,uw=1e-6,lw=1e-12;function hw(t,e){return e[1]-t[1]||e[0]-t[0]}function fw(t,e){var n,r,i,a=t.sort(hw).pop();for(cw=[],ow=new Array(t.length),aw=new Fk,sw=new Fk;;)if(i=Vk,a&&(!i||a[1]<i.y||a[1]===i.y&&a[0]<i.x))a[0]===n&&a[1]===r||(nw(a),n=a[0],r=a[1]),a=t.pop();else{if(!i)break;ew(i.arc)}if(function(){for(var t,e,n,r,i=0,a=ow.length;i<a;++i)if((t=ow[i])&&(r=(e=t.halfedges).length)){var o=new Array(r),s=new Array(r);for(n=0;n<r;++n)o[n]=n,s[n]=$k(t,cw[e[n]]);for(o.sort((function(t,e){return s[e]-s[t]})),n=0;n<r;++n)s[n]=e[o[n]];for(n=0;n<r;++n)e[n]=s[n]}}(),e){var o=+e[0][0],s=+e[0][1],c=+e[1][0],u=+e[1][1];!function(t,e,n,r){for(var i,a=cw.length;a--;)Uk(i=cw[a],t,e,n,r)&&zk(i,t,e,n,r)&&(Math.abs(i[0][0]-i[1][0])>uw||Math.abs(i[0][1]-i[1][1])>uw)||delete cw[a]}(o,s,c,u),function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,g,y=ow.length,v=!0;for(i=0;i<y;++i)if(a=ow[i]){for(o=a.site,s=(c=a.halfedges).length;s--;)cw[c[s]]||c.splice(s,1);for(s=0,u=c.length;s<u;)p=(d=Hk(a,cw[c[s]]))[0],g=d[1],h=(l=Wk(a,cw[c[++s%u]]))[0],f=l[1],(Math.abs(p-h)>uw||Math.abs(g-f)>uw)&&(c.splice(s,0,cw.push(Rk(o,d,Math.abs(p-t)<uw&&r-g>uw?[t,Math.abs(h-t)<uw?f:r]:Math.abs(g-r)<uw&&n-p>uw?[Math.abs(f-r)<uw?h:n,r]:Math.abs(p-n)<uw&&g-e>uw?[n,Math.abs(h-n)<uw?f:e]:Math.abs(g-e)<uw&&p-t>uw?[Math.abs(f-e)<uw?h:t,e]:null))-1),++u);u&&(v=!1)}if(v){var m,b,x,_=1/0;for(i=0,v=null;i<y;++i)(a=ow[i])&&(x=(m=(o=a.site)[0]-t)*m+(b=o[1]-e)*b)<_&&(_=x,v=a);if(v){var k=[t,e],w=[t,r],E=[n,r],T=[n,e];v.halfedges.push(cw.push(Rk(o=v.site,k,w))-1,cw.push(Rk(o,w,E))-1,cw.push(Rk(o,E,T))-1,cw.push(Rk(o,T,k))-1)}}for(i=0;i<y;++i)(a=ow[i])&&(a.halfedges.length||delete ow[i])}(o,s,c,u)}this.edges=cw,this.cells=ow,aw=sw=cw=ow=null}fw.prototype={constructor:fw,polygons:function(){var t=this.edges;return this.cells.map((function(e){var n=e.halfedges.map((function(n){return Wk(e,t[n])}));return n.data=e.site.data,n}))},triangles:function(){var t=[],e=this.edges;return this.cells.forEach((function(n,r){if(a=(i=n.halfedges).length)for(var i,a,o,s,c,u,l=n.site,h=-1,f=e[i[a-1]],d=f.left===l?f.right:f.left;++h<a;)o=d,d=(f=e[i[h]]).left===l?f.right:f.left,o&&d&&r<o.index&&r<d.index&&(c=o,u=d,((s=l)[0]-u[0])*(c[1]-s[1])-(s[0]-c[0])*(u[1]-s[1])<0)&&t.push([l.data,o.data,d.data])})),t},links:function(){return this.edges.filter((function(t){return t.right})).map((function(t){return{source:t.left.data,target:t.right.data}}))},find:function(t,e,n){for(var r,i,a=this,o=a._found||0,s=a.cells.length;!(i=a.cells[o]);)if(++o>=s)return null;var c=t-i.site[0],u=e-i.site[1],l=c*c+u*u;do{i=a.cells[r=o],o=null,i.halfedges.forEach((function(n){var r=a.edges[n],s=r.left;if(s!==i.site&&s||(s=r.right)){var c=t-s[0],u=e-s[1],h=c*c+u*u;h<l&&(l=h,o=s.index)}}))}while(null!==o);return a._found=r,null==n||l<=n*n?i.site:null}};var dw=function(){var t=Ok,e=Dk,n=null;function r(r){return new fw(r.map((function(n,i){var a=[Math.round(t(n,i,r)/uw)*uw,Math.round(e(n,i,r)/uw)*uw];return a.index=i,a.data=n,a})),n)}return r.polygons=function(t){return r(t).polygons()},r.links=function(t){return r(t).links()},r.triangles=function(t){return r(t).triangles()},r.x=function(e){return arguments.length?(t="function"==typeof e?e:Mk(+e),r):t},r.y=function(t){return arguments.length?(e="function"==typeof t?t:Mk(+t),r):e},r.extent=function(t){return arguments.length?(n=null==t?null:[[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]],r):n&&[[n[0][0],n[0][1]],[n[1][0],n[1][1]]]},r.size=function(t){return arguments.length?(n=null==t?null:[[0,0],[+t[0],+t[1]]],r):n&&[n[1][0]-n[0][0],n[1][1]-n[0][1]]},r},pw=function(t){return function(){return t}};function gw(t,e,n){this.target=t,this.type=e,this.transform=n}function yw(t,e,n){this.k=t,this.x=e,this.y=n}yw.prototype={constructor:yw,scale:function(t){return 1===t?this:new yw(this.k*t,this.x,this.y)},translate:function(t,e){return 0===t&0===e?this:new yw(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var vw=new yw(1,0,0);function mw(t){for(;!t.__zoom;)if(!(t=t.parentNode))return vw;return t.__zoom}function bw(){ce.stopImmediatePropagation()}mw.prototype=yw.prototype;var xw=function(){ce.preventDefault(),ce.stopImmediatePropagation()};function _w(){return!ce.ctrlKey&&!ce.button}function kw(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function ww(){return this.__zoom||vw}function Ew(){return-ce.deltaY*(1===ce.deltaMode?.05:ce.deltaMode?1:.002)}function Tw(){return navigator.maxTouchPoints||"ontouchstart"in this}function Cw(t,e,n){var r=t.invertX(e[0][0])-n[0][0],i=t.invertX(e[1][0])-n[1][0],a=t.invertY(e[0][1])-n[0][1],o=t.invertY(e[1][1])-n[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),o>a?(a+o)/2:Math.min(0,a)||Math.max(0,o))}var Aw=function(){var t,e,n=_w,r=kw,i=Cw,a=Ew,o=Tw,s=[0,1/0],c=[[-1/0,-1/0],[1/0,1/0]],u=250,l=fp,h=lt("start","zoom","end"),f=0;function d(t){t.property("__zoom",ww).on("wheel.zoom",x).on("mousedown.zoom",_).on("dblclick.zoom",k).filter(o).on("touchstart.zoom",w).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",T).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function p(t,e){return(e=Math.max(s[0],Math.min(s[1],e)))===t.k?t:new yw(e,t.x,t.y)}function g(t,e,n){var r=e[0]-n[0]*t.k,i=e[1]-n[1]*t.k;return r===t.x&&i===t.y?t:new yw(t.k,r,i)}function y(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function v(t,e,n){t.on("start.zoom",(function(){m(this,arguments).start()})).on("interrupt.zoom end.zoom",(function(){m(this,arguments).end()})).tween("zoom",(function(){var t=this,i=arguments,a=m(t,i),o=r.apply(t,i),s=null==n?y(o):"function"==typeof n?n.apply(t,i):n,c=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),u=t.__zoom,h="function"==typeof e?e.apply(t,i):e,f=l(u.invert(s).concat(c/u.k),h.invert(s).concat(c/h.k));return function(t){if(1===t)t=h;else{var e=f(t),n=c/e[2];t=new yw(n,s[0]-e[0]*n,s[1]-e[1]*n)}a.zoom(null,t)}}))}function m(t,e,n){return!n&&t.__zooming||new b(t,e)}function b(t,e){this.that=t,this.args=e,this.active=0,this.extent=r.apply(t,e),this.taps=0}function x(){if(n.apply(this,arguments)){var t=m(this,arguments),e=this.__zoom,r=Math.max(s[0],Math.min(s[1],e.k*Math.pow(2,a.apply(this,arguments)))),o=Nn(this);if(t.wheel)t.mouse[0][0]===o[0]&&t.mouse[0][1]===o[1]||(t.mouse[1]=e.invert(t.mouse[0]=o)),clearTimeout(t.wheel);else{if(e.k===r)return;t.mouse=[o,e.invert(o)],or(this),t.start()}xw(),t.wheel=setTimeout(u,150),t.zoom("mouse",i(g(p(e,r),t.mouse[0],t.mouse[1]),t.extent,c))}function u(){t.wheel=null,t.end()}}function _(){if(!e&&n.apply(this,arguments)){var t=m(this,arguments,!0),r=ke(ce.view).on("mousemove.zoom",u,!0).on("mouseup.zoom",l,!0),a=Nn(this),o=ce.clientX,s=ce.clientY;Te(ce.view),bw(),t.mouse=[a,this.__zoom.invert(a)],or(this),t.start()}function u(){if(xw(),!t.moved){var e=ce.clientX-o,n=ce.clientY-s;t.moved=e*e+n*n>f}t.zoom("mouse",i(g(t.that.__zoom,t.mouse[0]=Nn(t.that),t.mouse[1]),t.extent,c))}function l(){r.on("mousemove.zoom mouseup.zoom",null),Ce(ce.view,t.moved),xw(),t.end()}}function k(){if(n.apply(this,arguments)){var t=this.__zoom,e=Nn(this),a=t.invert(e),o=t.k*(ce.shiftKey?.5:2),s=i(g(p(t,o),e,a),r.apply(this,arguments),c);xw(),u>0?ke(this).transition().duration(u).call(v,s,e):ke(this).call(d.transform,s)}}function w(){if(n.apply(this,arguments)){var e,r,i,a,o=ce.touches,s=o.length,c=m(this,arguments,ce.changedTouches.length===s);for(bw(),r=0;r<s;++r)i=o[r],a=[a=Dn(this,o,i.identifier),this.__zoom.invert(a),i.identifier],c.touch0?c.touch1||c.touch0[2]===a[2]||(c.touch1=a,c.taps=0):(c.touch0=a,e=!0,c.taps=1+!!t);t&&(t=clearTimeout(t)),e&&(c.taps<2&&(t=setTimeout((function(){t=null}),500)),or(this),c.start())}}function E(){if(this.__zooming){var e,n,r,a,o=m(this,arguments),s=ce.changedTouches,u=s.length;for(xw(),t&&(t=clearTimeout(t)),o.taps=0,e=0;e<u;++e)n=s[e],r=Dn(this,s,n.identifier),o.touch0&&o.touch0[2]===n.identifier?o.touch0[0]=r:o.touch1&&o.touch1[2]===n.identifier&&(o.touch1[0]=r);if(n=o.that.__zoom,o.touch1){var l=o.touch0[0],h=o.touch0[1],f=o.touch1[0],d=o.touch1[1],y=(y=f[0]-l[0])*y+(y=f[1]-l[1])*y,v=(v=d[0]-h[0])*v+(v=d[1]-h[1])*v;n=p(n,Math.sqrt(y/v)),r=[(l[0]+f[0])/2,(l[1]+f[1])/2],a=[(h[0]+d[0])/2,(h[1]+d[1])/2]}else{if(!o.touch0)return;r=o.touch0[0],a=o.touch0[1]}o.zoom("touch",i(g(n,r,a),o.extent,c))}}function T(){if(this.__zooming){var t,n,r=m(this,arguments),i=ce.changedTouches,a=i.length;for(bw(),e&&clearTimeout(e),e=setTimeout((function(){e=null}),500),t=0;t<a;++t)n=i[t],r.touch0&&r.touch0[2]===n.identifier?delete r.touch0:r.touch1&&r.touch1[2]===n.identifier&&delete r.touch1;if(r.touch1&&!r.touch0&&(r.touch0=r.touch1,delete r.touch1),r.touch0)r.touch0[1]=this.__zoom.invert(r.touch0[0]);else if(r.end(),2===r.taps){var o=ke(this).on("dblclick.zoom");o&&o.apply(this,arguments)}}}return d.transform=function(t,e,n){var r=t.selection?t.selection():t;r.property("__zoom",ww),t!==r?v(t,e,n):r.interrupt().each((function(){m(this,arguments).start().zoom(null,"function"==typeof e?e.apply(this,arguments):e).end()}))},d.scaleBy=function(t,e,n){d.scaleTo(t,(function(){var t=this.__zoom.k,n="function"==typeof e?e.apply(this,arguments):e;return t*n}),n)},d.scaleTo=function(t,e,n){d.transform(t,(function(){var t=r.apply(this,arguments),a=this.__zoom,o=null==n?y(t):"function"==typeof n?n.apply(this,arguments):n,s=a.invert(o),u="function"==typeof e?e.apply(this,arguments):e;return i(g(p(a,u),o,s),t,c)}),n)},d.translateBy=function(t,e,n){d.transform(t,(function(){return i(this.__zoom.translate("function"==typeof e?e.apply(this,arguments):e,"function"==typeof n?n.apply(this,arguments):n),r.apply(this,arguments),c)}))},d.translateTo=function(t,e,n,a){d.transform(t,(function(){var t=r.apply(this,arguments),o=this.__zoom,s=null==a?y(t):"function"==typeof a?a.apply(this,arguments):a;return i(vw.translate(s[0],s[1]).scale(o.k).translate("function"==typeof e?-e.apply(this,arguments):-e,"function"==typeof n?-n.apply(this,arguments):-n),t,c)}),a)},b.prototype={start:function(){return 1==++this.active&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(t,e){return this.mouse&&"mouse"!==t&&(this.mouse[1]=e.invert(this.mouse[0])),this.touch0&&"touch"!==t&&(this.touch0[1]=e.invert(this.touch0[0])),this.touch1&&"touch"!==t&&(this.touch1[1]=e.invert(this.touch1[0])),this.that.__zoom=e,this.emit("zoom"),this},end:function(){return 0==--this.active&&(delete this.that.__zooming,this.emit("end")),this},emit:function(t){pe(new gw(d,t,this.that.__zoom),h.apply,h,[t,this.that,this.args])}},d.wheelDelta=function(t){return arguments.length?(a="function"==typeof t?t:pw(+t),d):a},d.filter=function(t){return arguments.length?(n="function"==typeof t?t:pw(!!t),d):n},d.touchable=function(t){return arguments.length?(o="function"==typeof t?t:pw(!!t),d):o},d.extent=function(t){return arguments.length?(r="function"==typeof t?t:pw([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),d):r},d.scaleExtent=function(t){return arguments.length?(s[0]=+t[0],s[1]=+t[1],d):[s[0],s[1]]},d.translateExtent=function(t){return arguments.length?(c[0][0]=+t[0][0],c[1][0]=+t[1][0],c[0][1]=+t[0][1],c[1][1]=+t[1][1],d):[[c[0][0],c[0][1]],[c[1][0],c[1][1]]]},d.constrain=function(t){return arguments.length?(i=t,d):i},d.duration=function(t){return arguments.length?(u=+t,d):u},d.interpolate=function(t){return arguments.length?(l=t,d):l},d.on=function(){var t=h.on.apply(h,arguments);return t===h?d:t},d.clickDistance=function(t){return arguments.length?(f=(t=+t)*t,d):Math.sqrt(f)},d};n.d(e,"version",(function(){return"5.15.0"})),n.d(e,"bisect",(function(){return c})),n.d(e,"bisectRight",(function(){return o})),n.d(e,"bisectLeft",(function(){return s})),n.d(e,"ascending",(function(){return r})),n.d(e,"bisector",(function(){return i})),n.d(e,"cross",(function(){return h})),n.d(e,"descending",(function(){return f})),n.d(e,"deviation",(function(){return g})),n.d(e,"extent",(function(){return y})),n.d(e,"histogram",(function(){return O})),n.d(e,"thresholdFreedmanDiaconis",(function(){return N})),n.d(e,"thresholdScott",(function(){return B})),n.d(e,"thresholdSturges",(function(){return M})),n.d(e,"max",(function(){return L})),n.d(e,"mean",(function(){return P})),n.d(e,"median",(function(){return I})),n.d(e,"merge",(function(){return F})),n.d(e,"min",(function(){return j})),n.d(e,"pairs",(function(){return u})),n.d(e,"permute",(function(){return R})),n.d(e,"quantile",(function(){return D})),n.d(e,"range",(function(){return k})),n.d(e,"scan",(function(){return Y})),n.d(e,"shuffle",(function(){return z})),n.d(e,"sum",(function(){return U})),n.d(e,"ticks",(function(){return C})),n.d(e,"tickIncrement",(function(){return A})),n.d(e,"tickStep",(function(){return S})),n.d(e,"transpose",(function(){return $})),n.d(e,"variance",(function(){return p})),n.d(e,"zip",(function(){return H})),n.d(e,"axisTop",(function(){return tt})),n.d(e,"axisRight",(function(){return et})),n.d(e,"axisBottom",(function(){return nt})),n.d(e,"axisLeft",(function(){return rt})),n.d(e,"brush",(function(){return Ti})),n.d(e,"brushX",(function(){return wi})),n.d(e,"brushY",(function(){return Ei})),n.d(e,"brushSelection",(function(){return ki})),n.d(e,"chord",(function(){return Li})),n.d(e,"ribbon",(function(){return qi})),n.d(e,"nest",(function(){return Ki})),n.d(e,"set",(function(){return oa})),n.d(e,"map",(function(){return Ji})),n.d(e,"keys",(function(){return sa})),n.d(e,"values",(function(){return ca})),n.d(e,"entries",(function(){return ua})),n.d(e,"color",(function(){return $e})),n.d(e,"rgb",(function(){return Ge})),n.d(e,"hsl",(function(){return tn})),n.d(e,"lab",(function(){return pa})),n.d(e,"hcl",(function(){return ka})),n.d(e,"lch",(function(){return _a})),n.d(e,"gray",(function(){return da})),n.d(e,"cubehelix",(function(){return Oa})),n.d(e,"contours",(function(){return Ya})),n.d(e,"contourDensity",(function(){return Va})),n.d(e,"dispatch",(function(){return lt})),n.d(e,"drag",(function(){return Qa})),n.d(e,"dragDisable",(function(){return Te})),n.d(e,"dragEnable",(function(){return Ce})),n.d(e,"dsvFormat",(function(){return oo})),n.d(e,"csvParse",(function(){return co})),n.d(e,"csvParseRows",(function(){return uo})),n.d(e,"csvFormat",(function(){return lo})),n.d(e,"csvFormatBody",(function(){return ho})),n.d(e,"csvFormatRows",(function(){return fo})),n.d(e,"csvFormatRow",(function(){return po})),n.d(e,"csvFormatValue",(function(){return go})),n.d(e,"tsvParse",(function(){return vo})),n.d(e,"tsvParseRows",(function(){return mo})),n.d(e,"tsvFormat",(function(){return bo})),n.d(e,"tsvFormatBody",(function(){return xo})),n.d(e,"tsvFormatRows",(function(){return _o})),n.d(e,"tsvFormatRow",(function(){return ko})),n.d(e,"tsvFormatValue",(function(){return wo})),n.d(e,"autoType",(function(){return Eo})),n.d(e,"easeLinear",(function(){return Co})),n.d(e,"easeQuad",(function(){return Mo})),n.d(e,"easeQuadIn",(function(){return Ao})),n.d(e,"easeQuadOut",(function(){return So})),n.d(e,"easeQuadInOut",(function(){return Mo})),n.d(e,"easeCubic",(function(){return Vr})),n.d(e,"easeCubicIn",(function(){return Wr})),n.d(e,"easeCubicOut",(function(){return Hr})),n.d(e,"easeCubicInOut",(function(){return Vr})),n.d(e,"easePoly",(function(){return No})),n.d(e,"easePolyIn",(function(){return Oo})),n.d(e,"easePolyOut",(function(){return Do})),n.d(e,"easePolyInOut",(function(){return No})),n.d(e,"easeSin",(function(){return Fo})),n.d(e,"easeSinIn",(function(){return Po})),n.d(e,"easeSinOut",(function(){return Io})),n.d(e,"easeSinInOut",(function(){return Fo})),n.d(e,"easeExp",(function(){return Yo})),n.d(e,"easeExpIn",(function(){return jo})),n.d(e,"easeExpOut",(function(){return Ro})),n.d(e,"easeExpInOut",(function(){return Yo})),n.d(e,"easeCircle",(function(){return $o})),n.d(e,"easeCircleIn",(function(){return zo})),n.d(e,"easeCircleOut",(function(){return Uo})),n.d(e,"easeCircleInOut",(function(){return $o})),n.d(e,"easeBounce",(function(){return Ho})),n.d(e,"easeBounceIn",(function(){return Wo})),n.d(e,"easeBounceOut",(function(){return Ho})),n.d(e,"easeBounceInOut",(function(){return Vo})),n.d(e,"easeBack",(function(){return Xo})),n.d(e,"easeBackIn",(function(){return Go})),n.d(e,"easeBackOut",(function(){return qo})),n.d(e,"easeBackInOut",(function(){return Xo})),n.d(e,"easeElastic",(function(){return Ko})),n.d(e,"easeElasticIn",(function(){return Jo})),n.d(e,"easeElasticOut",(function(){return Ko})),n.d(e,"easeElasticInOut",(function(){return Qo})),n.d(e,"blob",(function(){return es})),n.d(e,"buffer",(function(){return rs})),n.d(e,"dsv",(function(){return ss})),n.d(e,"csv",(function(){return cs})),n.d(e,"tsv",(function(){return us})),n.d(e,"image",(function(){return ls})),n.d(e,"json",(function(){return fs})),n.d(e,"text",(function(){return as})),n.d(e,"xml",(function(){return ps})),n.d(e,"html",(function(){return gs})),n.d(e,"svg",(function(){return ys})),n.d(e,"forceCenter",(function(){return vs})),n.d(e,"forceCollide",(function(){return Os})),n.d(e,"forceLink",(function(){return Bs})),n.d(e,"forceManyBody",(function(){return js})),n.d(e,"forceRadial",(function(){return Rs})),n.d(e,"forceSimulation",(function(){return Fs})),n.d(e,"forceX",(function(){return Ys})),n.d(e,"forceY",(function(){return zs})),n.d(e,"formatDefaultLocale",(function(){return rc})),n.d(e,"format",(function(){return Xs})),n.d(e,"formatPrefix",(function(){return Zs})),n.d(e,"formatLocale",(function(){return nc})),n.d(e,"formatSpecifier",(function(){return Hs})),n.d(e,"FormatSpecifier",(function(){return Vs})),n.d(e,"precisionFixed",(function(){return ic})),n.d(e,"precisionPrefix",(function(){return ac})),n.d(e,"precisionRound",(function(){return oc})),n.d(e,"geoArea",(function(){return Jc})),n.d(e,"geoBounds",(function(){return $u})),n.d(e,"geoCentroid",(function(){return el})),n.d(e,"geoCircle",(function(){return fl})),n.d(e,"geoClipAntimeridian",(function(){return El})),n.d(e,"geoClipCircle",(function(){return Tl})),n.d(e,"geoClipExtent",(function(){return Ol})),n.d(e,"geoClipRectangle",(function(){return Cl})),n.d(e,"geoContains",(function(){return ql})),n.d(e,"geoDistance",(function(){return Rl})),n.d(e,"geoGraticule",(function(){return Jl})),n.d(e,"geoGraticule10",(function(){return Kl})),n.d(e,"geoInterpolate",(function(){return rh})),n.d(e,"geoLength",(function(){return Il})),n.d(e,"geoPath",(function(){return ef})),n.d(e,"geoAlbers",(function(){return _f})),n.d(e,"geoAlbersUsa",(function(){return kf})),n.d(e,"geoAzimuthalEqualArea",(function(){return Cf})),n.d(e,"geoAzimuthalEqualAreaRaw",(function(){return Tf})),n.d(e,"geoAzimuthalEquidistant",(function(){return Sf})),n.d(e,"geoAzimuthalEquidistantRaw",(function(){return Af})),n.d(e,"geoConicConformal",(function(){return Lf})),n.d(e,"geoConicConformalRaw",(function(){return Bf})),n.d(e,"geoConicEqualArea",(function(){return xf})),n.d(e,"geoConicEqualAreaRaw",(function(){return bf})),n.d(e,"geoConicEquidistant",(function(){return jf})),n.d(e,"geoConicEquidistantRaw",(function(){return Ff})),n.d(e,"geoEqualEarth",(function(){return Hf})),n.d(e,"geoEqualEarthRaw",(function(){return Wf})),n.d(e,"geoEquirectangular",(function(){return If})),n.d(e,"geoEquirectangularRaw",(function(){return Pf})),n.d(e,"geoGnomonic",(function(){return Gf})),n.d(e,"geoGnomonicRaw",(function(){return Vf})),n.d(e,"geoIdentity",(function(){return Xf})),n.d(e,"geoProjection",(function(){return yf})),n.d(e,"geoProjectionMutator",(function(){return vf})),n.d(e,"geoMercator",(function(){return Of})),n.d(e,"geoMercatorRaw",(function(){return Mf})),n.d(e,"geoNaturalEarth1",(function(){return Jf})),n.d(e,"geoNaturalEarth1Raw",(function(){return Zf})),n.d(e,"geoOrthographic",(function(){return Qf})),n.d(e,"geoOrthographicRaw",(function(){return Kf})),n.d(e,"geoStereographic",(function(){return ed})),n.d(e,"geoStereographicRaw",(function(){return td})),n.d(e,"geoTransverseMercator",(function(){return rd})),n.d(e,"geoTransverseMercatorRaw",(function(){return nd})),n.d(e,"geoRotation",(function(){return ul})),n.d(e,"geoStream",(function(){return $c})),n.d(e,"geoTransform",(function(){return nf})),n.d(e,"cluster",(function(){return sd})),n.d(e,"hierarchy",(function(){return ud})),n.d(e,"pack",(function(){return Ld})),n.d(e,"packSiblings",(function(){return Sd})),n.d(e,"packEnclose",(function(){return gd})),n.d(e,"partition",(function(){return Yd})),n.d(e,"stratify",(function(){return Hd})),n.d(e,"tree",(function(){return Kd})),n.d(e,"treemap",(function(){return rp})),n.d(e,"treemapBinary",(function(){return ip})),n.d(e,"treemapDice",(function(){return Rd})),n.d(e,"treemapSlice",(function(){return Qd})),n.d(e,"treemapSliceDice",(function(){return ap})),n.d(e,"treemapSquarify",(function(){return np})),n.d(e,"treemapResquarify",(function(){return op})),n.d(e,"interpolate",(function(){return Sn})),n.d(e,"interpolateArray",(function(){return mn})),n.d(e,"interpolateBasis",(function(){return an})),n.d(e,"interpolateBasisClosed",(function(){return on})),n.d(e,"interpolateDate",(function(){return xn})),n.d(e,"interpolateDiscrete",(function(){return sp})),n.d(e,"interpolateHue",(function(){return cp})),n.d(e,"interpolateNumber",(function(){return _n})),n.d(e,"interpolateNumberArray",(function(){return yn})),n.d(e,"interpolateObject",(function(){return kn})),n.d(e,"interpolateRound",(function(){return up})),n.d(e,"interpolateString",(function(){return An})),n.d(e,"interpolateTransformCss",(function(){return hr})),n.d(e,"interpolateTransformSvg",(function(){return fr})),n.d(e,"interpolateZoom",(function(){return fp})),n.d(e,"interpolateRgb",(function(){return fn})),n.d(e,"interpolateRgbBasis",(function(){return pn})),n.d(e,"interpolateRgbBasisClosed",(function(){return gn})),n.d(e,"interpolateHsl",(function(){return pp})),n.d(e,"interpolateHslLong",(function(){return gp})),n.d(e,"interpolateLab",(function(){return yp})),n.d(e,"interpolateHcl",(function(){return mp})),n.d(e,"interpolateHclLong",(function(){return bp})),n.d(e,"interpolateCubehelix",(function(){return _p})),n.d(e,"interpolateCubehelixLong",(function(){return kp})),n.d(e,"piecewise",(function(){return wp})),n.d(e,"quantize",(function(){return Ep})),n.d(e,"path",(function(){return Ui})),n.d(e,"polygonArea",(function(){return Tp})),n.d(e,"polygonCentroid",(function(){return Cp})),n.d(e,"polygonHull",(function(){return Mp})),n.d(e,"polygonContains",(function(){return Op})),n.d(e,"polygonLength",(function(){return Dp})),n.d(e,"quadtree",(function(){return Es})),n.d(e,"randomUniform",(function(){return Bp})),n.d(e,"randomNormal",(function(){return Lp})),n.d(e,"randomLogNormal",(function(){return Pp})),n.d(e,"randomBates",(function(){return Fp})),n.d(e,"randomIrwinHall",(function(){return Ip})),n.d(e,"randomExponential",(function(){return jp})),n.d(e,"scaleBand",(function(){return Vp})),n.d(e,"scalePoint",(function(){return qp})),n.d(e,"scaleIdentity",(function(){return cg})),n.d(e,"scaleLinear",(function(){return sg})),n.d(e,"scaleLog",(function(){return vg})),n.d(e,"scaleSymlog",(function(){return _g})),n.d(e,"scaleOrdinal",(function(){return Hp})),n.d(e,"scaleImplicit",(function(){return Wp})),n.d(e,"scalePow",(function(){return Cg})),n.d(e,"scaleSqrt",(function(){return Ag})),n.d(e,"scaleQuantile",(function(){return Sg})),n.d(e,"scaleQuantize",(function(){return Mg})),n.d(e,"scaleThreshold",(function(){return Og})),n.d(e,"scaleTime",(function(){return sm})),n.d(e,"scaleUtc",(function(){return vm})),n.d(e,"scaleSequential",(function(){return xm})),n.d(e,"scaleSequentialLog",(function(){return _m})),n.d(e,"scaleSequentialPow",(function(){return wm})),n.d(e,"scaleSequentialSqrt",(function(){return Em})),n.d(e,"scaleSequentialSymlog",(function(){return km})),n.d(e,"scaleSequentialQuantile",(function(){return Tm})),n.d(e,"scaleDiverging",(function(){return Am})),n.d(e,"scaleDivergingLog",(function(){return Sm})),n.d(e,"scaleDivergingPow",(function(){return Om})),n.d(e,"scaleDivergingSqrt",(function(){return Dm})),n.d(e,"scaleDivergingSymlog",(function(){return Mm})),n.d(e,"tickFormat",(function(){return ag})),n.d(e,"schemeCategory10",(function(){return Bm})),n.d(e,"schemeAccent",(function(){return Lm})),n.d(e,"schemeDark2",(function(){return Pm})),n.d(e,"schemePaired",(function(){return Im})),n.d(e,"schemePastel1",(function(){return Fm})),n.d(e,"schemePastel2",(function(){return jm})),n.d(e,"schemeSet1",(function(){return Rm})),n.d(e,"schemeSet2",(function(){return Ym})),n.d(e,"schemeSet3",(function(){return zm})),n.d(e,"schemeTableau10",(function(){return Um})),n.d(e,"interpolateBrBG",(function(){return Hm})),n.d(e,"schemeBrBG",(function(){return Wm})),n.d(e,"interpolatePRGn",(function(){return Gm})),n.d(e,"schemePRGn",(function(){return Vm})),n.d(e,"interpolatePiYG",(function(){return Xm})),n.d(e,"schemePiYG",(function(){return qm})),n.d(e,"interpolatePuOr",(function(){return Jm})),n.d(e,"schemePuOr",(function(){return Zm})),n.d(e,"interpolateRdBu",(function(){return Qm})),n.d(e,"schemeRdBu",(function(){return Km})),n.d(e,"interpolateRdGy",(function(){return eb})),n.d(e,"schemeRdGy",(function(){return tb})),n.d(e,"interpolateRdYlBu",(function(){return rb})),n.d(e,"schemeRdYlBu",(function(){return nb})),n.d(e,"interpolateRdYlGn",(function(){return ab})),n.d(e,"schemeRdYlGn",(function(){return ib})),n.d(e,"interpolateSpectral",(function(){return sb})),n.d(e,"schemeSpectral",(function(){return ob})),n.d(e,"interpolateBuGn",(function(){return ub})),n.d(e,"schemeBuGn",(function(){return cb})),n.d(e,"interpolateBuPu",(function(){return hb})),n.d(e,"schemeBuPu",(function(){return lb})),n.d(e,"interpolateGnBu",(function(){return db})),n.d(e,"schemeGnBu",(function(){return fb})),n.d(e,"interpolateOrRd",(function(){return gb})),n.d(e,"schemeOrRd",(function(){return pb})),n.d(e,"interpolatePuBuGn",(function(){return vb})),n.d(e,"schemePuBuGn",(function(){return yb})),n.d(e,"interpolatePuBu",(function(){return bb})),n.d(e,"schemePuBu",(function(){return mb})),n.d(e,"interpolatePuRd",(function(){return _b})),n.d(e,"schemePuRd",(function(){return xb})),n.d(e,"interpolateRdPu",(function(){return wb})),n.d(e,"schemeRdPu",(function(){return kb})),n.d(e,"interpolateYlGnBu",(function(){return Tb})),n.d(e,"schemeYlGnBu",(function(){return Eb})),n.d(e,"interpolateYlGn",(function(){return Ab})),n.d(e,"schemeYlGn",(function(){return Cb})),n.d(e,"interpolateYlOrBr",(function(){return Mb})),n.d(e,"schemeYlOrBr",(function(){return Sb})),n.d(e,"interpolateYlOrRd",(function(){return Db})),n.d(e,"schemeYlOrRd",(function(){return Ob})),n.d(e,"interpolateBlues",(function(){return Bb})),n.d(e,"schemeBlues",(function(){return Nb})),n.d(e,"interpolateGreens",(function(){return Pb})),n.d(e,"schemeGreens",(function(){return Lb})),n.d(e,"interpolateGreys",(function(){return Fb})),n.d(e,"schemeGreys",(function(){return Ib})),n.d(e,"interpolatePurples",(function(){return Rb})),n.d(e,"schemePurples",(function(){return jb})),n.d(e,"interpolateReds",(function(){return zb})),n.d(e,"schemeReds",(function(){return Yb})),n.d(e,"interpolateOranges",(function(){return $b})),n.d(e,"schemeOranges",(function(){return Ub})),n.d(e,"interpolateCividis",(function(){return Wb})),n.d(e,"interpolateCubehelixDefault",(function(){return Hb})),n.d(e,"interpolateRainbow",(function(){return Xb})),n.d(e,"interpolateWarm",(function(){return Vb})),n.d(e,"interpolateCool",(function(){return Gb})),n.d(e,"interpolateSinebow",(function(){return Qb})),n.d(e,"interpolateTurbo",(function(){return tx})),n.d(e,"interpolateViridis",(function(){return nx})),n.d(e,"interpolateMagma",(function(){return rx})),n.d(e,"interpolateInferno",(function(){return ix})),n.d(e,"interpolatePlasma",(function(){return ax})),n.d(e,"create",(function(){return ox})),n.d(e,"creator",(function(){return ne})),n.d(e,"local",(function(){return cx})),n.d(e,"matcher",(function(){return gt})),n.d(e,"mouse",(function(){return Nn})),n.d(e,"namespace",(function(){return wt})),n.d(e,"namespaces",(function(){return kt})),n.d(e,"clientPoint",(function(){return On})),n.d(e,"select",(function(){return ke})),n.d(e,"selectAll",(function(){return lx})),n.d(e,"selection",(function(){return _e})),n.d(e,"selector",(function(){return ft})),n.d(e,"selectorAll",(function(){return pt})),n.d(e,"style",(function(){return Lt})),n.d(e,"touch",(function(){return Dn})),n.d(e,"touches",(function(){return hx})),n.d(e,"window",(function(){return Ot})),n.d(e,"event",(function(){return ce})),n.d(e,"customEvent",(function(){return pe})),n.d(e,"arc",(function(){return Nx})),n.d(e,"area",(function(){return jx})),n.d(e,"line",(function(){return Fx})),n.d(e,"pie",(function(){return zx})),n.d(e,"areaRadial",(function(){return Gx})),n.d(e,"radialArea",(function(){return Gx})),n.d(e,"lineRadial",(function(){return Vx})),n.d(e,"radialLine",(function(){return Vx})),n.d(e,"pointRadial",(function(){return qx})),n.d(e,"linkHorizontal",(function(){return n_})),n.d(e,"linkVertical",(function(){return r_})),n.d(e,"linkRadial",(function(){return i_})),n.d(e,"symbol",(function(){return k_})),n.d(e,"symbols",(function(){return __})),n.d(e,"symbolCircle",(function(){return a_})),n.d(e,"symbolCross",(function(){return o_})),n.d(e,"symbolDiamond",(function(){return u_})),n.d(e,"symbolSquare",(function(){return p_})),n.d(e,"symbolStar",(function(){return d_})),n.d(e,"symbolTriangle",(function(){return y_})),n.d(e,"symbolWye",(function(){return x_})),n.d(e,"curveBasisClosed",(function(){return S_})),n.d(e,"curveBasisOpen",(function(){return O_})),n.d(e,"curveBasis",(function(){return C_})),n.d(e,"curveBundle",(function(){return N_})),n.d(e,"curveCardinalClosed",(function(){return F_})),n.d(e,"curveCardinalOpen",(function(){return R_})),n.d(e,"curveCardinal",(function(){return P_})),n.d(e,"curveCatmullRomClosed",(function(){return W_})),n.d(e,"curveCatmullRomOpen",(function(){return V_})),n.d(e,"curveCatmullRom",(function(){return U_})),n.d(e,"curveLinearClosed",(function(){return q_})),n.d(e,"curveLinear",(function(){return Lx})),n.d(e,"curveMonotoneX",(function(){return nk})),n.d(e,"curveMonotoneY",(function(){return rk})),n.d(e,"curveNatural",(function(){return ok})),n.d(e,"curveStep",(function(){return ck})),n.d(e,"curveStepAfter",(function(){return lk})),n.d(e,"curveStepBefore",(function(){return uk})),n.d(e,"stack",(function(){return pk})),n.d(e,"stackOffsetExpand",(function(){return gk})),n.d(e,"stackOffsetDiverging",(function(){return yk})),n.d(e,"stackOffsetNone",(function(){return hk})),n.d(e,"stackOffsetSilhouette",(function(){return vk})),n.d(e,"stackOffsetWiggle",(function(){return mk})),n.d(e,"stackOrderAppearance",(function(){return bk})),n.d(e,"stackOrderAscending",(function(){return _k})),n.d(e,"stackOrderDescending",(function(){return wk})),n.d(e,"stackOrderInsideOut",(function(){return Ek})),n.d(e,"stackOrderNone",(function(){return fk})),n.d(e,"stackOrderReverse",(function(){return Tk})),n.d(e,"timeInterval",(function(){return Bg})),n.d(e,"timeMillisecond",(function(){return py})),n.d(e,"timeMilliseconds",(function(){return gy})),n.d(e,"utcMillisecond",(function(){return py})),n.d(e,"utcMilliseconds",(function(){return gy})),n.d(e,"timeSecond",(function(){return hy})),n.d(e,"timeSeconds",(function(){return fy})),n.d(e,"utcSecond",(function(){return hy})),n.d(e,"utcSeconds",(function(){return fy})),n.d(e,"timeMinute",(function(){return cy})),n.d(e,"timeMinutes",(function(){return uy})),n.d(e,"timeHour",(function(){return ay})),n.d(e,"timeHours",(function(){return oy})),n.d(e,"timeDay",(function(){return ny})),n.d(e,"timeDays",(function(){return ry})),n.d(e,"timeWeek",(function(){return zg})),n.d(e,"timeWeeks",(function(){return qg})),n.d(e,"timeSunday",(function(){return zg})),n.d(e,"timeSundays",(function(){return qg})),n.d(e,"timeMonday",(function(){return Ug})),n.d(e,"timeMondays",(function(){return Xg})),n.d(e,"timeTuesday",(function(){return $g})),n.d(e,"timeTuesdays",(function(){return Zg})),n.d(e,"timeWednesday",(function(){return Wg})),n.d(e,"timeWednesdays",(function(){return Jg})),n.d(e,"timeThursday",(function(){return Hg})),n.d(e,"timeThursdays",(function(){return Kg})),n.d(e,"timeFriday",(function(){return Vg})),n.d(e,"timeFridays",(function(){return Qg})),n.d(e,"timeSaturday",(function(){return Gg})),n.d(e,"timeSaturdays",(function(){return ty})),n.d(e,"timeMonth",(function(){return jg})),n.d(e,"timeMonths",(function(){return Rg})),n.d(e,"timeYear",(function(){return Pg})),n.d(e,"timeYears",(function(){return Ig})),n.d(e,"utcMinute",(function(){return gm})),n.d(e,"utcMinutes",(function(){return ym})),n.d(e,"utcHour",(function(){return fm})),n.d(e,"utcHours",(function(){return dm})),n.d(e,"utcDay",(function(){return Ny})),n.d(e,"utcDays",(function(){return By})),n.d(e,"utcWeek",(function(){return vy})),n.d(e,"utcWeeks",(function(){return Ey})),n.d(e,"utcSunday",(function(){return vy})),n.d(e,"utcSundays",(function(){return Ey})),n.d(e,"utcMonday",(function(){return my})),n.d(e,"utcMondays",(function(){return Ty})),n.d(e,"utcTuesday",(function(){return by})),n.d(e,"utcTuesdays",(function(){return Cy})),n.d(e,"utcWednesday",(function(){return xy})),n.d(e,"utcWednesdays",(function(){return Ay})),n.d(e,"utcThursday",(function(){return _y})),n.d(e,"utcThursdays",(function(){return Sy})),n.d(e,"utcFriday",(function(){return ky})),n.d(e,"utcFridays",(function(){return My})),n.d(e,"utcSaturday",(function(){return wy})),n.d(e,"utcSaturdays",(function(){return Oy})),n.d(e,"utcMonth",(function(){return um})),n.d(e,"utcMonths",(function(){return lm})),n.d(e,"utcYear",(function(){return Py})),n.d(e,"utcYears",(function(){return Iy})),n.d(e,"timeFormatDefaultLocale",(function(){return rm})),n.d(e,"timeFormat",(function(){return Uy})),n.d(e,"timeParse",(function(){return $y})),n.d(e,"utcFormat",(function(){return Wy})),n.d(e,"utcParse",(function(){return Hy})),n.d(e,"timeFormatLocale",(function(){return Yy})),n.d(e,"isoFormat",(function(){return Ck})),n.d(e,"isoParse",(function(){return Ak})),n.d(e,"now",(function(){return zn})),n.d(e,"timer",(function(){return Wn})),n.d(e,"timerFlush",(function(){return Hn})),n.d(e,"timeout",(function(){return Xn})),n.d(e,"interval",(function(){return Sk})),n.d(e,"transition",(function(){return zr})),n.d(e,"active",(function(){return Zr})),n.d(e,"interrupt",(function(){return or})),n.d(e,"voronoi",(function(){return dw})),n.d(e,"zoom",(function(){return Aw})),n.d(e,"zoomTransform",(function(){return mw})),n.d(e,"zoomIdentity",(function(){return vw}))},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),function(t){for(var n in t)e.hasOwnProperty(n)||(e[n]=t[n])}(n(172))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,18],u=[1,20],l=[1,21],h=[1,22],f=[1,24],d=[1,25],p=[1,26],g=[1,27],y=[1,28],v=[1,29],m=[1,32],b=[1,33],x=[1,36],_=[1,4,5,16,21,22,23,25,27,28,29,30,31,33,35,36,37,48,58],k=[1,44],w=[4,5,16,21,22,23,25,27,28,29,30,31,33,37,48,58],E=[4,5,16,21,22,23,25,27,28,29,30,31,33,36,37,48,58],T=[4,5,16,21,22,23,25,27,28,29,30,31,33,35,37,48,58],C=[46,47,48],A=[1,4,5,7,16,21,22,23,25,27,28,29,30,31,33,35,36,37,48,58],S={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NEWLINE:5,directive:6,SD:7,document:8,line:9,statement:10,openDirective:11,typeDirective:12,closeDirective:13,":":14,argDirective:15,participant:16,actor:17,AS:18,restOfLine:19,signal:20,autonumber:21,activate:22,deactivate:23,note_statement:24,title:25,text2:26,loop:27,end:28,rect:29,opt:30,alt:31,else_sections:32,par:33,par_sections:34,and:35,else:36,note:37,placement:38,over:39,actor_pair:40,spaceList:41,",":42,left_of:43,right_of:44,signaltype:45,"+":46,"-":47,ACTOR:48,SOLID_OPEN_ARROW:49,DOTTED_OPEN_ARROW:50,SOLID_ARROW:51,DOTTED_ARROW:52,SOLID_CROSS:53,DOTTED_CROSS:54,SOLID_POINT:55,DOTTED_POINT:56,TXT:57,open_directive:58,type_directive:59,arg_directive:60,close_directive:61,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NEWLINE",7:"SD",14:":",16:"participant",18:"AS",19:"restOfLine",21:"autonumber",22:"activate",23:"deactivate",25:"title",27:"loop",28:"end",29:"rect",30:"opt",31:"alt",33:"par",35:"and",36:"else",37:"note",39:"over",42:",",43:"left_of",44:"right_of",46:"+",47:"-",48:"ACTOR",49:"SOLID_OPEN_ARROW",50:"DOTTED_OPEN_ARROW",51:"SOLID_ARROW",52:"DOTTED_ARROW",53:"SOLID_CROSS",54:"DOTTED_CROSS",55:"SOLID_POINT",56:"DOTTED_POINT",57:"TXT",58:"open_directive",59:"type_directive",60:"arg_directive",61:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[6,4],[6,6],[10,5],[10,3],[10,2],[10,1],[10,3],[10,3],[10,2],[10,3],[10,4],[10,4],[10,4],[10,4],[10,4],[10,1],[34,1],[34,4],[32,1],[32,4],[24,4],[24,4],[41,2],[41,1],[40,3],[40,1],[38,1],[38,1],[20,5],[20,5],[20,4],[17,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[26,1],[11,1],[12,1],[15,1],[13,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.apply(a[s]),a[s];case 5:this.$=[];break;case 6:a[s-1].push(a[s]),this.$=a[s-1];break;case 7:case 8:this.$=a[s];break;case 9:this.$=[];break;case 12:a[s-3].description=r.parseMessage(a[s-1]),this.$=a[s-3];break;case 13:this.$=a[s-1];break;case 15:r.enableSequenceNumbers();break;case 16:this.$={type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]};break;case 17:this.$={type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-1]};break;case 19:this.$=[{type:"setTitle",text:a[s-1]}];break;case 20:a[s-1].unshift({type:"loopStart",loopText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.LOOP_START}),a[s-1].push({type:"loopEnd",loopText:a[s-2],signalType:r.LINETYPE.LOOP_END}),this.$=a[s-1];break;case 21:a[s-1].unshift({type:"rectStart",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_START}),a[s-1].push({type:"rectEnd",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_END}),this.$=a[s-1];break;case 22:a[s-1].unshift({type:"optStart",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_START}),a[s-1].push({type:"optEnd",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_END}),this.$=a[s-1];break;case 23:a[s-1].unshift({type:"altStart",altText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.ALT_START}),a[s-1].push({type:"altEnd",signalType:r.LINETYPE.ALT_END}),this.$=a[s-1];break;case 24:a[s-1].unshift({type:"parStart",parText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.PAR_START}),a[s-1].push({type:"parEnd",signalType:r.LINETYPE.PAR_END}),this.$=a[s-1];break;case 27:this.$=a[s-3].concat([{type:"and",parText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.PAR_AND},a[s]]);break;case 29:this.$=a[s-3].concat([{type:"else",altText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.ALT_ELSE},a[s]]);break;case 30:this.$=[a[s-1],{type:"addNote",placement:a[s-2],actor:a[s-1].actor,text:a[s]}];break;case 31:a[s-2]=[].concat(a[s-1],a[s-1]).slice(0,2),a[s-2][0]=a[s-2][0].actor,a[s-2][1]=a[s-2][1].actor,this.$=[a[s-1],{type:"addNote",placement:r.PLACEMENT.OVER,actor:a[s-2].slice(0,2),text:a[s]}];break;case 34:this.$=[a[s-2],a[s]];break;case 35:this.$=a[s];break;case 36:this.$=r.PLACEMENT.LEFTOF;break;case 37:this.$=r.PLACEMENT.RIGHTOF;break;case 38:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]}];break;case 39:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-4]}];break;case 40:this.$=[a[s-3],a[s-1],{type:"addMessage",from:a[s-3].actor,to:a[s-1].actor,signalType:a[s-2],msg:a[s]}];break;case 41:this.$={type:"addActor",actor:a[s]};break;case 42:this.$=r.LINETYPE.SOLID_OPEN;break;case 43:this.$=r.LINETYPE.DOTTED_OPEN;break;case 44:this.$=r.LINETYPE.SOLID;break;case 45:this.$=r.LINETYPE.DOTTED;break;case 46:this.$=r.LINETYPE.SOLID_CROSS;break;case 47:this.$=r.LINETYPE.DOTTED_CROSS;break;case 48:this.$=r.LINETYPE.SOLID_POINT;break;case 49:this.$=r.LINETYPE.DOTTED_POINT;break;case 50:this.$=r.parseMessage(a[s].trim().substring(1));break;case 51:r.parseDirective("%%{","open_directive");break;case 52:r.parseDirective(a[s],"type_directive");break;case 53:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 54:r.parseDirective("}%%","close_directive","sequence")}},table:[{3:1,4:e,5:n,6:4,7:r,11:6,58:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,11:6,58:i},{3:9,4:e,5:n,6:4,7:r,11:6,58:i},{3:10,4:e,5:n,6:4,7:r,11:6,58:i},t([1,4,5,16,21,22,23,25,27,29,30,31,33,37,48,58],a,{8:11}),{12:12,59:[1,13]},{59:[2,51]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,29:p,30:g,31:y,33:v,37:m,48:b,58:i},{13:34,14:[1,35],61:x},t([14,61],[2,52]),t(_,[2,6]),{6:30,10:37,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,29:p,30:g,31:y,33:v,37:m,48:b,58:i},t(_,[2,8]),t(_,[2,9]),{17:38,48:b},{5:[1,39]},t(_,[2,15]),{17:40,48:b},{17:41,48:b},{5:[1,42]},{26:43,57:k},{19:[1,45]},{19:[1,46]},{19:[1,47]},{19:[1,48]},{19:[1,49]},t(_,[2,25]),{45:50,49:[1,51],50:[1,52],51:[1,53],52:[1,54],53:[1,55],54:[1,56],55:[1,57],56:[1,58]},{38:59,39:[1,60],43:[1,61],44:[1,62]},t([5,18,42,49,50,51,52,53,54,55,56,57],[2,41]),{5:[1,63]},{15:64,60:[1,65]},{5:[2,54]},t(_,[2,7]),{5:[1,67],18:[1,66]},t(_,[2,14]),{5:[1,68]},{5:[1,69]},t(_,[2,18]),{5:[1,70]},{5:[2,50]},t(w,a,{8:71}),t(w,a,{8:72}),t(w,a,{8:73}),t(E,a,{32:74,8:75}),t(T,a,{34:76,8:77}),{17:80,46:[1,78],47:[1,79],48:b},t(C,[2,42]),t(C,[2,43]),t(C,[2,44]),t(C,[2,45]),t(C,[2,46]),t(C,[2,47]),t(C,[2,48]),t(C,[2,49]),{17:81,48:b},{17:83,40:82,48:b},{48:[2,36]},{48:[2,37]},t(A,[2,10]),{13:84,61:x},{61:[2,53]},{19:[1,85]},t(_,[2,13]),t(_,[2,16]),t(_,[2,17]),t(_,[2,19]),{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[1,86],29:p,30:g,31:y,33:v,37:m,48:b,58:i},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[1,87],29:p,30:g,31:y,33:v,37:m,48:b,58:i},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[1,88],29:p,30:g,31:y,33:v,37:m,48:b,58:i},{28:[1,89]},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[2,28],29:p,30:g,31:y,33:v,36:[1,90],37:m,48:b,58:i},{28:[1,91]},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[2,26],29:p,30:g,31:y,33:v,35:[1,92],37:m,48:b,58:i},{17:93,48:b},{17:94,48:b},{26:95,57:k},{26:96,57:k},{26:97,57:k},{42:[1,98],57:[2,35]},{5:[1,99]},{5:[1,100]},t(_,[2,20]),t(_,[2,21]),t(_,[2,22]),t(_,[2,23]),{19:[1,101]},t(_,[2,24]),{19:[1,102]},{26:103,57:k},{26:104,57:k},{5:[2,40]},{5:[2,30]},{5:[2,31]},{17:105,48:b},t(A,[2,11]),t(_,[2,12]),t(E,a,{8:75,32:106}),t(T,a,{8:77,34:107}),{5:[2,38]},{5:[2,39]},{57:[2,34]},{28:[2,29]},{28:[2,27]}],defaultActions:{7:[2,51],8:[2,1],9:[2,2],10:[2,3],36:[2,54],44:[2,50],61:[2,36],62:[2,37],65:[2,53],95:[2,40],96:[2,30],97:[2,31],103:[2,38],104:[2,39],105:[2,34],106:[2,29],107:[2,27]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},M={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),58;case 1:return this.begin("type_directive"),59;case 2:return this.popState(),this.begin("arg_directive"),14;case 3:return this.popState(),this.popState(),61;case 4:return 60;case 5:return 5;case 6:case 7:case 8:case 9:case 10:break;case 11:return this.begin("ID"),16;case 12:return e.yytext=e.yytext.trim(),this.begin("ALIAS"),48;case 13:return this.popState(),this.popState(),this.begin("LINE"),18;case 14:return this.popState(),this.popState(),5;case 15:return this.begin("LINE"),27;case 16:return this.begin("LINE"),29;case 17:return this.begin("LINE"),30;case 18:return this.begin("LINE"),31;case 19:return this.begin("LINE"),36;case 20:return this.begin("LINE"),33;case 21:return this.begin("LINE"),35;case 22:return this.popState(),19;case 23:return 28;case 24:return 43;case 25:return 44;case 26:return 39;case 27:return 37;case 28:return this.begin("ID"),22;case 29:return this.begin("ID"),23;case 30:return 25;case 31:return 7;case 32:return 21;case 33:return 42;case 34:return 5;case 35:return e.yytext=e.yytext.trim(),48;case 36:return 51;case 37:return 52;case 38:return 49;case 39:return 50;case 40:return 53;case 41:return 54;case 42:return 55;case 43:return 56;case 44:return 57;case 45:return 46;case 46:return 47;case 47:return 5;case 48:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:participant\b)/i,/^(?:[^\->:\n,;]+?(?=((?!\n)\s)+as(?!\n)\s|[#\n;]|$))/i,/^(?:as\b)/i,/^(?:(?:))/i,/^(?:loop\b)/i,/^(?:rect\b)/i,/^(?:opt\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:par\b)/i,/^(?:and\b)/i,/^(?:(?:[:]?(?:no)?wrap)?[^#\n;]*)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:activate\b)/i,/^(?:deactivate\b)/i,/^(?:title\b)/i,/^(?:sequenceDiagram\b)/i,/^(?:autonumber\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\+\->:\n,;]+((?!(-x|--x|-\)|--\)))[\-]*[^\+\->:\n,;]+)*)/i,/^(?:->>)/i,/^(?:-->>)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:-[x])/i,/^(?:--[x])/i,/^(?:-[\)])/i,/^(?:--[\)])/i,/^(?::(?:(?:no)?wrap)?[^#\n;]+)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1,8],inclusive:!1},type_directive:{rules:[2,3,8],inclusive:!1},arg_directive:{rules:[3,4,8],inclusive:!1},ID:{rules:[7,8,12],inclusive:!1},ALIAS:{rules:[7,8,13,14],inclusive:!1},LINE:{rules:[7,8,22],inclusive:!1},INITIAL:{rules:[0,5,6,8,9,10,11,15,16,17,18,19,20,21,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],inclusive:!0}}};function O(){this.yy={}}return S.lexer=M,O.prototype=S,S.Parser=O,new O}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){var r=n(198);t.exports={Graph:r.Graph,json:n(301),alg:n(302),version:r.version}},function(t,e,n){var r;try{r={cloneDeep:n(313),constant:n(86),defaults:n(154),each:n(87),filter:n(128),find:n(314),flatten:n(156),forEach:n(126),forIn:n(319),has:n(93),isUndefined:n(139),last:n(320),map:n(140),mapValues:n(321),max:n(322),merge:n(324),min:n(329),minBy:n(330),now:n(331),pick:n(161),range:n(162),reduce:n(142),sortBy:n(338),uniqueId:n(163),values:n(147),zipObject:n(343)}}catch(t){}r||(r=window._),t.exports=r},function(t,e){var n=Array.isArray;t.exports=n},function(t,e,n){ -/** - * @license - * Copyright (c) 2012-2013 Chris Pettitt - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -t.exports={graphlib:n(311),dagre:n(153),intersect:n(368),render:n(370),util:n(12),version:n(382)}},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,n){"use strict";var r=n(4),i=n(17).Graph;function a(t,e,n,i){var a;do{a=r.uniqueId(i)}while(t.hasNode(a));return n.dummy=e,t.setNode(a,n),a}function o(t){return r.max(r.map(t.nodes(),(function(e){var n=t.node(e).rank;if(!r.isUndefined(n))return n})))}t.exports={addDummyNode:a,simplify:function(t){var e=(new i).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){var r=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:r.weight+i.weight,minlen:Math.max(r.minlen,i.minlen)})})),e},asNonCompoundGraph:function(t){var e=new i({multigraph:t.isMultigraph()}).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){t.children(n).length||e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){e.setEdge(n,t.edge(n))})),e},successorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.outEdges(e),(function(e){n[e.w]=(n[e.w]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},predecessorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.inEdges(e),(function(e){n[e.v]=(n[e.v]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},intersectRect:function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;if(!o&&!s)throw new Error("Not possible to find intersection inside of the rectangle");Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=u*o/s,r=u):(o<0&&(c=-c),n=c,r=c*s/o);return{x:i+n,y:a+r}},buildLayerMatrix:function(t){var e=r.map(r.range(o(t)+1),(function(){return[]}));return r.forEach(t.nodes(),(function(n){var i=t.node(n),a=i.rank;r.isUndefined(a)||(e[a][i.order]=n)})),e},normalizeRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank})));r.forEach(t.nodes(),(function(n){var i=t.node(n);r.has(i,"rank")&&(i.rank-=e)}))},removeEmptyRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank}))),n=[];r.forEach(t.nodes(),(function(r){var i=t.node(r).rank-e;n[i]||(n[i]=[]),n[i].push(r)}));var i=0,a=t.graph().nodeRankFactor;r.forEach(n,(function(e,n){r.isUndefined(e)&&n%a!=0?--i:i&&r.forEach(e,(function(e){t.node(e).rank+=i}))}))},addBorderNode:function(t,e,n,r){var i={width:0,height:0};arguments.length>=4&&(i.rank=n,i.order=r);return a(t,"border",i,e)},maxRank:o,partition:function(t,e){var n={lhs:[],rhs:[]};return r.forEach(t,(function(t){e(t)?n.lhs.push(t):n.rhs.push(t)})),n},time:function(t,e){var n=r.now();try{return e()}finally{console.log(t+" time: "+(r.now()-n)+"ms")}},notime:function(t,e){return e()}}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(173),i=n(174),a=n(175),o={channel:r.default,lang:i.default,unit:a.default};e.default=o},function(t,e,n){var r;try{r={clone:n(199),constant:n(86),each:n(87),filter:n(128),has:n(93),isArray:n(5),isEmpty:n(276),isFunction:n(37),isUndefined:n(139),keys:n(30),map:n(140),reduce:n(142),size:n(279),transform:n(285),union:n(286),values:n(147)}}catch(t){}r||(r=window._),t.exports=r},function(t,e){t.exports=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}},function(t,e,n){var r=n(43);t.exports={isSubgraph:function(t,e){return!!t.children(e).length},edgeToId:function(t){return a(t.v)+":"+a(t.w)+":"+a(t.name)},applyStyle:function(t,e){e&&t.attr("style",e)},applyClass:function(t,e,n){e&&t.attr("class",e).attr("class",n+" "+t.attr("class"))},applyTransition:function(t,e){var n=e.graph();if(r.isPlainObject(n)){var i=n.transition;if(r.isFunction(i))return i(t)}return t}};var i=/:/g;function a(t){return t?String(t).replace(i,"\\:"):""}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,7],n=[1,6],r=[1,14],i=[1,25],a=[1,28],o=[1,26],s=[1,27],c=[1,29],u=[1,30],l=[1,31],h=[1,32],f=[1,34],d=[1,35],p=[1,36],g=[10,19],y=[1,48],v=[1,49],m=[1,50],b=[1,51],x=[1,52],_=[1,53],k=[10,19,25,32,33,41,44,45,46,47,48,49,54,56],w=[10,19,23,25,32,33,37,41,44,45,46,47,48,49,54,56,71,72,73],E=[10,13,17,19],T=[41,71,72,73],C=[41,48,49,71,72,73],A=[41,44,45,46,47,71,72,73],S=[10,19,25],M=[1,85],O={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,directive:5,graphConfig:6,openDirective:7,typeDirective:8,closeDirective:9,NEWLINE:10,":":11,argDirective:12,open_directive:13,type_directive:14,arg_directive:15,close_directive:16,CLASS_DIAGRAM:17,statements:18,EOF:19,statement:20,className:21,alphaNumToken:22,GENERICTYPE:23,relationStatement:24,LABEL:25,classStatement:26,methodStatement:27,annotationStatement:28,clickStatement:29,cssClassStatement:30,CLASS:31,STYLE_SEPARATOR:32,STRUCT_START:33,members:34,STRUCT_STOP:35,ANNOTATION_START:36,ANNOTATION_END:37,MEMBER:38,SEPARATOR:39,relation:40,STR:41,relationType:42,lineType:43,AGGREGATION:44,EXTENSION:45,COMPOSITION:46,DEPENDENCY:47,LINE:48,DOTTED_LINE:49,CALLBACK:50,LINK:51,LINK_TARGET:52,CLICK:53,CALLBACK_NAME:54,CALLBACK_ARGS:55,HREF:56,CSSCLASS:57,commentToken:58,textToken:59,graphCodeTokens:60,textNoTagsToken:61,TAGSTART:62,TAGEND:63,"==":64,"--":65,PCT:66,DEFAULT:67,SPACE:68,MINUS:69,keywords:70,UNICODE_TEXT:71,NUM:72,ALPHA:73,$accept:0,$end:1},terminals_:{2:"error",10:"NEWLINE",11:":",13:"open_directive",14:"type_directive",15:"arg_directive",16:"close_directive",17:"CLASS_DIAGRAM",19:"EOF",23:"GENERICTYPE",25:"LABEL",31:"CLASS",32:"STYLE_SEPARATOR",33:"STRUCT_START",35:"STRUCT_STOP",36:"ANNOTATION_START",37:"ANNOTATION_END",38:"MEMBER",39:"SEPARATOR",41:"STR",44:"AGGREGATION",45:"EXTENSION",46:"COMPOSITION",47:"DEPENDENCY",48:"LINE",49:"DOTTED_LINE",50:"CALLBACK",51:"LINK",52:"LINK_TARGET",53:"CLICK",54:"CALLBACK_NAME",55:"CALLBACK_ARGS",56:"HREF",57:"CSSCLASS",60:"graphCodeTokens",62:"TAGSTART",63:"TAGEND",64:"==",65:"--",66:"PCT",67:"DEFAULT",68:"SPACE",69:"MINUS",70:"keywords",71:"UNICODE_TEXT",72:"NUM",73:"ALPHA"},productions_:[0,[3,1],[3,2],[4,1],[5,4],[5,6],[7,1],[8,1],[12,1],[9,1],[6,4],[18,1],[18,2],[18,3],[21,1],[21,2],[21,3],[21,2],[20,1],[20,2],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[26,2],[26,4],[26,5],[26,7],[28,4],[34,1],[34,2],[27,1],[27,2],[27,1],[27,1],[24,3],[24,4],[24,4],[24,5],[40,3],[40,2],[40,2],[40,1],[42,1],[42,1],[42,1],[42,1],[43,1],[43,1],[29,3],[29,4],[29,3],[29,4],[29,4],[29,5],[29,3],[29,4],[29,4],[29,5],[29,3],[29,4],[29,4],[29,5],[30,3],[58,1],[58,1],[59,1],[59,1],[59,1],[59,1],[59,1],[59,1],[59,1],[61,1],[61,1],[61,1],[61,1],[22,1],[22,1],[22,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 6:r.parseDirective("%%{","open_directive");break;case 7:r.parseDirective(a[s],"type_directive");break;case 8:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 9:r.parseDirective("}%%","close_directive","class");break;case 14:this.$=a[s];break;case 15:this.$=a[s-1]+a[s];break;case 16:this.$=a[s-2]+"~"+a[s-1]+a[s];break;case 17:this.$=a[s-1]+"~"+a[s];break;case 18:r.addRelation(a[s]);break;case 19:a[s-1].title=r.cleanupLabel(a[s]),r.addRelation(a[s-1]);break;case 26:r.addClass(a[s]);break;case 27:r.addClass(a[s-2]),r.setCssClass(a[s-2],a[s]);break;case 28:r.addClass(a[s-3]),r.addMembers(a[s-3],a[s-1]);break;case 29:r.addClass(a[s-5]),r.setCssClass(a[s-5],a[s-3]),r.addMembers(a[s-5],a[s-1]);break;case 30:r.addAnnotation(a[s],a[s-2]);break;case 31:this.$=[a[s]];break;case 32:a[s].push(a[s-1]),this.$=a[s];break;case 33:break;case 34:r.addMember(a[s-1],r.cleanupLabel(a[s]));break;case 35:case 36:break;case 37:this.$={id1:a[s-2],id2:a[s],relation:a[s-1],relationTitle1:"none",relationTitle2:"none"};break;case 38:this.$={id1:a[s-3],id2:a[s],relation:a[s-1],relationTitle1:a[s-2],relationTitle2:"none"};break;case 39:this.$={id1:a[s-3],id2:a[s],relation:a[s-2],relationTitle1:"none",relationTitle2:a[s-1]};break;case 40:this.$={id1:a[s-4],id2:a[s],relation:a[s-2],relationTitle1:a[s-3],relationTitle2:a[s-1]};break;case 41:this.$={type1:a[s-2],type2:a[s],lineType:a[s-1]};break;case 42:this.$={type1:"none",type2:a[s],lineType:a[s-1]};break;case 43:this.$={type1:a[s-1],type2:"none",lineType:a[s]};break;case 44:this.$={type1:"none",type2:"none",lineType:a[s]};break;case 45:this.$=r.relationType.AGGREGATION;break;case 46:this.$=r.relationType.EXTENSION;break;case 47:this.$=r.relationType.COMPOSITION;break;case 48:this.$=r.relationType.DEPENDENCY;break;case 49:this.$=r.lineType.LINE;break;case 50:this.$=r.lineType.DOTTED_LINE;break;case 51:case 57:this.$=a[s-2],r.setClickEvent(a[s-1],a[s]);break;case 52:case 58:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 53:case 61:this.$=a[s-2],r.setLink(a[s-1],a[s]);break;case 54:this.$=a[s-3],r.setLink(a[s-2],a[s-1],a[s]);break;case 55:case 63:this.$=a[s-3],r.setLink(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 56:case 64:this.$=a[s-4],r.setLink(a[s-3],a[s-2],a[s]),r.setTooltip(a[s-3],a[s-1]);break;case 59:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 60:this.$=a[s-4],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setTooltip(a[s-3],a[s]);break;case 62:this.$=a[s-3],r.setLink(a[s-2],a[s-1],a[s]);break;case 65:r.setCssClass(a[s-1],a[s])}},table:[{3:1,4:2,5:3,6:4,7:5,13:e,17:n},{1:[3]},{1:[2,1]},{3:8,4:2,5:3,6:4,7:5,13:e,17:n},{1:[2,3]},{8:9,14:[1,10]},{10:[1,11]},{14:[2,6]},{1:[2,2]},{9:12,11:[1,13],16:r},t([11,16],[2,7]),{5:23,7:5,13:e,18:15,20:16,21:24,22:33,24:17,26:18,27:19,28:20,29:21,30:22,31:i,36:a,38:o,39:s,50:c,51:u,53:l,57:h,71:f,72:d,73:p},{10:[1,37]},{12:38,15:[1,39]},{10:[2,9]},{19:[1,40]},{10:[1,41],19:[2,11]},t(g,[2,18],{25:[1,42]}),t(g,[2,20]),t(g,[2,21]),t(g,[2,22]),t(g,[2,23]),t(g,[2,24]),t(g,[2,25]),t(g,[2,33],{40:43,42:46,43:47,25:[1,45],41:[1,44],44:y,45:v,46:m,47:b,48:x,49:_}),{21:54,22:33,71:f,72:d,73:p},t(g,[2,35]),t(g,[2,36]),{22:55,71:f,72:d,73:p},{21:56,22:33,71:f,72:d,73:p},{21:57,22:33,71:f,72:d,73:p},{21:58,22:33,71:f,72:d,73:p},{41:[1,59]},t(k,[2,14],{22:33,21:60,23:[1,61],71:f,72:d,73:p}),t(w,[2,79]),t(w,[2,80]),t(w,[2,81]),t(E,[2,4]),{9:62,16:r},{16:[2,8]},{1:[2,10]},{5:23,7:5,13:e,18:63,19:[2,12],20:16,21:24,22:33,24:17,26:18,27:19,28:20,29:21,30:22,31:i,36:a,38:o,39:s,50:c,51:u,53:l,57:h,71:f,72:d,73:p},t(g,[2,19]),{21:64,22:33,41:[1,65],71:f,72:d,73:p},{40:66,42:46,43:47,44:y,45:v,46:m,47:b,48:x,49:_},t(g,[2,34]),{43:67,48:x,49:_},t(T,[2,44],{42:68,44:y,45:v,46:m,47:b}),t(C,[2,45]),t(C,[2,46]),t(C,[2,47]),t(C,[2,48]),t(A,[2,49]),t(A,[2,50]),t(g,[2,26],{32:[1,69],33:[1,70]}),{37:[1,71]},{41:[1,72]},{41:[1,73]},{54:[1,74],56:[1,75]},{22:76,71:f,72:d,73:p},t(k,[2,15]),t(k,[2,17],{22:33,21:77,71:f,72:d,73:p}),{10:[1,78]},{19:[2,13]},t(S,[2,37]),{21:79,22:33,71:f,72:d,73:p},{21:80,22:33,41:[1,81],71:f,72:d,73:p},t(T,[2,43],{42:82,44:y,45:v,46:m,47:b}),t(T,[2,42]),{22:83,71:f,72:d,73:p},{34:84,38:M},{21:86,22:33,71:f,72:d,73:p},t(g,[2,51],{41:[1,87]}),t(g,[2,53],{41:[1,89],52:[1,88]}),t(g,[2,57],{41:[1,90],55:[1,91]}),t(g,[2,61],{41:[1,93],52:[1,92]}),t(g,[2,65]),t(k,[2,16]),t(E,[2,5]),t(S,[2,39]),t(S,[2,38]),{21:94,22:33,71:f,72:d,73:p},t(T,[2,41]),t(g,[2,27],{33:[1,95]}),{35:[1,96]},{34:97,35:[2,31],38:M},t(g,[2,30]),t(g,[2,52]),t(g,[2,54]),t(g,[2,55],{52:[1,98]}),t(g,[2,58]),t(g,[2,59],{41:[1,99]}),t(g,[2,62]),t(g,[2,63],{52:[1,100]}),t(S,[2,40]),{34:101,38:M},t(g,[2,28]),{35:[2,32]},t(g,[2,56]),t(g,[2,60]),t(g,[2,64]),{35:[1,102]},t(g,[2,29])],defaultActions:{2:[2,1],4:[2,3],7:[2,6],8:[2,2],14:[2,9],39:[2,8],40:[2,10],63:[2,13],97:[2,32]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},D={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),13;case 1:return this.begin("type_directive"),14;case 2:return this.popState(),this.begin("arg_directive"),11;case 3:return this.popState(),this.popState(),16;case 4:return 15;case 5:case 6:break;case 7:return 10;case 8:break;case 9:case 10:return 17;case 11:return this.begin("struct"),33;case 12:return"EOF_IN_STRUCT";case 13:return"OPEN_IN_STRUCT";case 14:return this.popState(),35;case 15:break;case 16:return"MEMBER";case 17:return 31;case 18:return 57;case 19:return 50;case 20:return 51;case 21:return 53;case 22:return 36;case 23:return 37;case 24:this.begin("generic");break;case 25:this.popState();break;case 26:return"GENERICTYPE";case 27:this.begin("string");break;case 28:this.popState();break;case 29:return"STR";case 30:this.begin("href");break;case 31:this.popState();break;case 32:return 56;case 33:this.begin("callback_name");break;case 34:this.popState();break;case 35:this.popState(),this.begin("callback_args");break;case 36:return 54;case 37:this.popState();break;case 38:return 55;case 39:case 40:case 41:case 42:return 52;case 43:case 44:return 45;case 45:case 46:return 47;case 47:return 46;case 48:return 44;case 49:return 48;case 50:return 49;case 51:return 25;case 52:return 32;case 53:return 69;case 54:return"DOT";case 55:return"PLUS";case 56:return 66;case 57:case 58:return"EQUALS";case 59:return 73;case 60:return"PUNCTUATION";case 61:return 72;case 62:return 71;case 63:return 68;case 64:return 19}},rules:[/^(?:%%\{)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:(\r?\n)+)/,/^(?:\s+)/,/^(?:classDiagram-v2\b)/,/^(?:classDiagram\b)/,/^(?:[{])/,/^(?:$)/,/^(?:[{])/,/^(?:[}])/,/^(?:[\n])/,/^(?:[^{}\n]*)/,/^(?:class\b)/,/^(?:cssClass\b)/,/^(?:callback\b)/,/^(?:link\b)/,/^(?:click\b)/,/^(?:<<)/,/^(?:>>)/,/^(?:[~])/,/^(?:[~])/,/^(?:[^~]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:--)/,/^(?:\.\.)/,/^(?::{1}[^:\n;]+)/,/^(?::{3})/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:\w+)/,/^(?:[!"#$%&'*+,-.`?\\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\s)/,/^(?:$)/],conditions:{arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callback_args:{rules:[37,38],inclusive:!1},callback_name:{rules:[34,35,36],inclusive:!1},href:{rules:[31,32],inclusive:!1},struct:{rules:[12,13,14,15,16],inclusive:!1},generic:{rules:[25,26],inclusive:!1},string:{rules:[28,29],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,17,18,19,20,21,22,23,24,27,30,33,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64],inclusive:!0}}};function N(){this.yy={}}return O.lexer=D,N.prototype=O,O.Parser=N,new N}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e){var n,r,i=t.exports={};function a(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function s(t){if(n===setTimeout)return setTimeout(t,0);if((n===a||!n)&&setTimeout)return n=setTimeout,setTimeout(t,0);try{return n(t,0)}catch(e){try{return n.call(null,t,0)}catch(e){return n.call(this,t,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:a}catch(t){n=a}try{r="function"==typeof clearTimeout?clearTimeout:o}catch(t){r=o}}();var c,u=[],l=!1,h=-1;function f(){l&&c&&(l=!1,c.length?u=c.concat(u):h=-1,u.length&&d())}function d(){if(!l){var t=s(f);l=!0;for(var e=u.length;e;){for(c=u,u=[];++h<e;)c&&c[h].run();h=-1,e=u.length}c=null,l=!1,function(t){if(r===clearTimeout)return clearTimeout(t);if((r===o||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(t);try{r(t)}catch(e){try{return r.call(null,t)}catch(e){return r.call(this,t)}}}(t)}}function p(t,e){this.fun=t,this.array=e}function g(){}i.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];u.push(new p(t,e)),1!==u.length||l||s(d)},p.prototype.run=function(){this.fun.apply(null,this.array)},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=g,i.addListener=g,i.once=g,i.off=g,i.removeListener=g,i.removeAllListeners=g,i.emit=g,i.prependListener=g,i.prependOnceListener=g,i.listeners=function(t){return[]},i.binding=function(t){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(t){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(75),i=n(99),a=n(179),o=n(180),s=n(181),c={format:{keyword:a.default,hex:i.default,rgb:o.default,rgba:o.default,hsl:s.default,hsla:s.default},parse:function(t){if("string"!=typeof t)return t;var e=i.default.parse(t)||o.default.parse(t)||s.default.parse(t)||a.default.parse(t);if(e)return e;throw new Error('Unsupported color format: "'+t+'"')},stringify:function(t){return!t.changed&&t.color?t.color:t.type.is(r.TYPE.HSL)||void 0===t.data.r?s.default.stringify(t):t.a<1||!Number.isInteger(t.r)||!Number.isInteger(t.g)||!Number.isInteger(t.b)?o.default.stringify(t):i.default.stringify(t)}};e.default=c},function(t,e,n){var r=n(109),i="object"==typeof self&&self&&self.Object===Object&&self,a=r||i||Function("return this")();t.exports=a},function(t,e,n){var r;try{r=n(3)}catch(t){}r||(r=window.graphlib),t.exports=r},function(t,e,n){t.exports={graphlib:n(17),layout:n(312),debug:n(366),util:{time:n(8).time,notime:n(8).notime},version:n(367)}},function(t,e){},function(t,e,n){(function(t){function n(t,e){for(var n=0,r=t.length-1;r>=0;r--){var i=t[r];"."===i?t.splice(r,1):".."===i?(t.splice(r,1),n++):n&&(t.splice(r,1),n--)}if(e)for(;n--;n)t.unshift("..");return t}function r(t,e){if(t.filter)return t.filter(e);for(var n=[],r=0;r<t.length;r++)e(t[r],r,t)&&n.push(t[r]);return n}e.resolve=function(){for(var e="",i=!1,a=arguments.length-1;a>=-1&&!i;a--){var o=a>=0?arguments[a]:t.cwd();if("string"!=typeof o)throw new TypeError("Arguments to path.resolve must be strings");o&&(e=o+"/"+e,i="/"===o.charAt(0))}return(i?"/":"")+(e=n(r(e.split("/"),(function(t){return!!t})),!i).join("/"))||"."},e.normalize=function(t){var a=e.isAbsolute(t),o="/"===i(t,-1);return(t=n(r(t.split("/"),(function(t){return!!t})),!a).join("/"))||a||(t="."),t&&o&&(t+="/"),(a?"/":"")+t},e.isAbsolute=function(t){return"/"===t.charAt(0)},e.join=function(){var t=Array.prototype.slice.call(arguments,0);return e.normalize(r(t,(function(t,e){if("string"!=typeof t)throw new TypeError("Arguments to path.join must be strings");return t})).join("/"))},e.relative=function(t,n){function r(t){for(var e=0;e<t.length&&""===t[e];e++);for(var n=t.length-1;n>=0&&""===t[n];n--);return e>n?[]:t.slice(e,n-e+1)}t=e.resolve(t).substr(1),n=e.resolve(n).substr(1);for(var i=r(t.split("/")),a=r(n.split("/")),o=Math.min(i.length,a.length),s=o,c=0;c<o;c++)if(i[c]!==a[c]){s=c;break}var u=[];for(c=s;c<i.length;c++)u.push("..");return(u=u.concat(a.slice(s))).join("/")},e.sep="/",e.delimiter=":",e.dirname=function(t){if("string"!=typeof t&&(t+=""),0===t.length)return".";for(var e=t.charCodeAt(0),n=47===e,r=-1,i=!0,a=t.length-1;a>=1;--a)if(47===(e=t.charCodeAt(a))){if(!i){r=a;break}}else i=!1;return-1===r?n?"/":".":n&&1===r?"/":t.slice(0,r)},e.basename=function(t,e){var n=function(t){"string"!=typeof t&&(t+="");var e,n=0,r=-1,i=!0;for(e=t.length-1;e>=0;--e)if(47===t.charCodeAt(e)){if(!i){n=e+1;break}}else-1===r&&(i=!1,r=e+1);return-1===r?"":t.slice(n,r)}(t);return e&&n.substr(-1*e.length)===e&&(n=n.substr(0,n.length-e.length)),n},e.extname=function(t){"string"!=typeof t&&(t+="");for(var e=-1,n=0,r=-1,i=!0,a=0,o=t.length-1;o>=0;--o){var s=t.charCodeAt(o);if(47!==s)-1===r&&(i=!1,r=o+1),46===s?-1===e?e=o:1!==a&&(a=1):-1!==e&&(a=-1);else if(!i){n=o+1;break}}return-1===e||-1===r||0===a||1===a&&e===r-1&&e===n+1?"":t.slice(e,r)};var i="b"==="ab".substr(-1)?function(t,e,n){return t.substr(e,n)}:function(t,e,n){return e<0&&(e=t.length+e),t.substr(e,n)}}).call(this,n(14))},function(t,e){t.exports=function(t){return null!=t&&"object"==typeof t}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,19],u=[1,20],l=[1,21],h=[1,22],f=[1,28],d=[1,23],p=[1,24],g=[1,25],y=[1,26],v=[1,29],m=[1,32],b=[1,4,5,14,15,17,19,20,22,23,24,25,26,36,39],x=[1,4,5,12,13,14,15,17,19,20,22,23,24,25,26,36,39],_=[1,4,5,7,14,15,17,19,20,22,23,24,25,26,36,39],k=[4,5,14,15,17,19,20,22,23,24,25,26,36,39],w={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NL:5,directive:6,SD:7,document:8,line:9,statement:10,idStatement:11,DESCR:12,"--\x3e":13,HIDE_EMPTY:14,scale:15,WIDTH:16,COMPOSIT_STATE:17,STRUCT_START:18,STRUCT_STOP:19,STATE_DESCR:20,AS:21,ID:22,FORK:23,JOIN:24,CONCURRENT:25,note:26,notePosition:27,NOTE_TEXT:28,openDirective:29,typeDirective:30,closeDirective:31,":":32,argDirective:33,eol:34,";":35,EDGE_STATE:36,left_of:37,right_of:38,open_directive:39,type_directive:40,arg_directive:41,close_directive:42,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NL",7:"SD",12:"DESCR",13:"--\x3e",14:"HIDE_EMPTY",15:"scale",16:"WIDTH",17:"COMPOSIT_STATE",18:"STRUCT_START",19:"STRUCT_STOP",20:"STATE_DESCR",21:"AS",22:"ID",23:"FORK",24:"JOIN",25:"CONCURRENT",26:"note",28:"NOTE_TEXT",32:":",35:";",36:"EDGE_STATE",37:"left_of",38:"right_of",39:"open_directive",40:"type_directive",41:"arg_directive",42:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[10,1],[10,2],[10,3],[10,4],[10,1],[10,2],[10,1],[10,4],[10,3],[10,6],[10,1],[10,1],[10,1],[10,4],[10,4],[10,1],[6,3],[6,5],[34,1],[34,1],[11,1],[11,1],[27,1],[27,1],[29,1],[30,1],[33,1],[31,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.setRootDoc(a[s]),a[s];case 5:this.$=[];break;case 6:"nl"!=a[s]&&(a[s-1].push(a[s]),this.$=a[s-1]);break;case 7:case 8:this.$=a[s];break;case 9:this.$="nl";break;case 10:this.$={stmt:"state",id:a[s],type:"default",description:""};break;case 11:this.$={stmt:"state",id:a[s-1],type:"default",description:r.trimColon(a[s])};break;case 12:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-2],type:"default",description:""},state2:{stmt:"state",id:a[s],type:"default",description:""}};break;case 13:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-3],type:"default",description:""},state2:{stmt:"state",id:a[s-1],type:"default",description:""},description:a[s].substr(1).trim()};break;case 17:this.$={stmt:"state",id:a[s-3],type:"default",description:"",doc:a[s-1]};break;case 18:var c=a[s],u=a[s-2].trim();if(a[s].match(":")){var l=a[s].split(":");c=l[0],u=[u,l[1]]}this.$={stmt:"state",id:c,type:"default",description:u};break;case 19:this.$={stmt:"state",id:a[s-3],type:"default",description:a[s-5],doc:a[s-1]};break;case 20:this.$={stmt:"state",id:a[s],type:"fork"};break;case 21:this.$={stmt:"state",id:a[s],type:"join"};break;case 22:this.$={stmt:"state",id:r.getDividerId(),type:"divider"};break;case 23:this.$={stmt:"state",id:a[s-1].trim(),note:{position:a[s-2].trim(),text:a[s].trim()}};break;case 30:case 31:this.$=a[s];break;case 34:r.parseDirective("%%{","open_directive");break;case 35:r.parseDirective(a[s],"type_directive");break;case 36:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 37:r.parseDirective("}%%","close_directive","state")}},table:[{3:1,4:e,5:n,6:4,7:r,29:6,39:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,29:6,39:i},{3:9,4:e,5:n,6:4,7:r,29:6,39:i},{3:10,4:e,5:n,6:4,7:r,29:6,39:i},t([1,4,5,14,15,17,20,22,23,24,25,26,36,39],a,{8:11}),{30:12,40:[1,13]},{40:[2,34]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:27,9:14,10:16,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},{31:30,32:[1,31],42:m},t([32,42],[2,35]),t(b,[2,6]),{6:27,10:33,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},t(b,[2,8]),t(b,[2,9]),t(b,[2,10],{12:[1,34],13:[1,35]}),t(b,[2,14]),{16:[1,36]},t(b,[2,16],{18:[1,37]}),{21:[1,38]},t(b,[2,20]),t(b,[2,21]),t(b,[2,22]),{27:39,28:[1,40],37:[1,41],38:[1,42]},t(b,[2,25]),t(x,[2,30]),t(x,[2,31]),t(_,[2,26]),{33:43,41:[1,44]},t(_,[2,37]),t(b,[2,7]),t(b,[2,11]),{11:45,22:f,36:v},t(b,[2,15]),t(k,a,{8:46}),{22:[1,47]},{22:[1,48]},{21:[1,49]},{22:[2,32]},{22:[2,33]},{31:50,42:m},{42:[2,36]},t(b,[2,12],{12:[1,51]}),{4:o,5:s,6:27,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,52],20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},t(b,[2,18],{18:[1,53]}),{28:[1,54]},{22:[1,55]},t(_,[2,27]),t(b,[2,13]),t(b,[2,17]),t(k,a,{8:56}),t(b,[2,23]),t(b,[2,24]),{4:o,5:s,6:27,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,57],20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},t(b,[2,19])],defaultActions:{7:[2,34],8:[2,1],9:[2,2],10:[2,3],41:[2,32],42:[2,33],44:[2,36]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},E={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),39;case 1:return this.begin("type_directive"),40;case 2:return this.popState(),this.begin("arg_directive"),32;case 3:return this.popState(),this.popState(),42;case 4:return 41;case 5:break;case 6:console.log("Crap after close");break;case 7:return 5;case 8:case 9:case 10:case 11:break;case 12:return this.pushState("SCALE"),15;case 13:return 16;case 14:this.popState();break;case 15:this.pushState("STATE");break;case 16:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),23;case 17:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),24;case 18:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),23;case 19:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),24;case 20:this.begin("STATE_STRING");break;case 21:return this.popState(),this.pushState("STATE_ID"),"AS";case 22:return this.popState(),"ID";case 23:this.popState();break;case 24:return"STATE_DESCR";case 25:return 17;case 26:this.popState();break;case 27:return this.popState(),this.pushState("struct"),18;case 28:return this.popState(),19;case 29:break;case 30:return this.begin("NOTE"),26;case 31:return this.popState(),this.pushState("NOTE_ID"),37;case 32:return this.popState(),this.pushState("NOTE_ID"),38;case 33:this.popState(),this.pushState("FLOATING_NOTE");break;case 34:return this.popState(),this.pushState("FLOATING_NOTE_ID"),"AS";case 35:break;case 36:return"NOTE_TEXT";case 37:return this.popState(),"ID";case 38:return this.popState(),this.pushState("NOTE_TEXT"),22;case 39:return this.popState(),e.yytext=e.yytext.substr(2).trim(),28;case 40:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),28;case 41:case 42:return 7;case 43:return 14;case 44:return 36;case 45:return 22;case 46:return e.yytext=e.yytext.trim(),12;case 47:return 13;case 48:return 25;case 49:return 5;case 50:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:state\s+)/i,/^(?:.*<<fork>>)/i,/^(?:.*<<join>>)/i,/^(?:.*\[\[fork\]\])/i,/^(?:.*\[\[join\]\])/i,/^(?:["])/i,/^(?:\s*as\s+)/i,/^(?:[^\n\{]*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n\s\{]+)/i,/^(?:\n)/i,/^(?:\{)/i,/^(?:\})/i,/^(?:[\n])/i,/^(?:note\s+)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:")/i,/^(?:\s*as\s*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n]*)/i,/^(?:\s*[^:\n\s\-]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:[\s\S]*?end note\b)/i,/^(?:stateDiagram\s+)/i,/^(?:stateDiagram-v2\s+)/i,/^(?:hide empty description\b)/i,/^(?:\[\*\])/i,/^(?:[^:\n\s\-\{]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:-->)/i,/^(?:--)/i,/^(?:$)/i,/^(?:.)/i],conditions:{LINE:{rules:[9,10],inclusive:!1},close_directive:{rules:[9,10],inclusive:!1},arg_directive:{rules:[3,4,9,10],inclusive:!1},type_directive:{rules:[2,3,9,10],inclusive:!1},open_directive:{rules:[1,9,10],inclusive:!1},struct:{rules:[9,10,15,28,29,30,44,45,46,47,48],inclusive:!1},FLOATING_NOTE_ID:{rules:[37],inclusive:!1},FLOATING_NOTE:{rules:[34,35,36],inclusive:!1},NOTE_TEXT:{rules:[39,40],inclusive:!1},NOTE_ID:{rules:[38],inclusive:!1},NOTE:{rules:[31,32,33],inclusive:!1},SCALE:{rules:[13,14],inclusive:!1},ALIAS:{rules:[],inclusive:!1},STATE_ID:{rules:[22],inclusive:!1},STATE_STRING:{rules:[23,24],inclusive:!1},FORK_STATE:{rules:[],inclusive:!1},STATE:{rules:[9,10,16,17,18,19,20,21,25,26,27],inclusive:!1},ID:{rules:[9,10],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,10,11,12,15,27,30,41,42,43,44,45,46,47,49,50],inclusive:!0}}};function T(){this.yy={}}return w.lexer=E,T.prototype=w,w.Parser=T,new T}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t){t.exports=function(){"use strict";var e,r;function i(){return e.apply(null,arguments)}function a(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function o(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function s(t){return void 0===t}function c(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function u(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function l(t,e){var n,r=[];for(n=0;n<t.length;++n)r.push(e(t[n],n));return r}function h(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function f(t,e){for(var n in e)h(e,n)&&(t[n]=e[n]);return h(e,"toString")&&(t.toString=e.toString),h(e,"valueOf")&&(t.valueOf=e.valueOf),t}function d(t,e,n,r){return be(t,e,n,r,!0).utc()}function p(t){return null==t._pf&&(t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),t._pf}function g(t){if(null==t._isValid){var e=p(t),n=r.call(e.parsedDateParts,(function(t){return null!=t})),i=!isNaN(t._d.getTime())&&e.overflow<0&&!e.empty&&!e.invalidMonth&&!e.invalidWeekday&&!e.weekdayMismatch&&!e.nullInput&&!e.invalidFormat&&!e.userInvalidated&&(!e.meridiem||e.meridiem&&n);if(t._strict&&(i=i&&0===e.charsLeftOver&&0===e.unusedTokens.length&&void 0===e.bigHour),null!=Object.isFrozen&&Object.isFrozen(t))return i;t._isValid=i}return t._isValid}function y(t){var e=d(NaN);return null!=t?f(p(e),t):p(e).userInvalidated=!0,e}r=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,r=0;r<n;r++)if(r in e&&t.call(this,e[r],r,e))return!0;return!1};var v=i.momentProperties=[];function m(t,e){var n,r,i;if(s(e._isAMomentObject)||(t._isAMomentObject=e._isAMomentObject),s(e._i)||(t._i=e._i),s(e._f)||(t._f=e._f),s(e._l)||(t._l=e._l),s(e._strict)||(t._strict=e._strict),s(e._tzm)||(t._tzm=e._tzm),s(e._isUTC)||(t._isUTC=e._isUTC),s(e._offset)||(t._offset=e._offset),s(e._pf)||(t._pf=p(e)),s(e._locale)||(t._locale=e._locale),0<v.length)for(n=0;n<v.length;n++)s(i=e[r=v[n]])||(t[r]=i);return t}var b=!1;function x(t){m(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===b&&(b=!0,i.updateOffset(this),b=!1)}function _(t){return t instanceof x||null!=t&&null!=t._isAMomentObject}function k(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function w(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=k(e)),n}function E(t,e,n){var r,i=Math.min(t.length,e.length),a=Math.abs(t.length-e.length),o=0;for(r=0;r<i;r++)(n&&t[r]!==e[r]||!n&&w(t[r])!==w(e[r]))&&o++;return o+a}function T(t){!1===i.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function C(t,e){var n=!0;return f((function(){if(null!=i.deprecationHandler&&i.deprecationHandler(null,t),n){for(var r,a=[],o=0;o<arguments.length;o++){if(r="","object"==typeof arguments[o]){for(var s in r+="\n["+o+"] ",arguments[0])r+=s+": "+arguments[0][s]+", ";r=r.slice(0,-2)}else r=arguments[o];a.push(r)}T(t+"\nArguments: "+Array.prototype.slice.call(a).join("")+"\n"+(new Error).stack),n=!1}return e.apply(this,arguments)}),e)}var A,S={};function M(t,e){null!=i.deprecationHandler&&i.deprecationHandler(t,e),S[t]||(T(e),S[t]=!0)}function O(t){return t instanceof Function||"[object Function]"===Object.prototype.toString.call(t)}function D(t,e){var n,r=f({},t);for(n in e)h(e,n)&&(o(t[n])&&o(e[n])?(r[n]={},f(r[n],t[n]),f(r[n],e[n])):null!=e[n]?r[n]=e[n]:delete r[n]);for(n in t)h(t,n)&&!h(e,n)&&o(t[n])&&(r[n]=f({},r[n]));return r}function N(t){null!=t&&this.set(t)}i.suppressDeprecationWarnings=!1,i.deprecationHandler=null,A=Object.keys?Object.keys:function(t){var e,n=[];for(e in t)h(t,e)&&n.push(e);return n};var B={};function L(t,e){var n=t.toLowerCase();B[n]=B[n+"s"]=B[e]=t}function P(t){return"string"==typeof t?B[t]||B[t.toLowerCase()]:void 0}function I(t){var e,n,r={};for(n in t)h(t,n)&&(e=P(n))&&(r[e]=t[n]);return r}var F={};function j(t,e){F[t]=e}function R(t,e,n){var r=""+Math.abs(t),i=e-r.length;return(0<=t?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var Y=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,z=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,U={},$={};function W(t,e,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),t&&($[t]=i),e&&($[e[0]]=function(){return R(i.apply(this,arguments),e[1],e[2])}),n&&($[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),t)})}function H(t,e){return t.isValid()?(e=V(e,t.localeData()),U[e]=U[e]||function(t){var e,n,r,i=t.match(Y);for(e=0,n=i.length;e<n;e++)$[i[e]]?i[e]=$[i[e]]:i[e]=(r=i[e]).match(/\[[\s\S]/)?r.replace(/^\[|\]$/g,""):r.replace(/\\/g,"");return function(e){var r,a="";for(r=0;r<n;r++)a+=O(i[r])?i[r].call(e,t):i[r];return a}}(e),U[e](t)):t.localeData().invalidDate()}function V(t,e){var n=5;function r(t){return e.longDateFormat(t)||t}for(z.lastIndex=0;0<=n&&z.test(t);)t=t.replace(z,r),z.lastIndex=0,n-=1;return t}var G=/\d/,q=/\d\d/,X=/\d{3}/,Z=/\d{4}/,J=/[+-]?\d{6}/,K=/\d\d?/,Q=/\d\d\d\d?/,tt=/\d\d\d\d\d\d?/,et=/\d{1,3}/,nt=/\d{1,4}/,rt=/[+-]?\d{1,6}/,it=/\d+/,at=/[+-]?\d+/,ot=/Z|[+-]\d\d:?\d\d/gi,st=/Z|[+-]\d\d(?::?\d\d)?/gi,ct=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ut={};function lt(t,e,n){ut[t]=O(e)?e:function(t,r){return t&&n?n:e}}function ht(t,e){return h(ut,t)?ut[t](e._strict,e._locale):new RegExp(ft(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(t,e,n,r,i){return e||n||r||i}))))}function ft(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var dt={};function pt(t,e){var n,r=e;for("string"==typeof t&&(t=[t]),c(e)&&(r=function(t,n){n[e]=w(t)}),n=0;n<t.length;n++)dt[t[n]]=r}function gt(t,e){pt(t,(function(t,n,r,i){r._w=r._w||{},e(t,r._w,r,i)}))}function yt(t){return vt(t)?366:365}function vt(t){return t%4==0&&t%100!=0||t%400==0}W("Y",0,0,(function(){var t=this.year();return t<=9999?""+t:"+"+t})),W(0,["YY",2],0,(function(){return this.year()%100})),W(0,["YYYY",4],0,"year"),W(0,["YYYYY",5],0,"year"),W(0,["YYYYYY",6,!0],0,"year"),L("year","y"),j("year",1),lt("Y",at),lt("YY",K,q),lt("YYYY",nt,Z),lt("YYYYY",rt,J),lt("YYYYYY",rt,J),pt(["YYYYY","YYYYYY"],0),pt("YYYY",(function(t,e){e[0]=2===t.length?i.parseTwoDigitYear(t):w(t)})),pt("YY",(function(t,e){e[0]=i.parseTwoDigitYear(t)})),pt("Y",(function(t,e){e[0]=parseInt(t,10)})),i.parseTwoDigitYear=function(t){return w(t)+(68<w(t)?1900:2e3)};var mt,bt=xt("FullYear",!0);function xt(t,e){return function(n){return null!=n?(kt(this,t,n),i.updateOffset(this,e),this):_t(this,t)}}function _t(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function kt(t,e,n){t.isValid()&&!isNaN(n)&&("FullYear"===e&&vt(t.year())&&1===t.month()&&29===t.date()?t._d["set"+(t._isUTC?"UTC":"")+e](n,t.month(),wt(n,t.month())):t._d["set"+(t._isUTC?"UTC":"")+e](n))}function wt(t,e){if(isNaN(t)||isNaN(e))return NaN;var n=(e%12+12)%12;return t+=(e-n)/12,1===n?vt(t)?29:28:31-n%7%2}mt=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var e;for(e=0;e<this.length;++e)if(this[e]===t)return e;return-1},W("M",["MM",2],"Mo",(function(){return this.month()+1})),W("MMM",0,0,(function(t){return this.localeData().monthsShort(this,t)})),W("MMMM",0,0,(function(t){return this.localeData().months(this,t)})),L("month","M"),j("month",8),lt("M",K),lt("MM",K,q),lt("MMM",(function(t,e){return e.monthsShortRegex(t)})),lt("MMMM",(function(t,e){return e.monthsRegex(t)})),pt(["M","MM"],(function(t,e){e[1]=w(t)-1})),pt(["MMM","MMMM"],(function(t,e,n,r){var i=n._locale.monthsParse(t,r,n._strict);null!=i?e[1]=i:p(n).invalidMonth=t}));var Et=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Tt="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Ct="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function At(t,e){var n;if(!t.isValid())return t;if("string"==typeof e)if(/^\d+$/.test(e))e=w(e);else if(!c(e=t.localeData().monthsParse(e)))return t;return n=Math.min(t.date(),wt(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function St(t){return null!=t?(At(this,t),i.updateOffset(this,!0),this):_t(this,"Month")}var Mt=ct,Ot=ct;function Dt(){function t(t,e){return e.length-t.length}var e,n,r=[],i=[],a=[];for(e=0;e<12;e++)n=d([2e3,e]),r.push(this.monthsShort(n,"")),i.push(this.months(n,"")),a.push(this.months(n,"")),a.push(this.monthsShort(n,""));for(r.sort(t),i.sort(t),a.sort(t),e=0;e<12;e++)r[e]=ft(r[e]),i[e]=ft(i[e]);for(e=0;e<24;e++)a[e]=ft(a[e]);this._monthsRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+r.join("|")+")","i")}function Nt(t){var e;if(t<100&&0<=t){var n=Array.prototype.slice.call(arguments);n[0]=t+400,e=new Date(Date.UTC.apply(null,n)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)}else e=new Date(Date.UTC.apply(null,arguments));return e}function Bt(t,e,n){var r=7+e-n;return-(7+Nt(t,0,r).getUTCDay()-e)%7+r-1}function Lt(t,e,n,r,i){var a,o,s=1+7*(e-1)+(7+n-r)%7+Bt(t,r,i);return o=s<=0?yt(a=t-1)+s:s>yt(t)?(a=t+1,s-yt(t)):(a=t,s),{year:a,dayOfYear:o}}function Pt(t,e,n){var r,i,a=Bt(t.year(),e,n),o=Math.floor((t.dayOfYear()-a-1)/7)+1;return o<1?r=o+It(i=t.year()-1,e,n):o>It(t.year(),e,n)?(r=o-It(t.year(),e,n),i=t.year()+1):(i=t.year(),r=o),{week:r,year:i}}function It(t,e,n){var r=Bt(t,e,n),i=Bt(t+1,e,n);return(yt(t)-r+i)/7}function Ft(t,e){return t.slice(e,7).concat(t.slice(0,e))}W("w",["ww",2],"wo","week"),W("W",["WW",2],"Wo","isoWeek"),L("week","w"),L("isoWeek","W"),j("week",5),j("isoWeek",5),lt("w",K),lt("ww",K,q),lt("W",K),lt("WW",K,q),gt(["w","ww","W","WW"],(function(t,e,n,r){e[r.substr(0,1)]=w(t)})),W("d",0,"do","day"),W("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),W("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),W("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),W("e",0,0,"weekday"),W("E",0,0,"isoWeekday"),L("day","d"),L("weekday","e"),L("isoWeekday","E"),j("day",11),j("weekday",11),j("isoWeekday",11),lt("d",K),lt("e",K),lt("E",K),lt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),lt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),lt("dddd",(function(t,e){return e.weekdaysRegex(t)})),gt(["dd","ddd","dddd"],(function(t,e,n,r){var i=n._locale.weekdaysParse(t,r,n._strict);null!=i?e.d=i:p(n).invalidWeekday=t})),gt(["d","e","E"],(function(t,e,n,r){e[r]=w(t)}));var jt="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Rt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Yt="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),zt=ct,Ut=ct,$t=ct;function Wt(){function t(t,e){return e.length-t.length}var e,n,r,i,a,o=[],s=[],c=[],u=[];for(e=0;e<7;e++)n=d([2e3,1]).day(e),r=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),a=this.weekdays(n,""),o.push(r),s.push(i),c.push(a),u.push(r),u.push(i),u.push(a);for(o.sort(t),s.sort(t),c.sort(t),u.sort(t),e=0;e<7;e++)s[e]=ft(s[e]),c[e]=ft(c[e]),u[e]=ft(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+c.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function Ht(){return this.hours()%12||12}function Vt(t,e){W(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function Gt(t,e){return e._meridiemParse}W("H",["HH",2],0,"hour"),W("h",["hh",2],0,Ht),W("k",["kk",2],0,(function(){return this.hours()||24})),W("hmm",0,0,(function(){return""+Ht.apply(this)+R(this.minutes(),2)})),W("hmmss",0,0,(function(){return""+Ht.apply(this)+R(this.minutes(),2)+R(this.seconds(),2)})),W("Hmm",0,0,(function(){return""+this.hours()+R(this.minutes(),2)})),W("Hmmss",0,0,(function(){return""+this.hours()+R(this.minutes(),2)+R(this.seconds(),2)})),Vt("a",!0),Vt("A",!1),L("hour","h"),j("hour",13),lt("a",Gt),lt("A",Gt),lt("H",K),lt("h",K),lt("k",K),lt("HH",K,q),lt("hh",K,q),lt("kk",K,q),lt("hmm",Q),lt("hmmss",tt),lt("Hmm",Q),lt("Hmmss",tt),pt(["H","HH"],3),pt(["k","kk"],(function(t,e,n){var r=w(t);e[3]=24===r?0:r})),pt(["a","A"],(function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t})),pt(["h","hh"],(function(t,e,n){e[3]=w(t),p(n).bigHour=!0})),pt("hmm",(function(t,e,n){var r=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r)),p(n).bigHour=!0})),pt("hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r,2)),e[5]=w(t.substr(i)),p(n).bigHour=!0})),pt("Hmm",(function(t,e,n){var r=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r))})),pt("Hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r,2)),e[5]=w(t.substr(i))}));var qt,Xt=xt("Hours",!0),Zt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Tt,monthsShort:Ct,week:{dow:0,doy:6},weekdays:jt,weekdaysMin:Yt,weekdaysShort:Rt,meridiemParse:/[ap]\.?m?\.?/i},Jt={},Kt={};function Qt(t){return t?t.toLowerCase().replace("_","-"):t}function te(e){var r=null;if(!Jt[e]&&void 0!==t&&t&&t.exports)try{r=qt._abbr,n(171)("./"+e),ee(r)}catch(e){}return Jt[e]}function ee(t,e){var n;return t&&((n=s(e)?re(t):ne(t,e))?qt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),qt._abbr}function ne(t,e){if(null===e)return delete Jt[t],null;var n,r=Zt;if(e.abbr=t,null!=Jt[t])M("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=Jt[t]._config;else if(null!=e.parentLocale)if(null!=Jt[e.parentLocale])r=Jt[e.parentLocale]._config;else{if(null==(n=te(e.parentLocale)))return Kt[e.parentLocale]||(Kt[e.parentLocale]=[]),Kt[e.parentLocale].push({name:t,config:e}),null;r=n._config}return Jt[t]=new N(D(r,e)),Kt[t]&&Kt[t].forEach((function(t){ne(t.name,t.config)})),ee(t),Jt[t]}function re(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return qt;if(!a(t)){if(e=te(t))return e;t=[t]}return function(t){for(var e,n,r,i,a=0;a<t.length;){for(e=(i=Qt(t[a]).split("-")).length,n=(n=Qt(t[a+1]))?n.split("-"):null;0<e;){if(r=te(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&E(i,n,!0)>=e-1)break;e--}a++}return qt}(t)}function ie(t){var e,n=t._a;return n&&-2===p(t).overflow&&(e=n[1]<0||11<n[1]?1:n[2]<1||n[2]>wt(n[0],n[1])?2:n[3]<0||24<n[3]||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||59<n[4]?4:n[5]<0||59<n[5]?5:n[6]<0||999<n[6]?6:-1,p(t)._overflowDayOfYear&&(e<0||2<e)&&(e=2),p(t)._overflowWeeks&&-1===e&&(e=7),p(t)._overflowWeekday&&-1===e&&(e=8),p(t).overflow=e),t}function ae(t,e,n){return null!=t?t:null!=e?e:n}function oe(t){var e,n,r,a,o,s=[];if(!t._d){var c,u;for(c=t,u=new Date(i.now()),r=c._useUTC?[u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()]:[u.getFullYear(),u.getMonth(),u.getDate()],t._w&&null==t._a[2]&&null==t._a[1]&&function(t){var e,n,r,i,a,o,s,c;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)a=1,o=4,n=ae(e.GG,t._a[0],Pt(xe(),1,4).year),r=ae(e.W,1),((i=ae(e.E,1))<1||7<i)&&(c=!0);else{a=t._locale._week.dow,o=t._locale._week.doy;var u=Pt(xe(),a,o);n=ae(e.gg,t._a[0],u.year),r=ae(e.w,u.week),null!=e.d?((i=e.d)<0||6<i)&&(c=!0):null!=e.e?(i=e.e+a,(e.e<0||6<e.e)&&(c=!0)):i=a}r<1||r>It(n,a,o)?p(t)._overflowWeeks=!0:null!=c?p(t)._overflowWeekday=!0:(s=Lt(n,r,i,a,o),t._a[0]=s.year,t._dayOfYear=s.dayOfYear)}(t),null!=t._dayOfYear&&(o=ae(t._a[0],r[0]),(t._dayOfYear>yt(o)||0===t._dayOfYear)&&(p(t)._overflowDayOfYear=!0),n=Nt(o,0,t._dayOfYear),t._a[1]=n.getUTCMonth(),t._a[2]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=r[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[3]&&0===t._a[4]&&0===t._a[5]&&0===t._a[6]&&(t._nextDay=!0,t._a[3]=0),t._d=(t._useUTC?Nt:function(t,e,n,r,i,a,o){var s;return t<100&&0<=t?(s=new Date(t+400,e,n,r,i,a,o),isFinite(s.getFullYear())&&s.setFullYear(t)):s=new Date(t,e,n,r,i,a,o),s}).apply(null,s),a=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[3]=24),t._w&&void 0!==t._w.d&&t._w.d!==a&&(p(t).weekdayMismatch=!0)}}var se=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ce=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ue=/Z|[+-]\d\d(?::?\d\d)?/,le=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],he=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],fe=/^\/?Date\((\-?\d+)/i;function de(t){var e,n,r,i,a,o,s=t._i,c=se.exec(s)||ce.exec(s);if(c){for(p(t).iso=!0,e=0,n=le.length;e<n;e++)if(le[e][1].exec(c[1])){i=le[e][0],r=!1!==le[e][2];break}if(null==i)return void(t._isValid=!1);if(c[3]){for(e=0,n=he.length;e<n;e++)if(he[e][1].exec(c[3])){a=(c[2]||" ")+he[e][0];break}if(null==a)return void(t._isValid=!1)}if(!r&&null!=a)return void(t._isValid=!1);if(c[4]){if(!ue.exec(c[4]))return void(t._isValid=!1);o="Z"}t._f=i+(a||"")+(o||""),ve(t)}else t._isValid=!1}var pe=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,ge={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function ye(t){var e,n,r,i=pe.exec(t._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(i){var a=function(t,e,n,r,i,a){var o=[function(t){var e=parseInt(t,10);return e<=49?2e3+e:e<=999?1900+e:e}(t),Ct.indexOf(e),parseInt(n,10),parseInt(r,10),parseInt(i,10)];return a&&o.push(parseInt(a,10)),o}(i[4],i[3],i[2],i[5],i[6],i[7]);if(n=a,r=t,(e=i[1])&&Rt.indexOf(e)!==new Date(n[0],n[1],n[2]).getDay()&&(p(r).weekdayMismatch=!0,!(r._isValid=!1)))return;t._a=a,t._tzm=function(t,e,n){if(t)return ge[t];if(e)return 0;var r=parseInt(n,10),i=r%100;return(r-i)/100*60+i}(i[8],i[9],i[10]),t._d=Nt.apply(null,t._a),t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),p(t).rfc2822=!0}else t._isValid=!1}function ve(t){if(t._f!==i.ISO_8601)if(t._f!==i.RFC_2822){t._a=[],p(t).empty=!0;var e,n,r,a,o,s,c,u,l=""+t._i,f=l.length,d=0;for(r=V(t._f,t._locale).match(Y)||[],e=0;e<r.length;e++)a=r[e],(n=(l.match(ht(a,t))||[])[0])&&(0<(o=l.substr(0,l.indexOf(n))).length&&p(t).unusedInput.push(o),l=l.slice(l.indexOf(n)+n.length),d+=n.length),$[a]?(n?p(t).empty=!1:p(t).unusedTokens.push(a),s=a,u=t,null!=(c=n)&&h(dt,s)&&dt[s](c,u._a,u,s)):t._strict&&!n&&p(t).unusedTokens.push(a);p(t).charsLeftOver=f-d,0<l.length&&p(t).unusedInput.push(l),t._a[3]<=12&&!0===p(t).bigHour&&0<t._a[3]&&(p(t).bigHour=void 0),p(t).parsedDateParts=t._a.slice(0),p(t).meridiem=t._meridiem,t._a[3]=function(t,e,n){var r;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):(null!=t.isPM&&((r=t.isPM(n))&&e<12&&(e+=12),r||12!==e||(e=0)),e)}(t._locale,t._a[3],t._meridiem),oe(t),ie(t)}else ye(t);else de(t)}function me(t){var e,n,r,h,d=t._i,v=t._f;return t._locale=t._locale||re(t._l),null===d||void 0===v&&""===d?y({nullInput:!0}):("string"==typeof d&&(t._i=d=t._locale.preparse(d)),_(d)?new x(ie(d)):(u(d)?t._d=d:a(v)?function(t){var e,n,r,i,a;if(0===t._f.length)return p(t).invalidFormat=!0,t._d=new Date(NaN);for(i=0;i<t._f.length;i++)a=0,e=m({},t),null!=t._useUTC&&(e._useUTC=t._useUTC),e._f=t._f[i],ve(e),g(e)&&(a+=p(e).charsLeftOver,a+=10*p(e).unusedTokens.length,p(e).score=a,(null==r||a<r)&&(r=a,n=e));f(t,n||e)}(t):v?ve(t):s(n=(e=t)._i)?e._d=new Date(i.now()):u(n)?e._d=new Date(n.valueOf()):"string"==typeof n?(r=e,null===(h=fe.exec(r._i))?(de(r),!1===r._isValid&&(delete r._isValid,ye(r),!1===r._isValid&&(delete r._isValid,i.createFromInputFallback(r)))):r._d=new Date(+h[1])):a(n)?(e._a=l(n.slice(0),(function(t){return parseInt(t,10)})),oe(e)):o(n)?function(t){if(!t._d){var e=I(t._i);t._a=l([e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],(function(t){return t&&parseInt(t,10)})),oe(t)}}(e):c(n)?e._d=new Date(n):i.createFromInputFallback(e),g(t)||(t._d=null),t))}function be(t,e,n,r,i){var s,c={};return!0!==n&&!1!==n||(r=n,n=void 0),(o(t)&&function(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}(t)||a(t)&&0===t.length)&&(t=void 0),c._isAMomentObject=!0,c._useUTC=c._isUTC=i,c._l=n,c._i=t,c._f=e,c._strict=r,(s=new x(ie(me(c))))._nextDay&&(s.add(1,"d"),s._nextDay=void 0),s}function xe(t,e,n,r){return be(t,e,n,r,!1)}i.createFromInputFallback=C("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",(function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))})),i.ISO_8601=function(){},i.RFC_2822=function(){};var _e=C("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=xe.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:y()})),ke=C("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=xe.apply(null,arguments);return this.isValid()&&t.isValid()?this<t?this:t:y()}));function we(t,e){var n,r;if(1===e.length&&a(e[0])&&(e=e[0]),!e.length)return xe();for(n=e[0],r=1;r<e.length;++r)e[r].isValid()&&!e[r][t](n)||(n=e[r]);return n}var Ee=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Te(t){var e=I(t),n=e.year||0,r=e.quarter||0,i=e.month||0,a=e.week||e.isoWeek||0,o=e.day||0,s=e.hour||0,c=e.minute||0,u=e.second||0,l=e.millisecond||0;this._isValid=function(t){for(var e in t)if(-1===mt.call(Ee,e)||null!=t[e]&&isNaN(t[e]))return!1;for(var n=!1,r=0;r<Ee.length;++r)if(t[Ee[r]]){if(n)return!1;parseFloat(t[Ee[r]])!==w(t[Ee[r]])&&(n=!0)}return!0}(e),this._milliseconds=+l+1e3*u+6e4*c+1e3*s*60*60,this._days=+o+7*a,this._months=+i+3*r+12*n,this._data={},this._locale=re(),this._bubble()}function Ce(t){return t instanceof Te}function Ae(t){return t<0?-1*Math.round(-1*t):Math.round(t)}function Se(t,e){W(t,0,0,(function(){var t=this.utcOffset(),n="+";return t<0&&(t=-t,n="-"),n+R(~~(t/60),2)+e+R(~~t%60,2)}))}Se("Z",":"),Se("ZZ",""),lt("Z",st),lt("ZZ",st),pt(["Z","ZZ"],(function(t,e,n){n._useUTC=!0,n._tzm=Oe(st,t)}));var Me=/([\+\-]|\d\d)/gi;function Oe(t,e){var n=(e||"").match(t);if(null===n)return null;var r=((n[n.length-1]||[])+"").match(Me)||["-",0,0],i=60*r[1]+w(r[2]);return 0===i?0:"+"===r[0]?i:-i}function De(t,e){var n,r;return e._isUTC?(n=e.clone(),r=(_(t)||u(t)?t.valueOf():xe(t).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+r),i.updateOffset(n,!1),n):xe(t).local()}function Ne(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function Be(){return!!this.isValid()&&this._isUTC&&0===this._offset}i.updateOffset=function(){};var Le=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Pe=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Ie(t,e){var n,r,i,a=t,o=null;return Ce(t)?a={ms:t._milliseconds,d:t._days,M:t._months}:c(t)?(a={},e?a[e]=t:a.milliseconds=t):(o=Le.exec(t))?(n="-"===o[1]?-1:1,a={y:0,d:w(o[2])*n,h:w(o[3])*n,m:w(o[4])*n,s:w(o[5])*n,ms:w(Ae(1e3*o[6]))*n}):(o=Pe.exec(t))?(n="-"===o[1]?-1:1,a={y:Fe(o[2],n),M:Fe(o[3],n),w:Fe(o[4],n),d:Fe(o[5],n),h:Fe(o[6],n),m:Fe(o[7],n),s:Fe(o[8],n)}):null==a?a={}:"object"==typeof a&&("from"in a||"to"in a)&&(i=function(t,e){var n;return t.isValid()&&e.isValid()?(e=De(e,t),t.isBefore(e)?n=je(t,e):((n=je(e,t)).milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}(xe(a.from),xe(a.to)),(a={}).ms=i.milliseconds,a.M=i.months),r=new Te(a),Ce(t)&&h(t,"_locale")&&(r._locale=t._locale),r}function Fe(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function je(t,e){var n={};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function Re(t,e){return function(n,r){var i;return null===r||isNaN(+r)||(M(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),i=n,n=r,r=i),Ye(this,Ie(n="string"==typeof n?+n:n,r),t),this}}function Ye(t,e,n,r){var a=e._milliseconds,o=Ae(e._days),s=Ae(e._months);t.isValid()&&(r=null==r||r,s&&At(t,_t(t,"Month")+s*n),o&&kt(t,"Date",_t(t,"Date")+o*n),a&&t._d.setTime(t._d.valueOf()+a*n),r&&i.updateOffset(t,o||s))}Ie.fn=Te.prototype,Ie.invalid=function(){return Ie(NaN)};var ze=Re(1,"add"),Ue=Re(-1,"subtract");function $e(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),r=t.clone().add(n,"months");return-(n+(e-r<0?(e-r)/(r-t.clone().add(n-1,"months")):(e-r)/(t.clone().add(n+1,"months")-r)))||0}function We(t){var e;return void 0===t?this._locale._abbr:(null!=(e=re(t))&&(this._locale=e),this)}i.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",i.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var He=C("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function Ve(){return this._locale}var Ge=126227808e5;function qe(t,e){return(t%e+e)%e}function Xe(t,e,n){return t<100&&0<=t?new Date(t+400,e,n)-Ge:new Date(t,e,n).valueOf()}function Ze(t,e,n){return t<100&&0<=t?Date.UTC(t+400,e,n)-Ge:Date.UTC(t,e,n)}function Je(t,e){W(0,[t,t.length],0,e)}function Ke(t,e,n,r,i){var a;return null==t?Pt(this,r,i).year:((a=It(t,r,i))<e&&(e=a),function(t,e,n,r,i){var a=Lt(t,e,n,r,i),o=Nt(a.year,0,a.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}.call(this,t,e,n,r,i))}W(0,["gg",2],0,(function(){return this.weekYear()%100})),W(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),Je("gggg","weekYear"),Je("ggggg","weekYear"),Je("GGGG","isoWeekYear"),Je("GGGGG","isoWeekYear"),L("weekYear","gg"),L("isoWeekYear","GG"),j("weekYear",1),j("isoWeekYear",1),lt("G",at),lt("g",at),lt("GG",K,q),lt("gg",K,q),lt("GGGG",nt,Z),lt("gggg",nt,Z),lt("GGGGG",rt,J),lt("ggggg",rt,J),gt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,n,r){e[r.substr(0,2)]=w(t)})),gt(["gg","GG"],(function(t,e,n,r){e[r]=i.parseTwoDigitYear(t)})),W("Q",0,"Qo","quarter"),L("quarter","Q"),j("quarter",7),lt("Q",G),pt("Q",(function(t,e){e[1]=3*(w(t)-1)})),W("D",["DD",2],"Do","date"),L("date","D"),j("date",9),lt("D",K),lt("DD",K,q),lt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),pt(["D","DD"],2),pt("Do",(function(t,e){e[2]=w(t.match(K)[0])}));var Qe=xt("Date",!0);W("DDD",["DDDD",3],"DDDo","dayOfYear"),L("dayOfYear","DDD"),j("dayOfYear",4),lt("DDD",et),lt("DDDD",X),pt(["DDD","DDDD"],(function(t,e,n){n._dayOfYear=w(t)})),W("m",["mm",2],0,"minute"),L("minute","m"),j("minute",14),lt("m",K),lt("mm",K,q),pt(["m","mm"],4);var tn=xt("Minutes",!1);W("s",["ss",2],0,"second"),L("second","s"),j("second",15),lt("s",K),lt("ss",K,q),pt(["s","ss"],5);var en,nn=xt("Seconds",!1);for(W("S",0,0,(function(){return~~(this.millisecond()/100)})),W(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),W(0,["SSS",3],0,"millisecond"),W(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),W(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),W(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),W(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),W(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),W(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),L("millisecond","ms"),j("millisecond",16),lt("S",et,G),lt("SS",et,q),lt("SSS",et,X),en="SSSS";en.length<=9;en+="S")lt(en,it);function rn(t,e){e[6]=w(1e3*("0."+t))}for(en="S";en.length<=9;en+="S")pt(en,rn);var an=xt("Milliseconds",!1);W("z",0,0,"zoneAbbr"),W("zz",0,0,"zoneName");var on=x.prototype;function sn(t){return t}on.add=ze,on.calendar=function(t,e){var n=t||xe(),r=De(n,this).startOf("day"),a=i.calendarFormat(this,r)||"sameElse",o=e&&(O(e[a])?e[a].call(this,n):e[a]);return this.format(o||this.localeData().calendar(a,this,xe(n)))},on.clone=function(){return new x(this)},on.diff=function(t,e,n){var r,i,a;if(!this.isValid())return NaN;if(!(r=De(t,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),e=P(e)){case"year":a=$e(this,r)/12;break;case"month":a=$e(this,r);break;case"quarter":a=$e(this,r)/3;break;case"second":a=(this-r)/1e3;break;case"minute":a=(this-r)/6e4;break;case"hour":a=(this-r)/36e5;break;case"day":a=(this-r-i)/864e5;break;case"week":a=(this-r-i)/6048e5;break;default:a=this-r}return n?a:k(a)},on.endOf=function(t){var e;if(void 0===(t=P(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year()+1,0,1)-1;break;case"quarter":e=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=n(this.year(),this.month()+1,1)-1;break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=36e5-qe(e+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":e=this._d.valueOf(),e+=6e4-qe(e,6e4)-1;break;case"second":e=this._d.valueOf(),e+=1e3-qe(e,1e3)-1}return this._d.setTime(e),i.updateOffset(this,!0),this},on.format=function(t){t||(t=this.isUtc()?i.defaultFormatUtc:i.defaultFormat);var e=H(this,t);return this.localeData().postformat(e)},on.from=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||xe(t).isValid())?Ie({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.fromNow=function(t){return this.from(xe(),t)},on.to=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||xe(t).isValid())?Ie({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.toNow=function(t){return this.to(xe(),t)},on.get=function(t){return O(this[t=P(t)])?this[t]():this},on.invalidAt=function(){return p(this).overflow},on.isAfter=function(t,e){var n=_(t)?t:xe(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=P(e)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(e).valueOf())},on.isBefore=function(t,e){var n=_(t)?t:xe(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=P(e)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(e).valueOf()<n.valueOf())},on.isBetween=function(t,e,n,r){var i=_(t)?t:xe(t),a=_(e)?e:xe(e);return!!(this.isValid()&&i.isValid()&&a.isValid())&&("("===(r=r||"()")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(")"===r[1]?this.isBefore(a,n):!this.isAfter(a,n))},on.isSame=function(t,e){var n,r=_(t)?t:xe(t);return!(!this.isValid()||!r.isValid())&&("millisecond"===(e=P(e)||"millisecond")?this.valueOf()===r.valueOf():(n=r.valueOf(),this.clone().startOf(e).valueOf()<=n&&n<=this.clone().endOf(e).valueOf()))},on.isSameOrAfter=function(t,e){return this.isSame(t,e)||this.isAfter(t,e)},on.isSameOrBefore=function(t,e){return this.isSame(t,e)||this.isBefore(t,e)},on.isValid=function(){return g(this)},on.lang=He,on.locale=We,on.localeData=Ve,on.max=ke,on.min=_e,on.parsingFlags=function(){return f({},p(this))},on.set=function(t,e){if("object"==typeof t)for(var n=function(t){var e=[];for(var n in t)e.push({unit:n,priority:F[n]});return e.sort((function(t,e){return t.priority-e.priority})),e}(t=I(t)),r=0;r<n.length;r++)this[n[r].unit](t[n[r].unit]);else if(O(this[t=P(t)]))return this[t](e);return this},on.startOf=function(t){var e;if(void 0===(t=P(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year(),0,1);break;case"quarter":e=n(this.year(),this.month()-this.month()%3,1);break;case"month":e=n(this.year(),this.month(),1);break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=n(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=qe(e+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":e=this._d.valueOf(),e-=qe(e,6e4);break;case"second":e=this._d.valueOf(),e-=qe(e,1e3)}return this._d.setTime(e),i.updateOffset(this,!0),this},on.subtract=Ue,on.toArray=function(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]},on.toObject=function(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}},on.toDate=function(){return new Date(this.valueOf())},on.toISOString=function(t){if(!this.isValid())return null;var e=!0!==t,n=e?this.clone().utc():this;return n.year()<0||9999<n.year()?H(n,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):O(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",H(n,"Z")):H(n,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},on.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',r=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=e+'[")]';return this.format(n+r+"-MM-DD[T]HH:mm:ss.SSS"+i)},on.toJSON=function(){return this.isValid()?this.toISOString():null},on.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},on.unix=function(){return Math.floor(this.valueOf()/1e3)},on.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},on.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},on.year=bt,on.isLeapYear=function(){return vt(this.year())},on.weekYear=function(t){return Ke.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},on.isoWeekYear=function(t){return Ke.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},on.quarter=on.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},on.month=St,on.daysInMonth=function(){return wt(this.year(),this.month())},on.week=on.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},on.isoWeek=on.isoWeeks=function(t){var e=Pt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},on.weeksInYear=function(){var t=this.localeData()._week;return It(this.year(),t.dow,t.doy)},on.isoWeeksInYear=function(){return It(this.year(),1,4)},on.date=Qe,on.day=on.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e,n,r=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(e=t,n=this.localeData(),t="string"!=typeof e?e:isNaN(e)?"number"==typeof(e=n.weekdaysParse(e))?e:null:parseInt(e,10),this.add(t-r,"d")):r},on.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},on.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null==t)return this.day()||7;var e,n,r=(e=t,n=this.localeData(),"string"==typeof e?n.weekdaysParse(e)%7||7:isNaN(e)?null:e);return this.day(this.day()%7?r:r-7)},on.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},on.hour=on.hours=Xt,on.minute=on.minutes=tn,on.second=on.seconds=nn,on.millisecond=on.milliseconds=an,on.utcOffset=function(t,e,n){var r,a=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null==t)return this._isUTC?a:Ne(this);if("string"==typeof t){if(null===(t=Oe(st,t)))return this}else Math.abs(t)<16&&!n&&(t*=60);return!this._isUTC&&e&&(r=Ne(this)),this._offset=t,this._isUTC=!0,null!=r&&this.add(r,"m"),a!==t&&(!e||this._changeInProgress?Ye(this,Ie(t-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,i.updateOffset(this,!0),this._changeInProgress=null)),this},on.utc=function(t){return this.utcOffset(0,t)},on.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Ne(this),"m")),this},on.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=Oe(ot,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},on.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?xe(t).utcOffset():0,(this.utcOffset()-t)%60==0)},on.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},on.isLocal=function(){return!!this.isValid()&&!this._isUTC},on.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},on.isUtc=Be,on.isUTC=Be,on.zoneAbbr=function(){return this._isUTC?"UTC":""},on.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},on.dates=C("dates accessor is deprecated. Use date instead.",Qe),on.months=C("months accessor is deprecated. Use month instead",St),on.years=C("years accessor is deprecated. Use year instead",bt),on.zone=C("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()})),on.isDSTShifted=C("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var t={};if(m(t,this),(t=me(t))._a){var e=t._isUTC?d(t._a):xe(t._a);this._isDSTShifted=this.isValid()&&0<E(t._a,e.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted}));var cn=N.prototype;function un(t,e,n,r){var i=re(),a=d().set(r,e);return i[n](a,t)}function ln(t,e,n){if(c(t)&&(e=t,t=void 0),t=t||"",null!=e)return un(t,e,n,"month");var r,i=[];for(r=0;r<12;r++)i[r]=un(t,r,n,"month");return i}function hn(t,e,n,r){"boolean"==typeof t?c(e)&&(n=e,e=void 0):(e=t,t=!1,c(n=e)&&(n=e,e=void 0)),e=e||"";var i,a=re(),o=t?a._week.dow:0;if(null!=n)return un(e,(n+o)%7,r,"day");var s=[];for(i=0;i<7;i++)s[i]=un(e,(i+o)%7,r,"day");return s}cn.calendar=function(t,e,n){var r=this._calendar[t]||this._calendar.sameElse;return O(r)?r.call(e,n):r},cn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,(function(t){return t.slice(1)})),this._longDateFormat[t])},cn.invalidDate=function(){return this._invalidDate},cn.ordinal=function(t){return this._ordinal.replace("%d",t)},cn.preparse=sn,cn.postformat=sn,cn.relativeTime=function(t,e,n,r){var i=this._relativeTime[n];return O(i)?i(t,e,n,r):i.replace(/%d/i,t)},cn.pastFuture=function(t,e){var n=this._relativeTime[0<t?"future":"past"];return O(n)?n(e):n.replace(/%s/i,e)},cn.set=function(t){var e,n;for(n in t)O(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},cn.months=function(t,e){return t?a(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Et).test(e)?"format":"standalone"][t.month()]:a(this._months)?this._months:this._months.standalone},cn.monthsShort=function(t,e){return t?a(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Et.test(e)?"format":"standalone"][t.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},cn.monthsParse=function(t,e,n){var r,i,a;if(this._monthsParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],r=0;r<12;++r)a=d([2e3,r]),this._shortMonthsParse[r]=this.monthsShort(a,"").toLocaleLowerCase(),this._longMonthsParse[r]=this.months(a,"").toLocaleLowerCase();return n?"MMM"===e?-1!==(i=mt.call(this._shortMonthsParse,o))?i:null:-1!==(i=mt.call(this._longMonthsParse,o))?i:null:"MMM"===e?-1!==(i=mt.call(this._shortMonthsParse,o))?i:-1!==(i=mt.call(this._longMonthsParse,o))?i:null:-1!==(i=mt.call(this._longMonthsParse,o))?i:-1!==(i=mt.call(this._shortMonthsParse,o))?i:null}.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=d([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(a="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(a.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[r].test(t))return r;if(n&&"MMM"===e&&this._shortMonthsParse[r].test(t))return r;if(!n&&this._monthsParse[r].test(t))return r}},cn.monthsRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=Ot),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},cn.monthsShortRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=Mt),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},cn.week=function(t){return Pt(t,this._week.dow,this._week.doy).week},cn.firstDayOfYear=function(){return this._week.doy},cn.firstDayOfWeek=function(){return this._week.dow},cn.weekdays=function(t,e){var n=a(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?Ft(n,this._week.dow):t?n[t.day()]:n},cn.weekdaysMin=function(t){return!0===t?Ft(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin},cn.weekdaysShort=function(t){return!0===t?Ft(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort},cn.weekdaysParse=function(t,e,n){var r,i,a;if(this._weekdaysParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)a=d([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(a,"").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(a,"").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(a,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(i=mt.call(this._weekdaysParse,o))?i:null:"ddd"===e?-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:null:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:null:"dddd"===e?-1!==(i=mt.call(this._weekdaysParse,o))?i:-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:null:"ddd"===e?-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:-1!==(i=mt.call(this._weekdaysParse,o))?i:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:null:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:-1!==(i=mt.call(this._weekdaysParse,o))?i:-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:null}.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=d([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[r]||(a="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[r]=new RegExp(a.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[r].test(t))return r;if(n&&"ddd"===e&&this._shortWeekdaysParse[r].test(t))return r;if(n&&"dd"===e&&this._minWeekdaysParse[r].test(t))return r;if(!n&&this._weekdaysParse[r].test(t))return r}},cn.weekdaysRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Wt.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=zt),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},cn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Wt.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Ut),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},cn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Wt.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=$t),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},cn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},cn.meridiem=function(t,e,n){return 11<t?n?"pm":"PM":n?"am":"AM"},ee("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===w(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),i.lang=C("moment.lang is deprecated. Use moment.locale instead.",ee),i.langData=C("moment.langData is deprecated. Use moment.localeData instead.",re);var fn=Math.abs;function dn(t,e,n,r){var i=Ie(e,n);return t._milliseconds+=r*i._milliseconds,t._days+=r*i._days,t._months+=r*i._months,t._bubble()}function pn(t){return t<0?Math.floor(t):Math.ceil(t)}function gn(t){return 4800*t/146097}function yn(t){return 146097*t/4800}function vn(t){return function(){return this.as(t)}}var mn=vn("ms"),bn=vn("s"),xn=vn("m"),_n=vn("h"),kn=vn("d"),wn=vn("w"),En=vn("M"),Tn=vn("Q"),Cn=vn("y");function An(t){return function(){return this.isValid()?this._data[t]:NaN}}var Sn=An("milliseconds"),Mn=An("seconds"),On=An("minutes"),Dn=An("hours"),Nn=An("days"),Bn=An("months"),Ln=An("years"),Pn=Math.round,In={ss:44,s:45,m:45,h:22,d:26,M:11},Fn=Math.abs;function jn(t){return(0<t)-(t<0)||+t}function Rn(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n=Fn(this._milliseconds)/1e3,r=Fn(this._days),i=Fn(this._months);e=k((t=k(n/60))/60),n%=60,t%=60;var a=k(i/12),o=i%=12,s=r,c=e,u=t,l=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var f=h<0?"-":"",d=jn(this._months)!==jn(h)?"-":"",p=jn(this._days)!==jn(h)?"-":"",g=jn(this._milliseconds)!==jn(h)?"-":"";return f+"P"+(a?d+a+"Y":"")+(o?d+o+"M":"")+(s?p+s+"D":"")+(c||u||l?"T":"")+(c?g+c+"H":"")+(u?g+u+"M":"")+(l?g+l+"S":"")}var Yn=Te.prototype;return Yn.isValid=function(){return this._isValid},Yn.abs=function(){var t=this._data;return this._milliseconds=fn(this._milliseconds),this._days=fn(this._days),this._months=fn(this._months),t.milliseconds=fn(t.milliseconds),t.seconds=fn(t.seconds),t.minutes=fn(t.minutes),t.hours=fn(t.hours),t.months=fn(t.months),t.years=fn(t.years),this},Yn.add=function(t,e){return dn(this,t,e,1)},Yn.subtract=function(t,e){return dn(this,t,e,-1)},Yn.as=function(t){if(!this.isValid())return NaN;var e,n,r=this._milliseconds;if("month"===(t=P(t))||"quarter"===t||"year"===t)switch(e=this._days+r/864e5,n=this._months+gn(e),t){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(e=this._days+Math.round(yn(this._months)),t){case"week":return e/7+r/6048e5;case"day":return e+r/864e5;case"hour":return 24*e+r/36e5;case"minute":return 1440*e+r/6e4;case"second":return 86400*e+r/1e3;case"millisecond":return Math.floor(864e5*e)+r;default:throw new Error("Unknown unit "+t)}},Yn.asMilliseconds=mn,Yn.asSeconds=bn,Yn.asMinutes=xn,Yn.asHours=_n,Yn.asDays=kn,Yn.asWeeks=wn,Yn.asMonths=En,Yn.asQuarters=Tn,Yn.asYears=Cn,Yn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*w(this._months/12):NaN},Yn._bubble=function(){var t,e,n,r,i,a=this._milliseconds,o=this._days,s=this._months,c=this._data;return 0<=a&&0<=o&&0<=s||a<=0&&o<=0&&s<=0||(a+=864e5*pn(yn(s)+o),s=o=0),c.milliseconds=a%1e3,t=k(a/1e3),c.seconds=t%60,e=k(t/60),c.minutes=e%60,n=k(e/60),c.hours=n%24,s+=i=k(gn(o+=k(n/24))),o-=pn(yn(i)),r=k(s/12),s%=12,c.days=o,c.months=s,c.years=r,this},Yn.clone=function(){return Ie(this)},Yn.get=function(t){return t=P(t),this.isValid()?this[t+"s"]():NaN},Yn.milliseconds=Sn,Yn.seconds=Mn,Yn.minutes=On,Yn.hours=Dn,Yn.days=Nn,Yn.weeks=function(){return k(this.days()/7)},Yn.months=Bn,Yn.years=Ln,Yn.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e,n,r,i,a,o,s,c,u,l,h=this.localeData(),f=(e=!t,n=h,r=Ie(this).abs(),i=Pn(r.as("s")),a=Pn(r.as("m")),o=Pn(r.as("h")),s=Pn(r.as("d")),c=Pn(r.as("M")),u=Pn(r.as("y")),(l=i<=In.ss&&["s",i]||i<In.s&&["ss",i]||a<=1&&["m"]||a<In.m&&["mm",a]||o<=1&&["h"]||o<In.h&&["hh",o]||s<=1&&["d"]||s<In.d&&["dd",s]||c<=1&&["M"]||c<In.M&&["MM",c]||u<=1&&["y"]||["yy",u])[2]=e,l[3]=0<+this,l[4]=n,function(t,e,n,r,i){return i.relativeTime(e||1,!!n,t,r)}.apply(null,l));return t&&(f=h.pastFuture(+this,f)),h.postformat(f)},Yn.toISOString=Rn,Yn.toString=Rn,Yn.toJSON=Rn,Yn.locale=We,Yn.localeData=Ve,Yn.toIsoString=C("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Rn),Yn.lang=He,W("X",0,0,"unix"),W("x",0,0,"valueOf"),lt("x",at),lt("X",/[+-]?\d+(\.\d{1,3})?/),pt("X",(function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))})),pt("x",(function(t,e,n){n._d=new Date(w(t))})),i.version="2.24.0",e=xe,i.fn=on,i.min=function(){return we("isBefore",[].slice.call(arguments,0))},i.max=function(){return we("isAfter",[].slice.call(arguments,0))},i.now=function(){return Date.now?Date.now():+new Date},i.utc=d,i.unix=function(t){return xe(1e3*t)},i.months=function(t,e){return ln(t,e,"months")},i.isDate=u,i.locale=ee,i.invalid=y,i.duration=Ie,i.isMoment=_,i.weekdays=function(t,e,n){return hn(t,e,n,"weekdays")},i.parseZone=function(){return xe.apply(null,arguments).parseZone()},i.localeData=re,i.isDuration=Ce,i.monthsShort=function(t,e){return ln(t,e,"monthsShort")},i.weekdaysMin=function(t,e,n){return hn(t,e,n,"weekdaysMin")},i.defineLocale=ne,i.updateLocale=function(t,e){if(null!=e){var n,r,i=Zt;null!=(r=te(t))&&(i=r._config),(n=new N(e=D(i,e))).parentLocale=Jt[t],Jt[t]=n,ee(t)}else null!=Jt[t]&&(null!=Jt[t].parentLocale?Jt[t]=Jt[t].parentLocale:null!=Jt[t]&&delete Jt[t]);return Jt[t]},i.locales=function(){return A(Jt)},i.weekdaysShort=function(t,e,n){return hn(t,e,n,"weekdaysShort")},i.normalizeUnits=P,i.relativeTimeRounding=function(t){return void 0===t?Pn:"function"==typeof t&&(Pn=t,!0)},i.relativeTimeThreshold=function(t,e){return void 0!==In[t]&&(void 0===e?In[t]:(In[t]=e,"s"===t&&(In.ss=e-1),!0))},i.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},i.prototype=on,i.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},i}()}).call(this,n(7)(t))},function(t,e,n){var r=n(37),i=n(80);t.exports=function(t){return null!=t&&i(t.length)&&!r(t)}},function(t,e,n){var r=n(256),i=n(266),a=n(35),o=n(5),s=n(273);t.exports=function(t){return"function"==typeof t?t:null==t?a:"object"==typeof t?o(t)?i(t[0],t[1]):r(t):s(t)}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,9],n=[1,7],r=[1,6],i=[1,8],a=[1,20,21,22,23,38,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],o=[2,10],s=[1,20],c=[1,21],u=[1,22],l=[1,23],h=[1,30],f=[1,54],d=[1,32],p=[1,33],g=[1,34],y=[1,35],v=[1,36],m=[1,48],b=[1,43],x=[1,45],_=[1,40],k=[1,44],w=[1,47],E=[1,51],T=[1,52],C=[1,53],A=[1,42],S=[1,46],M=[1,49],O=[1,50],D=[1,41],N=[1,57],B=[1,62],L=[1,20,21,22,23,38,42,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],P=[1,66],I=[1,65],F=[1,67],j=[20,21,23,69,70],R=[1,88],Y=[1,93],z=[1,90],U=[1,95],$=[1,98],W=[1,96],H=[1,97],V=[1,91],G=[1,103],q=[1,102],X=[1,92],Z=[1,94],J=[1,99],K=[1,100],Q=[1,101],tt=[1,104],et=[20,21,22,23,69,70],nt=[20,21,22,23,47,69,70],rt=[20,21,22,23,40,46,47,49,51,53,55,57,59,61,62,64,69,70,80,94,95,98,99,100,102,103,109,110,111,112,113,114],it=[20,21,23],at=[20,21,23,46,69,70,80,94,95,98,99,100,102,103,109,110,111,112,113,114],ot=[1,12,20,21,22,23,24,38,42,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],st=[46,80,94,95,98,99,100,102,103,109,110,111,112,113,114],ct=[1,136],ut=[1,144],lt=[1,145],ht=[1,146],ft=[1,147],dt=[1,131],pt=[1,132],gt=[1,128],yt=[1,139],vt=[1,140],mt=[1,141],bt=[1,142],xt=[1,143],_t=[1,148],kt=[1,149],wt=[1,134],Et=[1,137],Tt=[1,133],Ct=[1,130],At=[20,21,22,23,38,42,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],St=[1,152],Mt=[20,21,22,23,26,46,80,94,95,98,99,100,102,103,109,110,111,112,113,114],Ot=[20,21,22,23,24,26,38,40,41,42,46,50,52,54,56,58,60,61,63,65,69,70,71,75,76,77,78,79,80,81,84,94,95,98,99,100,102,103,104,105,109,110,111,112,113,114],Dt=[12,21,22,24],Nt=[22,95],Bt=[1,233],Lt=[1,237],Pt=[1,234],It=[1,231],Ft=[1,228],jt=[1,229],Rt=[1,230],Yt=[1,232],zt=[1,235],Ut=[1,236],$t=[1,238],Wt=[1,255],Ht=[20,21,23,95],Vt=[20,21,22,23,75,91,94,95,98,99,100,101,102,103,104],Gt={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,directive:5,openDirective:6,typeDirective:7,closeDirective:8,separator:9,":":10,argDirective:11,open_directive:12,type_directive:13,arg_directive:14,close_directive:15,graphConfig:16,document:17,line:18,statement:19,SEMI:20,NEWLINE:21,SPACE:22,EOF:23,GRAPH:24,NODIR:25,DIR:26,FirstStmtSeperator:27,ending:28,endToken:29,spaceList:30,spaceListNewline:31,verticeStatement:32,styleStatement:33,linkStyleStatement:34,classDefStatement:35,classStatement:36,clickStatement:37,subgraph:38,text:39,SQS:40,SQE:41,end:42,link:43,node:44,vertex:45,AMP:46,STYLE_SEPARATOR:47,idString:48,PS:49,PE:50,"(-":51,"-)":52,STADIUMSTART:53,STADIUMEND:54,SUBROUTINESTART:55,SUBROUTINEEND:56,CYLINDERSTART:57,CYLINDEREND:58,DIAMOND_START:59,DIAMOND_STOP:60,TAGEND:61,TRAPSTART:62,TRAPEND:63,INVTRAPSTART:64,INVTRAPEND:65,linkStatement:66,arrowText:67,TESTSTR:68,START_LINK:69,LINK:70,PIPE:71,textToken:72,STR:73,keywords:74,STYLE:75,LINKSTYLE:76,CLASSDEF:77,CLASS:78,CLICK:79,DOWN:80,UP:81,textNoTags:82,textNoTagsToken:83,DEFAULT:84,stylesOpt:85,alphaNum:86,CALLBACKNAME:87,CALLBACKARGS:88,HREF:89,LINK_TARGET:90,HEX:91,numList:92,INTERPOLATE:93,NUM:94,COMMA:95,style:96,styleComponent:97,ALPHA:98,COLON:99,MINUS:100,UNIT:101,BRKT:102,DOT:103,PCT:104,TAGSTART:105,alphaNumToken:106,idStringToken:107,alphaNumStatement:108,PUNCTUATION:109,UNICODE_TEXT:110,PLUS:111,EQUALS:112,MULT:113,UNDERSCORE:114,graphCodeTokens:115,ARROW_CROSS:116,ARROW_POINT:117,ARROW_CIRCLE:118,ARROW_OPEN:119,QUOTE:120,$accept:0,$end:1},terminals_:{2:"error",10:":",12:"open_directive",13:"type_directive",14:"arg_directive",15:"close_directive",20:"SEMI",21:"NEWLINE",22:"SPACE",23:"EOF",24:"GRAPH",25:"NODIR",26:"DIR",38:"subgraph",40:"SQS",41:"SQE",42:"end",46:"AMP",47:"STYLE_SEPARATOR",49:"PS",50:"PE",51:"(-",52:"-)",53:"STADIUMSTART",54:"STADIUMEND",55:"SUBROUTINESTART",56:"SUBROUTINEEND",57:"CYLINDERSTART",58:"CYLINDEREND",59:"DIAMOND_START",60:"DIAMOND_STOP",61:"TAGEND",62:"TRAPSTART",63:"TRAPEND",64:"INVTRAPSTART",65:"INVTRAPEND",68:"TESTSTR",69:"START_LINK",70:"LINK",71:"PIPE",73:"STR",75:"STYLE",76:"LINKSTYLE",77:"CLASSDEF",78:"CLASS",79:"CLICK",80:"DOWN",81:"UP",84:"DEFAULT",87:"CALLBACKNAME",88:"CALLBACKARGS",89:"HREF",90:"LINK_TARGET",91:"HEX",93:"INTERPOLATE",94:"NUM",95:"COMMA",98:"ALPHA",99:"COLON",100:"MINUS",101:"UNIT",102:"BRKT",103:"DOT",104:"PCT",105:"TAGSTART",109:"PUNCTUATION",110:"UNICODE_TEXT",111:"PLUS",112:"EQUALS",113:"MULT",114:"UNDERSCORE",116:"ARROW_CROSS",117:"ARROW_POINT",118:"ARROW_CIRCLE",119:"ARROW_OPEN",120:"QUOTE"},productions_:[0,[3,1],[3,2],[5,4],[5,6],[6,1],[7,1],[11,1],[8,1],[4,2],[17,0],[17,2],[18,1],[18,1],[18,1],[18,1],[18,1],[16,2],[16,2],[16,2],[16,3],[28,2],[28,1],[29,1],[29,1],[29,1],[27,1],[27,1],[27,2],[31,2],[31,2],[31,1],[31,1],[30,2],[30,1],[19,2],[19,2],[19,2],[19,2],[19,2],[19,2],[19,9],[19,6],[19,4],[9,1],[9,1],[9,1],[32,3],[32,4],[32,2],[32,1],[44,1],[44,5],[44,3],[45,4],[45,6],[45,4],[45,4],[45,4],[45,4],[45,4],[45,4],[45,6],[45,4],[45,4],[45,4],[45,4],[45,4],[45,1],[43,2],[43,3],[43,3],[43,1],[43,3],[66,1],[67,3],[39,1],[39,2],[39,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[82,1],[82,2],[35,5],[35,5],[36,5],[37,2],[37,4],[37,3],[37,5],[37,2],[37,4],[37,4],[37,6],[37,2],[37,4],[37,2],[37,4],[37,4],[37,6],[33,5],[33,5],[34,5],[34,5],[34,9],[34,9],[34,7],[34,7],[92,1],[92,3],[85,1],[85,3],[96,1],[96,2],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[72,1],[72,1],[72,1],[72,1],[72,1],[72,1],[83,1],[83,1],[83,1],[83,1],[48,1],[48,2],[86,1],[86,2],[108,1],[108,1],[108,1],[108,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 5:r.parseDirective("%%{","open_directive");break;case 6:r.parseDirective(a[s],"type_directive");break;case 7:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 8:r.parseDirective("}%%","close_directive","flowchart");break;case 10:this.$=[];break;case 11:a[s]!==[]&&a[s-1].push(a[s]),this.$=a[s-1];break;case 12:case 76:case 78:case 90:case 146:case 148:case 149:this.$=a[s];break;case 19:r.setDirection("TB"),this.$="TB";break;case 20:r.setDirection(a[s-1]),this.$=a[s-1];break;case 35:this.$=a[s-1].nodes;break;case 36:case 37:case 38:case 39:case 40:this.$=[];break;case 41:this.$=r.addSubGraph(a[s-6],a[s-1],a[s-4]);break;case 42:this.$=r.addSubGraph(a[s-3],a[s-1],a[s-3]);break;case 43:this.$=r.addSubGraph(void 0,a[s-1],void 0);break;case 47:r.addLink(a[s-2].stmt,a[s],a[s-1]),this.$={stmt:a[s],nodes:a[s].concat(a[s-2].nodes)};break;case 48:r.addLink(a[s-3].stmt,a[s-1],a[s-2]),this.$={stmt:a[s-1],nodes:a[s-1].concat(a[s-3].nodes)};break;case 49:this.$={stmt:a[s-1],nodes:a[s-1]};break;case 50:this.$={stmt:a[s],nodes:a[s]};break;case 51:this.$=[a[s]];break;case 52:this.$=a[s-4].concat(a[s]);break;case 53:this.$=[a[s-2]],r.setClass(a[s-2],a[s]);break;case 54:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"square");break;case 55:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"circle");break;case 56:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"ellipse");break;case 57:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"stadium");break;case 58:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"subroutine");break;case 59:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"cylinder");break;case 60:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"round");break;case 61:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"diamond");break;case 62:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"hexagon");break;case 63:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"odd");break;case 64:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"trapezoid");break;case 65:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"inv_trapezoid");break;case 66:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_right");break;case 67:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_left");break;case 68:this.$=a[s],r.addVertex(a[s]);break;case 69:a[s-1].text=a[s],this.$=a[s-1];break;case 70:case 71:a[s-2].text=a[s-1],this.$=a[s-2];break;case 72:this.$=a[s];break;case 73:var c=r.destructLink(a[s],a[s-2]);this.$={type:c.type,stroke:c.stroke,length:c.length,text:a[s-1]};break;case 74:c=r.destructLink(a[s]);this.$={type:c.type,stroke:c.stroke,length:c.length};break;case 75:this.$=a[s-1];break;case 77:case 91:case 147:this.$=a[s-1]+""+a[s];break;case 92:case 93:this.$=a[s-4],r.addClass(a[s-2],a[s]);break;case 94:this.$=a[s-4],r.setClass(a[s-2],a[s]);break;case 95:case 103:this.$=a[s-1],r.setClickEvent(a[s-1],a[s]);break;case 96:case 104:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 97:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 98:this.$=a[s-4],r.setClickEvent(a[s-4],a[s-3],a[s-2]),r.setTooltip(a[s-4],a[s]);break;case 99:case 105:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 100:case 106:this.$=a[s-3],r.setLink(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 101:case 107:this.$=a[s-3],r.setLink(a[s-3],a[s-2],a[s]);break;case 102:case 108:this.$=a[s-5],r.setLink(a[s-5],a[s-4],a[s]),r.setTooltip(a[s-5],a[s-2]);break;case 109:this.$=a[s-4],r.addVertex(a[s-2],void 0,void 0,a[s]);break;case 110:case 112:this.$=a[s-4],r.updateLink(a[s-2],a[s]);break;case 111:this.$=a[s-4],r.updateLink([a[s-2]],a[s]);break;case 113:this.$=a[s-8],r.updateLinkInterpolate([a[s-6]],a[s-2]),r.updateLink([a[s-6]],a[s]);break;case 114:this.$=a[s-8],r.updateLinkInterpolate(a[s-6],a[s-2]),r.updateLink(a[s-6],a[s]);break;case 115:this.$=a[s-6],r.updateLinkInterpolate([a[s-4]],a[s]);break;case 116:this.$=a[s-6],r.updateLinkInterpolate(a[s-4],a[s]);break;case 117:case 119:this.$=[a[s]];break;case 118:case 120:a[s-2].push(a[s]),this.$=a[s-2];break;case 122:this.$=a[s-1]+a[s];break;case 144:this.$=a[s];break;case 145:this.$=a[s-1]+""+a[s];break;case 150:this.$="v";break;case 151:this.$="-"}},table:[{3:1,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},{1:[3]},{1:[2,1]},{3:10,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},t(a,o,{17:11}),{7:12,13:[1,13]},{16:14,21:n,22:r,24:i},{16:15,21:n,22:r,24:i},{25:[1,16],26:[1,17]},{13:[2,5]},{1:[2,2]},{1:[2,9],18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{8:55,10:[1,56],15:N},t([10,15],[2,6]),t(a,[2,17]),t(a,[2,18]),t(a,[2,19]),{20:[1,59],21:[1,60],22:B,27:58,30:61},t(L,[2,11]),t(L,[2,12]),t(L,[2,13]),t(L,[2,14]),t(L,[2,15]),t(L,[2,16]),{9:63,20:P,21:I,23:F,43:64,66:68,69:[1,69],70:[1,70]},{9:71,20:P,21:I,23:F},{9:72,20:P,21:I,23:F},{9:73,20:P,21:I,23:F},{9:74,20:P,21:I,23:F},{9:75,20:P,21:I,23:F},{9:77,20:P,21:I,22:[1,76],23:F},t(j,[2,50],{30:78,22:B}),{22:[1,79]},{22:[1,80]},{22:[1,81]},{22:[1,82]},{26:R,46:Y,73:[1,86],80:z,86:85,87:[1,83],89:[1,84],94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(et,[2,51],{47:[1,105]}),t(nt,[2,68],{107:116,40:[1,106],46:f,49:[1,107],51:[1,108],53:[1,109],55:[1,110],57:[1,111],59:[1,112],61:[1,113],62:[1,114],64:[1,115],80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,109:C,110:A,111:S,112:M,113:O,114:D}),t(rt,[2,144]),t(rt,[2,165]),t(rt,[2,166]),t(rt,[2,167]),t(rt,[2,168]),t(rt,[2,169]),t(rt,[2,170]),t(rt,[2,171]),t(rt,[2,172]),t(rt,[2,173]),t(rt,[2,174]),t(rt,[2,175]),t(rt,[2,176]),t(rt,[2,177]),t(rt,[2,178]),t(rt,[2,179]),{9:117,20:P,21:I,23:F},{11:118,14:[1,119]},t(it,[2,8]),t(a,[2,20]),t(a,[2,26]),t(a,[2,27]),{21:[1,120]},t(at,[2,34],{30:121,22:B}),t(L,[2,35]),{44:122,45:37,46:f,48:38,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},t(ot,[2,44]),t(ot,[2,45]),t(ot,[2,46]),t(st,[2,72],{67:123,68:[1,124],71:[1,125]}),{22:ct,24:ut,26:lt,38:ht,39:126,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t([46,68,71,80,94,95,98,99,100,102,103,109,110,111,112,113,114],[2,74]),t(L,[2,36]),t(L,[2,37]),t(L,[2,38]),t(L,[2,39]),t(L,[2,40]),{22:ct,24:ut,26:lt,38:ht,39:150,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(At,o,{17:151}),t(j,[2,49],{46:St}),{26:R,46:Y,80:z,86:153,91:[1,154],94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},{84:[1,155],92:156,94:[1,157]},{26:R,46:Y,80:z,84:[1,158],86:159,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},{26:R,46:Y,80:z,86:160,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(it,[2,95],{22:[1,161],88:[1,162]}),t(it,[2,99],{22:[1,163]}),t(it,[2,103],{106:89,108:165,22:[1,164],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),t(it,[2,105],{22:[1,166]}),t(Mt,[2,146]),t(Mt,[2,148]),t(Mt,[2,149]),t(Mt,[2,150]),t(Mt,[2,151]),t(Ot,[2,152]),t(Ot,[2,153]),t(Ot,[2,154]),t(Ot,[2,155]),t(Ot,[2,156]),t(Ot,[2,157]),t(Ot,[2,158]),t(Ot,[2,159]),t(Ot,[2,160]),t(Ot,[2,161]),t(Ot,[2,162]),t(Ot,[2,163]),t(Ot,[2,164]),{46:f,48:167,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:ct,24:ut,26:lt,38:ht,39:168,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:170,42:ft,46:Y,49:[1,169],61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:171,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:172,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:173,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:174,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:175,42:ft,46:Y,59:[1,176],61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:177,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:178,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:179,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(rt,[2,145]),t(Dt,[2,3]),{8:180,15:N},{15:[2,7]},t(a,[2,28]),t(at,[2,33]),t(j,[2,47],{30:181,22:B}),t(st,[2,69],{22:[1,182]}),{22:[1,183]},{22:ct,24:ut,26:lt,38:ht,39:184,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,69:pt,70:[1,185],72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(Ot,[2,76]),t(Ot,[2,78]),t(Ot,[2,134]),t(Ot,[2,135]),t(Ot,[2,136]),t(Ot,[2,137]),t(Ot,[2,138]),t(Ot,[2,139]),t(Ot,[2,140]),t(Ot,[2,141]),t(Ot,[2,142]),t(Ot,[2,143]),t(Ot,[2,79]),t(Ot,[2,80]),t(Ot,[2,81]),t(Ot,[2,82]),t(Ot,[2,83]),t(Ot,[2,84]),t(Ot,[2,85]),t(Ot,[2,86]),t(Ot,[2,87]),t(Ot,[2,88]),t(Ot,[2,89]),{9:188,20:P,21:I,22:ct,23:F,24:ut,26:lt,38:ht,40:[1,187],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,189],44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:B,30:190},{22:[1,191],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:165,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:[1,192]},{22:[1,193]},{22:[1,194],95:[1,195]},t(Nt,[2,117]),{22:[1,196]},{22:[1,197],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:165,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:[1,198],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:165,109:X,110:Z,111:J,112:K,113:Q,114:tt},{73:[1,199]},t(it,[2,97],{22:[1,200]}),{73:[1,201],90:[1,202]},{73:[1,203]},t(Mt,[2,147]),{73:[1,204],90:[1,205]},t(et,[2,53],{107:116,46:f,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,109:C,110:A,111:S,112:M,113:O,114:D}),{22:ct,24:ut,26:lt,38:ht,41:[1,206],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:207,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,50:[1,208],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,52:[1,209],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,54:[1,210],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,56:[1,211],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,58:[1,212],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,60:[1,213],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:214,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,41:[1,215],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,63:[1,216],65:[1,217],69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,63:[1,219],65:[1,218],69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{9:220,20:P,21:I,23:F},t(j,[2,48],{46:St}),t(st,[2,71]),t(st,[2,70]),{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,69:pt,71:[1,221],72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(st,[2,73]),t(Ot,[2,77]),{22:ct,24:ut,26:lt,38:ht,39:222,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(At,o,{17:223}),t(L,[2,43]),{45:224,46:f,48:38,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:Bt,75:Lt,85:225,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:239,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:240,91:Pt,93:[1,241],94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:242,91:Pt,93:[1,243],94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{94:[1,244]},{22:Bt,75:Lt,85:245,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:246,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{26:R,46:Y,80:z,86:247,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(it,[2,96]),{73:[1,248]},t(it,[2,100],{22:[1,249]}),t(it,[2,101]),t(it,[2,104]),t(it,[2,106],{22:[1,250]}),t(it,[2,107]),t(nt,[2,54]),{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,50:[1,251],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(nt,[2,60]),t(nt,[2,56]),t(nt,[2,57]),t(nt,[2,58]),t(nt,[2,59]),t(nt,[2,61]),{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,60:[1,252],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(nt,[2,63]),t(nt,[2,64]),t(nt,[2,66]),t(nt,[2,65]),t(nt,[2,67]),t(Dt,[2,4]),t([22,46,80,94,95,98,99,100,102,103,109,110,111,112,113,114],[2,75]),{22:ct,24:ut,26:lt,38:ht,41:[1,253],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,254],44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},t(et,[2,52]),t(it,[2,109],{95:Wt}),t(Ht,[2,119],{97:256,22:Bt,75:Lt,91:Pt,94:It,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t}),t(Vt,[2,121]),t(Vt,[2,123]),t(Vt,[2,124]),t(Vt,[2,125]),t(Vt,[2,126]),t(Vt,[2,127]),t(Vt,[2,128]),t(Vt,[2,129]),t(Vt,[2,130]),t(Vt,[2,131]),t(Vt,[2,132]),t(Vt,[2,133]),t(it,[2,110],{95:Wt}),t(it,[2,111],{95:Wt}),{22:[1,257]},t(it,[2,112],{95:Wt}),{22:[1,258]},t(Nt,[2,118]),t(it,[2,92],{95:Wt}),t(it,[2,93],{95:Wt}),t(it,[2,94],{106:89,108:165,26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),t(it,[2,98]),{90:[1,259]},{90:[1,260]},{50:[1,261]},{60:[1,262]},{9:263,20:P,21:I,23:F},t(L,[2,42]),{22:Bt,75:Lt,91:Pt,94:It,96:264,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},t(Vt,[2,122]),{26:R,46:Y,80:z,86:265,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},{26:R,46:Y,80:z,86:266,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(it,[2,102]),t(it,[2,108]),t(nt,[2,55]),t(nt,[2,62]),t(At,o,{17:267}),t(Ht,[2,120],{97:256,22:Bt,75:Lt,91:Pt,94:It,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t}),t(it,[2,115],{106:89,108:165,22:[1,268],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),t(it,[2,116],{106:89,108:165,22:[1,269],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,270],44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:Bt,75:Lt,85:271,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:272,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},t(L,[2,41]),t(it,[2,113],{95:Wt}),t(it,[2,114],{95:Wt})],defaultActions:{2:[2,1],9:[2,5],10:[2,2],119:[2,7]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},qt={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),12;case 1:return this.begin("type_directive"),13;case 2:return this.popState(),this.begin("arg_directive"),10;case 3:return this.popState(),this.popState(),15;case 4:return 14;case 5:case 6:break;case 7:this.begin("string");break;case 8:this.popState();break;case 9:return"STR";case 10:return 75;case 11:return 84;case 12:return 76;case 13:return 93;case 14:return 77;case 15:return 78;case 16:this.begin("href");break;case 17:this.popState();break;case 18:return 89;case 19:this.begin("callbackname");break;case 20:this.popState();break;case 21:this.popState(),this.begin("callbackargs");break;case 22:return 87;case 23:this.popState();break;case 24:return 88;case 25:this.begin("click");break;case 26:this.popState();break;case 27:return 79;case 28:case 29:return t.lex.firstGraph()&&this.begin("dir"),24;case 30:return 38;case 31:return 42;case 32:case 33:case 34:case 35:return 90;case 36:return this.popState(),25;case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:return this.popState(),26;case 47:return 94;case 48:return 102;case 49:return 47;case 50:return 99;case 51:return 46;case 52:return 20;case 53:return 95;case 54:return 113;case 55:case 56:case 57:return 70;case 58:case 59:case 60:return 69;case 61:return 51;case 62:return 52;case 63:return 53;case 64:return 54;case 65:return 55;case 66:return 56;case 67:return 57;case 68:return 58;case 69:return 100;case 70:return 103;case 71:return 114;case 72:return 111;case 73:return 104;case 74:case 75:return 112;case 76:return 105;case 77:return 61;case 78:return 81;case 79:return"SEP";case 80:return 80;case 81:return 98;case 82:return 63;case 83:return 62;case 84:return 65;case 85:return 64;case 86:return 109;case 87:return 110;case 88:return 71;case 89:return 49;case 90:return 50;case 91:return 40;case 92:return 41;case 93:return 59;case 94:return 60;case 95:return 120;case 96:return 21;case 97:return 22;case 98:return 23}},rules:[/^(?:%%\{)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)[^\n]*)/,/^(?:[^\}]%%[^\n]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:click[\s]+)/,/^(?:[\s\n])/,/^(?:[^\s\n]*)/,/^(?:graph\b)/,/^(?:flowchart\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:(\r?\n)*\s*\n)/,/^(?:\s*LR\b)/,/^(?:\s*RL\b)/,/^(?:\s*TB\b)/,/^(?:\s*BT\b)/,/^(?:\s*TD\b)/,/^(?:\s*BR\b)/,/^(?:\s*<)/,/^(?:\s*>)/,/^(?:\s*\^)/,/^(?:\s*v\b)/,/^(?:[0-9]+)/,/^(?:#)/,/^(?::::)/,/^(?::)/,/^(?:&)/,/^(?:;)/,/^(?:,)/,/^(?:\*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:\(-)/,/^(?:-\))/,/^(?:\(\[)/,/^(?:\]\))/,/^(?:\[\[)/,/^(?:\]\])/,/^(?:\[\()/,/^(?:\)\])/,/^(?:-)/,/^(?:\.)/,/^(?:[\_])/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:<)/,/^(?:>)/,/^(?:\^)/,/^(?:\\\|)/,/^(?:v\b)/,/^(?:[A-Za-z]+)/,/^(?:\\\])/,/^(?:\[\/)/,/^(?:\/\])/,/^(?:\[\\)/,/^(?:[!"#$%&'*+,-.`?\\_/])/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\|)/,/^(?:\()/,/^(?:\))/,/^(?:\[)/,/^(?:\])/,/^(?:\{)/,/^(?:\})/,/^(?:")/,/^(?:(\r?\n)+)/,/^(?:\s)/,/^(?:$)/],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[23,24],inclusive:!1},callbackname:{rules:[20,21,22],inclusive:!1},href:{rules:[17,18],inclusive:!1},click:{rules:[26,27],inclusive:!1},vertex:{rules:[],inclusive:!1},dir:{rules:[36,37,38,39,40,41,42,43,44,45,46],inclusive:!1},string:{rules:[8,9],inclusive:!1},INITIAL:{rules:[0,5,6,7,10,11,12,13,14,15,16,19,25,28,29,30,31,32,33,34,35,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98],inclusive:!0}}};function Xt(){this.yy={}}return Gt.lexer=qt,Xt.prototype=Gt,Gt.Parser=Xt,new Xt}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,3],n=[1,5],r=[7,9,11,12,13,14,15,16,17,18,20,27,32],i=[1,15],a=[1,16],o=[1,17],s=[1,18],c=[1,19],u=[1,20],l=[1,21],h=[1,23],f=[1,25],d=[1,28],p=[5,7,9,11,12,13,14,15,16,17,18,20,27,32],g={trace:function(){},yy:{},symbols_:{error:2,start:3,directive:4,gantt:5,document:6,EOF:7,line:8,SPACE:9,statement:10,NL:11,dateFormat:12,inclusiveEndDates:13,axisFormat:14,excludes:15,todayMarker:16,title:17,section:18,clickStatement:19,taskTxt:20,taskData:21,openDirective:22,typeDirective:23,closeDirective:24,":":25,argDirective:26,click:27,callbackname:28,callbackargs:29,href:30,clickStatementDebug:31,open_directive:32,type_directive:33,arg_directive:34,close_directive:35,$accept:0,$end:1},terminals_:{2:"error",5:"gantt",7:"EOF",9:"SPACE",11:"NL",12:"dateFormat",13:"inclusiveEndDates",14:"axisFormat",15:"excludes",16:"todayMarker",17:"title",18:"section",20:"taskTxt",21:"taskData",25:":",27:"click",28:"callbackname",29:"callbackargs",30:"href",32:"open_directive",33:"type_directive",34:"arg_directive",35:"close_directive"},productions_:[0,[3,2],[3,3],[6,0],[6,2],[8,2],[8,1],[8,1],[8,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,2],[10,1],[4,4],[4,6],[19,2],[19,3],[19,3],[19,4],[19,3],[19,4],[19,2],[31,2],[31,3],[31,3],[31,4],[31,3],[31,4],[31,2],[22,1],[23,1],[26,1],[24,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 2:return a[s-1];case 3:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 7:case 8:this.$=[];break;case 9:r.setDateFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 10:r.enableInclusiveEndDates(),this.$=a[s].substr(18);break;case 11:r.setAxisFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 12:r.setExcludes(a[s].substr(9)),this.$=a[s].substr(9);break;case 13:r.setTodayMarker(a[s].substr(12)),this.$=a[s].substr(12);break;case 14:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 15:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 17:r.addTask(a[s-1],a[s]),this.$="task";break;case 21:this.$=a[s-1],r.setClickEvent(a[s-1],a[s],null);break;case 22:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 23:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],null),r.setLink(a[s-2],a[s]);break;case 24:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setLink(a[s-3],a[s]);break;case 25:this.$=a[s-2],r.setClickEvent(a[s-2],a[s],null),r.setLink(a[s-2],a[s-1]);break;case 26:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-1],a[s]),r.setLink(a[s-3],a[s-2]);break;case 27:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 28:case 34:this.$=a[s-1]+" "+a[s];break;case 29:case 30:case 32:this.$=a[s-2]+" "+a[s-1]+" "+a[s];break;case 31:case 33:this.$=a[s-3]+" "+a[s-2]+" "+a[s-1]+" "+a[s];break;case 35:r.parseDirective("%%{","open_directive");break;case 36:r.parseDirective(a[s],"type_directive");break;case 37:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 38:r.parseDirective("}%%","close_directive","gantt")}},table:[{3:1,4:2,5:e,22:4,32:n},{1:[3]},{3:6,4:2,5:e,22:4,32:n},t(r,[2,3],{6:7}),{23:8,33:[1,9]},{33:[2,35]},{1:[2,1]},{4:24,7:[1,10],8:11,9:[1,12],10:13,11:[1,14],12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:22,20:h,22:4,27:f,32:n},{24:26,25:[1,27],35:d},t([25,35],[2,36]),t(r,[2,8],{1:[2,2]}),t(r,[2,4]),{4:24,10:29,12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:22,20:h,22:4,27:f,32:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,9]),t(r,[2,10]),t(r,[2,11]),t(r,[2,12]),t(r,[2,13]),t(r,[2,14]),t(r,[2,15]),t(r,[2,16]),{21:[1,30]},t(r,[2,18]),{28:[1,31],30:[1,32]},{11:[1,33]},{26:34,34:[1,35]},{11:[2,38]},t(r,[2,5]),t(r,[2,17]),t(r,[2,21],{29:[1,36],30:[1,37]}),t(r,[2,27],{28:[1,38]}),t(p,[2,19]),{24:39,35:d},{35:[2,37]},t(r,[2,22],{30:[1,40]}),t(r,[2,23]),t(r,[2,25],{29:[1,41]}),{11:[1,42]},t(r,[2,24]),t(r,[2,26]),t(p,[2,20])],defaultActions:{5:[2,35],6:[2,1],28:[2,38],35:[2,37]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},y={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),32;case 1:return this.begin("type_directive"),33;case 2:return this.popState(),this.begin("arg_directive"),25;case 3:return this.popState(),this.popState(),35;case 4:return 34;case 5:case 6:case 7:break;case 8:return 11;case 9:case 10:case 11:break;case 12:this.begin("href");break;case 13:this.popState();break;case 14:return 30;case 15:this.begin("callbackname");break;case 16:this.popState();break;case 17:this.popState(),this.begin("callbackargs");break;case 18:return 28;case 19:this.popState();break;case 20:return 29;case 21:this.begin("click");break;case 22:this.popState();break;case 23:return 27;case 24:return 5;case 25:return 12;case 26:return 13;case 27:return 14;case 28:return 15;case 29:return 16;case 30:return"date";case 31:return 17;case 32:return 18;case 33:return 20;case 34:return 21;case 35:return 25;case 36:return 7;case 37:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)*[^\n]*)/i,/^(?:[^\}]%%*[^\n]*)/i,/^(?:%%*[^\n]*[\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:inclusiveEndDates\b)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:todayMarker\s[^\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[19,20],inclusive:!1},callbackname:{rules:[16,17,18],inclusive:!1},href:{rules:[13,14],inclusive:!1},click:{rules:[22,23],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,15,21,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inclusive:!0}}};function v(){this.yy={}}return g.lexer=y,v.prototype=g,g.Parser=v,new v}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,17,18,19,21],i=[1,15],a=[1,16],o=[1,17],s=[1,21],c=[4,6,9,11,17,18,19,21],u={trace:function(){},yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,title:17,section:18,taskName:19,taskData:20,open_directive:21,type_directive:22,arg_directive:23,close_directive:24,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",17:"title",18:"section",19:"taskName",20:"taskData",21:"open_directive",22:"type_directive",23:"arg_directive",24:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,1],[10,2],[10,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 3:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 7:case 8:this.$=[];break;case 11:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 12:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 13:r.addTask(a[s-1],a[s]),this.$="task";break;case 15:r.parseDirective("%%{","open_directive");break;case 16:r.parseDirective(a[s],"type_directive");break;case 17:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 18:r.parseDirective("}%%","close_directive","journey")}},table:[{3:1,4:e,7:3,12:4,21:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,21:n},{13:8,22:[1,9]},{22:[2,15]},{6:[1,10],7:18,8:11,9:[1,12],10:13,11:[1,14],12:4,17:i,18:a,19:o,21:n},{1:[2,2]},{14:19,15:[1,20],24:s},t([15,24],[2,16]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:18,10:22,12:4,17:i,18:a,19:o,21:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,12]),{20:[1,23]},t(r,[2,14]),{11:[1,24]},{16:25,23:[1,26]},{11:[2,18]},t(r,[2,5]),t(r,[2,13]),t(c,[2,9]),{14:27,24:s},{24:[2,17]},{11:[1,28]},t(c,[2,10])],defaultActions:{5:[2,15],7:[2,2],21:[2,18],26:[2,17]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},l={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),21;case 1:return this.begin("type_directive"),22;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),24;case 4:return 23;case 5:case 6:break;case 7:return 11;case 8:case 9:break;case 10:return 4;case 11:return 17;case 12:return 18;case 13:return 19;case 14:return 20;case 15:return 15;case 16:return 6;case 17:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:journey\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,13,14,15,16,17],inclusive:!0}}};function h(){this.yy={}}return u.lexer=l,h.prototype=u,u.Parser=h,new h}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t,e){return r.default.lang.round(i.default.parse(t)[e])}},function(t,e,n){var r=n(112),i=n(82),a=n(24);t.exports=function(t){return a(t)?r(t):i(t)}},function(t,e,n){var r;if(!r)try{r=n(0)}catch(t){}r||(r=window.d3),t.exports=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t,e,n){var a=i.default.parse(t),o=a[e],s=r.default.channel.clamp[e](o+n);return o!==s&&(a[e]=s),i.default.stringify(a)}},function(t,e,n){var r=n(210),i=n(216);t.exports=function(t,e){var n=i(t,e);return r(n)?n:void 0}},function(t,e,n){var r=n(38),i=n(212),a=n(213),o=r?r.toStringTag:void 0;t.exports=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":o&&o in Object(t)?i(t):a(t)}},function(t,e){t.exports=function(t){return t}},function(t,e){t.exports=function(t,e){return t===e||t!=t&&e!=e}},function(t,e,n){var r=n(34),i=n(11);t.exports=function(t){if(!i(t))return!1;var e=r(t);return"[object Function]"==e||"[object GeneratorFunction]"==e||"[object AsyncFunction]"==e||"[object Proxy]"==e}},function(t,e,n){var r=n(16).Symbol;t.exports=r},function(t,e,n){(function(t){var r=n(16),i=n(232),a=e&&!e.nodeType&&e,o=a&&"object"==typeof t&&t&&!t.nodeType&&t,s=o&&o.exports===a?r.Buffer:void 0,c=(s?s.isBuffer:void 0)||i;t.exports=c}).call(this,n(7)(t))},function(t,e,n){var r=n(112),i=n(236),a=n(24);t.exports=function(t){return a(t)?r(t,!0):i(t)}},function(t,e,n){var r=n(241),i=n(77),a=n(242),o=n(121),s=n(243),c=n(34),u=n(110),l=u(r),h=u(i),f=u(a),d=u(o),p=u(s),g=c;(r&&"[object DataView]"!=g(new r(new ArrayBuffer(1)))||i&&"[object Map]"!=g(new i)||a&&"[object Promise]"!=g(a.resolve())||o&&"[object Set]"!=g(new o)||s&&"[object WeakMap]"!=g(new s))&&(g=function(t){var e=c(t),n="[object Object]"==e?t.constructor:void 0,r=n?u(n):"";if(r)switch(r){case l:return"[object DataView]";case h:return"[object Map]";case f:return"[object Promise]";case d:return"[object Set]";case p:return"[object WeakMap]"}return e}),t.exports=g},function(t,e,n){var r=n(34),i=n(21);t.exports=function(t){return"symbol"==typeof t||i(t)&&"[object Symbol]"==r(t)}},function(t,e,n){var r;try{r={defaults:n(154),each:n(87),isFunction:n(37),isPlainObject:n(158),pick:n(161),has:n(93),range:n(162),uniqueId:n(163)}}catch(t){}r||(r=window._),t.exports=r},function(t){t.exports=JSON.parse('{"name":"mermaid","version":"8.9.0","description":"Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.","main":"dist/mermaid.core.js","keywords":["diagram","markdown","flowchart","sequence diagram","gantt","class diagram","git graph"],"scripts":{"build:development":"webpack --progress --colors","build:production":"yarn build:development -p --config webpack.config.prod.babel.js","build":"yarn build:development && yarn build:production","postbuild":"documentation build src/mermaidAPI.js src/config.js --shallow -f md --markdown-toc false > docs/Setup.md","build:watch":"yarn build --watch","minify":"minify ./dist/mermaid.js > ./dist/mermaid.min.js","release":"yarn build","lint":"eslint src","e2e:depr":"yarn lint && jest e2e --config e2e/jest.config.js","cypress":"percy exec -- cypress run","e2e":"start-server-and-test dev http://localhost:9000/ cypress","e2e-upd":"yarn lint && jest e2e -u --config e2e/jest.config.js","dev":"webpack-dev-server --config webpack.config.e2e.js","test":"yarn lint && jest src/.*","test:watch":"jest --watch src","prepublishOnly":"yarn build && yarn test","prepare":"yarn build"},"repository":{"type":"git","url":"https://github.com/knsv/mermaid"},"author":"Knut Sveidqvist","license":"MIT","standard":{"ignore":["**/parser/*.js","dist/**/*.js","cypress/**/*.js"],"globals":["page"]},"dependencies":{"@braintree/sanitize-url":"^3.1.0","d3":"^5.7.0","dagre":"^0.8.4","dagre-d3":"^0.6.4","entity-decode":"^2.0.2","graphlib":"^2.1.7","he":"^1.2.0","khroma":"^1.1.0","minify":"^4.1.1","moment-mini":"^2.22.1","stylis":"^3.5.2"},"devDependencies":{"@babel/core":"^7.2.2","@babel/preset-env":"^7.8.4","@babel/register":"^7.0.0","@percy/cypress":"*","babel-core":"7.0.0-bridge.0","babel-eslint":"^10.1.0","babel-jest":"^24.9.0","babel-loader":"^8.0.4","coveralls":"^3.0.2","css-loader":"^2.0.1","css-to-string-loader":"^0.1.3","cypress":"4.0.1","documentation":"^12.0.1","eslint":"^6.3.0","eslint-config-prettier":"^6.3.0","eslint-plugin-prettier":"^3.1.0","husky":"^1.2.1","identity-obj-proxy":"^3.0.0","jest":"^24.9.0","jison":"^0.4.18","moment":"^2.23.0","node-sass":"^4.12.0","prettier":"^1.18.2","puppeteer":"^1.17.0","sass-loader":"^7.1.0","start-server-and-test":"^1.10.6","terser-webpack-plugin":"^2.2.2","webpack":"^4.41.2","webpack-bundle-analyzer":"^3.7.0","webpack-cli":"^3.1.2","webpack-dev-server":"^3.4.1","webpack-node-externals":"^1.7.2","yarn-upgrade-all":"^0.5.0"},"files":["dist"],"yarn-upgrade-all":{"ignore":["babel-core"]},"sideEffects":["**/*.css","**/*.scss"],"husky":{"hooks":{"pre-push":"yarn test"}}}')},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=new(n(176).default)({r:0,g:0,b:0,a:0},"transparent");e.default=r},function(t,e,n){var r=n(58),i=n(59);t.exports=function(t,e,n,a){var o=!n;n||(n={});for(var s=-1,c=e.length;++s<c;){var u=e[s],l=a?a(n[u],t[u],u,n,t):void 0;void 0===l&&(l=t[u]),o?i(n,u,l):r(n,u,l)}return n}},function(t,e,n){var r=n(231),i=n(21),a=Object.prototype,o=a.hasOwnProperty,s=a.propertyIsEnumerable,c=r(function(){return arguments}())?r:function(t){return i(t)&&o.call(t,"callee")&&!s.call(t,"callee")};t.exports=c},function(t,e,n){var r=n(233),i=n(61),a=n(81),o=a&&a.isTypedArray,s=o?i(o):r;t.exports=s},function(t,e,n){var r=n(42);t.exports=function(t){if("string"==typeof t||r(t))return t;var e=t+"";return"0"==e&&1/t==-1/0?"-0":e}},function(t,e,n){var r=n(12);t.exports=function(t,e){var n=t.append("foreignObject").attr("width","100000"),i=n.append("xhtml:div");i.attr("xmlns","http://www.w3.org/1999/xhtml");var a=e.label;switch(typeof a){case"function":i.insert(a);break;case"object":i.insert((function(){return a}));break;default:i.html(a)}r.applyStyle(i,e.labelStyle),i.style("display","inline-block"),i.style("white-space","nowrap");var o=i.node().getBoundingClientRect();return n.attr("width",o.width).attr("height",o.height),n}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a=n(15),o=n(52);e.default=function(t,e,n,s){if(void 0===n&&(n=0),void 0===s&&(s=1),"number"!=typeof t)return o.default(t,{a:e});var c=i.default.set({r:r.default.channel.clamp.r(t),g:r.default.channel.clamp.g(e),b:r.default.channel.clamp.b(n),a:r.default.channel.clamp.a(s)});return a.default.stringify(c)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t,e){var n=i.default.parse(t);for(var a in e)n[a]=r.default.channel.clamp[a](e[a]);return i.default.stringify(n)}},function(t,e,n){var r=n(54),i=n(205),a=n(206),o=n(207),s=n(208),c=n(209);function u(t){var e=this.__data__=new r(t);this.size=e.size}u.prototype.clear=i,u.prototype.delete=a,u.prototype.get=o,u.prototype.has=s,u.prototype.set=c,t.exports=u},function(t,e,n){var r=n(200),i=n(201),a=n(202),o=n(203),s=n(204);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},function(t,e,n){var r=n(36);t.exports=function(t,e){for(var n=t.length;n--;)if(r(t[n][0],e))return n;return-1}},function(t,e,n){var r=n(33)(Object,"create");t.exports=r},function(t,e,n){var r=n(225);t.exports=function(t,e){var n=t.__data__;return r(e)?n["string"==typeof e?"string":"hash"]:n.map}},function(t,e,n){var r=n(59),i=n(36),a=Object.prototype.hasOwnProperty;t.exports=function(t,e,n){var o=t[e];a.call(t,e)&&i(o,n)&&(void 0!==n||e in t)||r(t,e,n)}},function(t,e,n){var r=n(111);t.exports=function(t,e,n){"__proto__"==e&&r?r(t,e,{configurable:!0,enumerable:!0,value:n,writable:!0}):t[e]=n}},function(t,e){var n=/^(?:0|[1-9]\d*)$/;t.exports=function(t,e){var r=typeof t;return!!(e=null==e?9007199254740991:e)&&("number"==r||"symbol"!=r&&n.test(t))&&t>-1&&t%1==0&&t<e}},function(t,e){t.exports=function(t){return function(e){return t(e)}}},function(t,e){var n=Object.prototype;t.exports=function(t){var e=t&&t.constructor;return t===("function"==typeof e&&e.prototype||n)}},function(t,e,n){var r=n(113)(Object.getPrototypeOf,Object);t.exports=r},function(t,e,n){var r=n(88),i=n(254)(r);t.exports=i},function(t,e,n){var r=n(5),i=n(92),a=n(268),o=n(135);t.exports=function(t,e){return r(t)?t:i(t,e)?[t]:a(o(t))}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=Array(r);++n<r;)i[n]=e(t[n],n,t);return i}},function(t,e,n){var r=n(35),i=n(143),a=n(144);t.exports=function(t,e){return a(i(t,e,r),t+"")}},function(t,e,n){var r=n(36),i=n(24),a=n(60),o=n(11);t.exports=function(t,e,n){if(!o(n))return!1;var s=typeof e;return!!("number"==s?i(n)&&a(e,n.length):"string"==s&&e in n)&&r(n[e],t)}},function(t,e,n){"use strict";var r=n(4);t.exports={longestPath:function(t){var e={};r.forEach(t.sources(),(function n(i){var a=t.node(i);if(r.has(e,i))return a.rank;e[i]=!0;var o=r.min(r.map(t.outEdges(i),(function(e){return n(e.w)-t.edge(e).minlen})));return o!==Number.POSITIVE_INFINITY&&null!=o||(o=0),a.rank=o}))},slack:function(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}}},function(t,e,n){"use strict";var r=/^(%20|\s)*(javascript|data)/im,i=/[^\x20-\x7E]/gim,a=/^([^:]+):/gm,o=[".","/"];t.exports={sanitizeUrl:function(t){if(!t)return"about:blank";var e,n,s=t.replace(i,"").trim();return function(t){return o.indexOf(t[0])>-1}(s)?s:(n=s.match(a))?(e=n[0],r.test(e)?"about:blank":s):"about:blank"}}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[2,3],n=[1,7],r=[7,12,15,17,19,20,21],i=[7,11,12,15,17,19,20,21],a=[2,20],o=[1,32],s={trace:function(){},yy:{},symbols_:{error:2,start:3,GG:4,":":5,document:6,EOF:7,DIR:8,options:9,body:10,OPT:11,NL:12,line:13,statement:14,COMMIT:15,commit_arg:16,BRANCH:17,ID:18,CHECKOUT:19,MERGE:20,RESET:21,reset_arg:22,STR:23,HEAD:24,reset_parents:25,CARET:26,$accept:0,$end:1},terminals_:{2:"error",4:"GG",5:":",7:"EOF",8:"DIR",11:"OPT",12:"NL",15:"COMMIT",17:"BRANCH",18:"ID",19:"CHECKOUT",20:"MERGE",21:"RESET",23:"STR",24:"HEAD",26:"CARET"},productions_:[0,[3,4],[3,5],[6,0],[6,2],[9,2],[9,1],[10,0],[10,2],[13,2],[13,1],[14,2],[14,2],[14,2],[14,2],[14,2],[16,0],[16,1],[22,2],[22,2],[25,0],[25,2]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 2:return r.setDirection(a[s-3]),a[s-1];case 4:r.setOptions(a[s-1]),this.$=a[s];break;case 5:a[s-1]+=a[s],this.$=a[s-1];break;case 7:this.$=[];break;case 8:a[s-1].push(a[s]),this.$=a[s-1];break;case 9:this.$=a[s-1];break;case 11:r.commit(a[s]);break;case 12:r.branch(a[s]);break;case 13:r.checkout(a[s]);break;case 14:r.merge(a[s]);break;case 15:r.reset(a[s]);break;case 16:this.$="";break;case 17:this.$=a[s];break;case 18:this.$=a[s-1]+":"+a[s];break;case 19:this.$=a[s-1]+":"+r.count,r.count=0;break;case 20:r.count=0;break;case 21:r.count+=1}},table:[{3:1,4:[1,2]},{1:[3]},{5:[1,3],8:[1,4]},{6:5,7:e,9:6,12:n},{5:[1,8]},{7:[1,9]},t(r,[2,7],{10:10,11:[1,11]}),t(i,[2,6]),{6:12,7:e,9:6,12:n},{1:[2,1]},{7:[2,4],12:[1,15],13:13,14:14,15:[1,16],17:[1,17],19:[1,18],20:[1,19],21:[1,20]},t(i,[2,5]),{7:[1,21]},t(r,[2,8]),{12:[1,22]},t(r,[2,10]),{12:[2,16],16:23,23:[1,24]},{18:[1,25]},{18:[1,26]},{18:[1,27]},{18:[1,30],22:28,24:[1,29]},{1:[2,2]},t(r,[2,9]),{12:[2,11]},{12:[2,17]},{12:[2,12]},{12:[2,13]},{12:[2,14]},{12:[2,15]},{12:a,25:31,26:o},{12:a,25:33,26:o},{12:[2,18]},{12:a,25:34,26:o},{12:[2,19]},{12:[2,21]}],defaultActions:{9:[2,1],21:[2,2],23:[2,11],24:[2,17],25:[2,12],26:[2,13],27:[2,14],28:[2,15],31:[2,18],33:[2,19],34:[2,21]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},c={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 12;case 1:case 2:case 3:break;case 4:return 4;case 5:return 15;case 6:return 17;case 7:return 20;case 8:return 21;case 9:return 19;case 10:case 11:return 8;case 12:return 5;case 13:return 26;case 14:this.begin("options");break;case 15:this.popState();break;case 16:return 11;case 17:this.begin("string");break;case 18:this.popState();break;case 19:return 23;case 20:return 18;case 21:return 7}},rules:[/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gitGraph\b)/i,/^(?:commit\b)/i,/^(?:branch\b)/i,/^(?:merge\b)/i,/^(?:reset\b)/i,/^(?:checkout\b)/i,/^(?:LR\b)/i,/^(?:BT\b)/i,/^(?::)/i,/^(?:\^)/i,/^(?:options\r?\n)/i,/^(?:end\r?\n)/i,/^(?:[^\n]+\r?\n)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[a-zA-Z][-_\.a-zA-Z0-9]*[-_a-zA-Z0-9])/i,/^(?:$)/i],conditions:{options:{rules:[15,16],inclusive:!1},string:{rules:[18,19],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,20,21],inclusive:!0}}};function u(){this.yy={}}return s.lexer=c,u.prototype=s,s.Parser=u,new u}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[6,9,10],n={trace:function(){},yy:{},symbols_:{error:2,start:3,info:4,document:5,EOF:6,line:7,statement:8,NL:9,showInfo:10,$accept:0,$end:1},terminals_:{2:"error",4:"info",6:"EOF",9:"NL",10:"showInfo"},productions_:[0,[3,3],[5,0],[5,2],[7,1],[7,1],[8,1]],performAction:function(t,e,n,r,i,a,o){a.length;switch(i){case 1:return r;case 4:break;case 6:r.setInfo(!0)}},table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:6,9:[1,7],10:[1,8]},{1:[2,1]},t(e,[2,3]),t(e,[2,4]),t(e,[2,5]),t(e,[2,6])],defaultActions:{4:[2,1]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},r={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 4;case 1:return 9;case 2:return"space";case 3:return 10;case 4:return 6;case 5:return"TXT"}},rules:[/^(?:info\b)/i,/^(?:[\s\n\r]+)/i,/^(?:[\s]+)/i,/^(?:showInfo\b)/i,/^(?:$)/i,/^(?:.)/i],conditions:{INITIAL:{rules:[0,1,2,3,4,5],inclusive:!0}}};function i(){this.yy={}}return n.lexer=r,i.prototype=n,n.Parser=i,new i}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,4],n=[1,5],r=[1,6],i=[1,7],a=[1,9],o=[1,10,12,19,20,21,22],s=[1,6,10,12,19,20,21,22],c=[19,20,21],u=[1,22],l=[6,19,20,21,22],h={trace:function(){},yy:{},symbols_:{error:2,start:3,eol:4,directive:5,PIE:6,document:7,line:8,statement:9,txt:10,value:11,title:12,title_value:13,openDirective:14,typeDirective:15,closeDirective:16,":":17,argDirective:18,NEWLINE:19,";":20,EOF:21,open_directive:22,type_directive:23,arg_directive:24,close_directive:25,$accept:0,$end:1},terminals_:{2:"error",6:"PIE",10:"txt",11:"value",12:"title",13:"title_value",17:":",19:"NEWLINE",20:";",21:"EOF",22:"open_directive",23:"type_directive",24:"arg_directive",25:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[9,0],[9,2],[9,2],[9,1],[5,3],[5,5],[4,1],[4,1],[4,1],[14,1],[15,1],[18,1],[16,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 6:this.$=a[s-1];break;case 8:r.addSection(a[s-1],r.cleanupValue(a[s]));break;case 9:this.$=a[s].trim(),r.setTitle(this.$);break;case 16:r.parseDirective("%%{","open_directive");break;case 17:r.parseDirective(a[s],"type_directive");break;case 18:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 19:r.parseDirective("}%%","close_directive","pie")}},table:[{3:1,4:2,5:3,6:e,14:8,19:n,20:r,21:i,22:a},{1:[3]},{3:10,4:2,5:3,6:e,14:8,19:n,20:r,21:i,22:a},{3:11,4:2,5:3,6:e,14:8,19:n,20:r,21:i,22:a},t(o,[2,4],{7:12}),t(s,[2,13]),t(s,[2,14]),t(s,[2,15]),{15:13,23:[1,14]},{23:[2,16]},{1:[2,1]},{1:[2,2]},t(c,[2,7],{14:8,8:15,9:16,5:19,1:[2,3],10:[1,17],12:[1,18],22:a}),{16:20,17:[1,21],25:u},t([17,25],[2,17]),t(o,[2,5]),{4:23,19:n,20:r,21:i},{11:[1,24]},{13:[1,25]},t(c,[2,10]),t(l,[2,11]),{18:26,24:[1,27]},t(l,[2,19]),t(o,[2,6]),t(c,[2,8]),t(c,[2,9]),{16:28,25:u},{25:[2,18]},t(l,[2,12])],defaultActions:{9:[2,16],10:[2,1],11:[2,2],27:[2,18]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},f={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),22;case 1:return this.begin("type_directive"),23;case 2:return this.popState(),this.begin("arg_directive"),17;case 3:return this.popState(),this.popState(),25;case 4:return 24;case 5:case 6:break;case 7:return 19;case 8:case 9:break;case 10:return this.begin("title"),12;case 11:return this.popState(),"title_value";case 12:this.begin("string");break;case 13:this.popState();break;case 14:return"txt";case 15:return 6;case 16:return"value";case 17:return 21}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:[\s]+)/i,/^(?:title\b)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:pie\b)/i,/^(?::[\s]*[\d]+(?:\.[\d]+)?)/i,/^(?:$)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},title:{rules:[11],inclusive:!1},string:{rules:[13,14],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,12,15,16,17],inclusive:!0}}};function d(){this.yy={}}return h.lexer=f,d.prototype=h,h.Parser=d,new d}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,23,37],i=[1,17],a=[1,20],o=[1,25],s=[1,26],c=[1,27],u=[1,28],l=[1,37],h=[23,34,35],f=[4,6,9,11,23,37],d=[30,31,32,33],p=[22,27],g={trace:function(){},yy:{},symbols_:{error:2,start:3,ER_DIAGRAM:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,entityName:17,relSpec:18,role:19,BLOCK_START:20,attributes:21,BLOCK_STOP:22,ALPHANUM:23,attribute:24,attributeType:25,attributeName:26,ATTRIBUTE_WORD:27,cardinality:28,relType:29,ZERO_OR_ONE:30,ZERO_OR_MORE:31,ONE_OR_MORE:32,ONLY_ONE:33,NON_IDENTIFYING:34,IDENTIFYING:35,WORD:36,open_directive:37,type_directive:38,arg_directive:39,close_directive:40,$accept:0,$end:1},terminals_:{2:"error",4:"ER_DIAGRAM",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",20:"BLOCK_START",22:"BLOCK_STOP",23:"ALPHANUM",27:"ATTRIBUTE_WORD",30:"ZERO_OR_ONE",31:"ZERO_OR_MORE",32:"ONE_OR_MORE",33:"ONLY_ONE",34:"NON_IDENTIFYING",35:"IDENTIFYING",36:"WORD",37:"open_directive",38:"type_directive",39:"arg_directive",40:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,5],[10,4],[10,3],[10,1],[17,1],[21,1],[21,2],[24,2],[25,1],[26,1],[18,3],[28,1],[28,1],[28,1],[28,1],[29,1],[29,1],[19,1],[19,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:break;case 3:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 7:case 8:this.$=[];break;case 12:r.addEntity(a[s-4]),r.addEntity(a[s-2]),r.addRelationship(a[s-4],a[s],a[s-2],a[s-3]);break;case 13:r.addEntity(a[s-3]),r.addAttributes(a[s-3],a[s-1]);break;case 14:r.addEntity(a[s-2]);break;case 15:r.addEntity(a[s]);break;case 16:this.$=a[s];break;case 17:this.$=[a[s]];break;case 18:a[s].push(a[s-1]),this.$=a[s];break;case 19:this.$={attributeType:a[s-1],attributeName:a[s]};break;case 20:case 21:this.$=a[s];break;case 22:this.$={cardA:a[s],relType:a[s-1],cardB:a[s-2]};break;case 23:this.$=r.Cardinality.ZERO_OR_ONE;break;case 24:this.$=r.Cardinality.ZERO_OR_MORE;break;case 25:this.$=r.Cardinality.ONE_OR_MORE;break;case 26:this.$=r.Cardinality.ONLY_ONE;break;case 27:this.$=r.Identification.NON_IDENTIFYING;break;case 28:this.$=r.Identification.IDENTIFYING;break;case 29:this.$=a[s].replace(/"/g,"");break;case 30:this.$=a[s];break;case 31:r.parseDirective("%%{","open_directive");break;case 32:r.parseDirective(a[s],"type_directive");break;case 33:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 34:r.parseDirective("}%%","close_directive","er")}},table:[{3:1,4:e,7:3,12:4,37:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,37:n},{13:8,38:[1,9]},{38:[2,31]},{6:[1,10],7:15,8:11,9:[1,12],10:13,11:[1,14],12:4,17:16,23:i,37:n},{1:[2,2]},{14:18,15:[1,19],40:a},t([15,40],[2,32]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:15,10:21,12:4,17:16,23:i,37:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,15],{18:22,28:24,20:[1,23],30:o,31:s,32:c,33:u}),t([6,9,11,15,20,23,30,31,32,33,37],[2,16]),{11:[1,29]},{16:30,39:[1,31]},{11:[2,34]},t(r,[2,5]),{17:32,23:i},{21:33,22:[1,34],24:35,25:36,27:l},{29:38,34:[1,39],35:[1,40]},t(h,[2,23]),t(h,[2,24]),t(h,[2,25]),t(h,[2,26]),t(f,[2,9]),{14:41,40:a},{40:[2,33]},{15:[1,42]},{22:[1,43]},t(r,[2,14]),{21:44,22:[2,17],24:35,25:36,27:l},{26:45,27:[1,46]},{27:[2,20]},{28:47,30:o,31:s,32:c,33:u},t(d,[2,27]),t(d,[2,28]),{11:[1,48]},{19:49,23:[1,51],36:[1,50]},t(r,[2,13]),{22:[2,18]},t(p,[2,19]),t(p,[2,21]),{23:[2,22]},t(f,[2,10]),t(r,[2,12]),t(r,[2,29]),t(r,[2,30])],defaultActions:{5:[2,31],7:[2,2],20:[2,34],31:[2,33],37:[2,20],44:[2,18],47:[2,22]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},y={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),37;case 1:return this.begin("type_directive"),38;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),40;case 4:return 39;case 5:case 6:break;case 7:return 11;case 8:break;case 9:return 9;case 10:return 36;case 11:return 4;case 12:return this.begin("block"),20;case 13:break;case 14:return 27;case 15:break;case 16:return this.popState(),22;case 17:return e.yytext[0];case 18:return 30;case 19:return 31;case 20:return 32;case 21:return 33;case 22:return 30;case 23:return 31;case 24:return 32;case 25:return 34;case 26:return 35;case 27:case 28:return 34;case 29:return 23;case 30:return e.yytext[0];case 31:return 6}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:[\s]+)/i,/^(?:"[^"]*")/i,/^(?:erDiagram\b)/i,/^(?:\{)/i,/^(?:\s+)/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:[\n]+)/i,/^(?:\})/i,/^(?:.)/i,/^(?:\|o\b)/i,/^(?:\}o\b)/i,/^(?:\}\|)/i,/^(?:\|\|)/i,/^(?:o\|)/i,/^(?:o\{)/i,/^(?:\|\{)/i,/^(?:\.\.)/i,/^(?:--)/i,/^(?:\.-)/i,/^(?:-\.)/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:.)/i,/^(?:$)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},block:{rules:[13,14,15,16,17],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,18,19,20,21,22,23,24,25,26,27,28,29,30,31],inclusive:!0}}};function v(){this.yy={}}return g.lexer=y,v.prototype=g,g.Parser=v,new v}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){"use strict";var r;Object.defineProperty(e,"__esModule",{value:!0}),function(t){t[t.ALL=0]="ALL",t[t.RGB=1]="RGB",t[t.HSL=2]="HSL"}(r||(r={})),e.TYPE=r},function(t,e,n){"use strict";var r=n(10);t.exports=i;function i(t){this._isDirected=!r.has(t,"directed")||t.directed,this._isMultigraph=!!r.has(t,"multigraph")&&t.multigraph,this._isCompound=!!r.has(t,"compound")&&t.compound,this._label=void 0,this._defaultNodeLabelFn=r.constant(void 0),this._defaultEdgeLabelFn=r.constant(void 0),this._nodes={},this._isCompound&&(this._parent={},this._children={},this._children["\0"]={}),this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={}}function a(t,e){t[e]?t[e]++:t[e]=1}function o(t,e){--t[e]||delete t[e]}function s(t,e,n,i){var a=""+e,o=""+n;if(!t&&a>o){var s=a;a=o,o=s}return a+""+o+""+(r.isUndefined(i)?"\0":i)}function c(t,e,n,r){var i=""+e,a=""+n;if(!t&&i>a){var o=i;i=a,a=o}var s={v:i,w:a};return r&&(s.name=r),s}function u(t,e){return s(t,e.v,e.w,e.name)}i.prototype._nodeCount=0,i.prototype._edgeCount=0,i.prototype.isDirected=function(){return this._isDirected},i.prototype.isMultigraph=function(){return this._isMultigraph},i.prototype.isCompound=function(){return this._isCompound},i.prototype.setGraph=function(t){return this._label=t,this},i.prototype.graph=function(){return this._label},i.prototype.setDefaultNodeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultNodeLabelFn=t,this},i.prototype.nodeCount=function(){return this._nodeCount},i.prototype.nodes=function(){return r.keys(this._nodes)},i.prototype.sources=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._in[e])}))},i.prototype.sinks=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._out[e])}))},i.prototype.setNodes=function(t,e){var n=arguments,i=this;return r.each(t,(function(t){n.length>1?i.setNode(t,e):i.setNode(t)})),this},i.prototype.setNode=function(t,e){return r.has(this._nodes,t)?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]="\0",this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)},i.prototype.node=function(t){return this._nodes[t]},i.prototype.hasNode=function(t){return r.has(this._nodes,t)},i.prototype.removeNode=function(t){var e=this;if(r.has(this._nodes,t)){var n=function(t){e.removeEdge(e._edgeObjs[t])};delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],r.each(this.children(t),(function(t){e.setParent(t)})),delete this._children[t]),r.each(r.keys(this._in[t]),n),delete this._in[t],delete this._preds[t],r.each(r.keys(this._out[t]),n),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this},i.prototype.setParent=function(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(r.isUndefined(e))e="\0";else{for(var n=e+="";!r.isUndefined(n);n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this},i.prototype._removeFromParentsChildList=function(t){delete this._children[this._parent[t]][t]},i.prototype.parent=function(t){if(this._isCompound){var e=this._parent[t];if("\0"!==e)return e}},i.prototype.children=function(t){if(r.isUndefined(t)&&(t="\0"),this._isCompound){var e=this._children[t];if(e)return r.keys(e)}else{if("\0"===t)return this.nodes();if(this.hasNode(t))return[]}},i.prototype.predecessors=function(t){var e=this._preds[t];if(e)return r.keys(e)},i.prototype.successors=function(t){var e=this._sucs[t];if(e)return r.keys(e)},i.prototype.neighbors=function(t){var e=this.predecessors(t);if(e)return r.union(e,this.successors(t))},i.prototype.isLeaf=function(t){return 0===(this.isDirected()?this.successors(t):this.neighbors(t)).length},i.prototype.filterNodes=function(t){var e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph());var n=this;r.each(this._nodes,(function(n,r){t(r)&&e.setNode(r,n)})),r.each(this._edgeObjs,(function(t){e.hasNode(t.v)&&e.hasNode(t.w)&&e.setEdge(t,n.edge(t))}));var i={};return this._isCompound&&r.each(e.nodes(),(function(t){e.setParent(t,function t(r){var a=n.parent(r);return void 0===a||e.hasNode(a)?(i[r]=a,a):a in i?i[a]:t(a)}(t))})),e},i.prototype.setDefaultEdgeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultEdgeLabelFn=t,this},i.prototype.edgeCount=function(){return this._edgeCount},i.prototype.edges=function(){return r.values(this._edgeObjs)},i.prototype.setPath=function(t,e){var n=this,i=arguments;return r.reduce(t,(function(t,r){return i.length>1?n.setEdge(t,r,e):n.setEdge(t,r),r})),this},i.prototype.setEdge=function(){var t,e,n,i,o=!1,u=arguments[0];"object"==typeof u&&null!==u&&"v"in u?(t=u.v,e=u.w,n=u.name,2===arguments.length&&(i=arguments[1],o=!0)):(t=u,e=arguments[1],n=arguments[3],arguments.length>2&&(i=arguments[2],o=!0)),t=""+t,e=""+e,r.isUndefined(n)||(n=""+n);var l=s(this._isDirected,t,e,n);if(r.has(this._edgeLabels,l))return o&&(this._edgeLabels[l]=i),this;if(!r.isUndefined(n)&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(t),this.setNode(e),this._edgeLabels[l]=o?i:this._defaultEdgeLabelFn(t,e,n);var h=c(this._isDirected,t,e,n);return t=h.v,e=h.w,Object.freeze(h),this._edgeObjs[l]=h,a(this._preds[e],t),a(this._sucs[t],e),this._in[e][l]=h,this._out[t][l]=h,this._edgeCount++,this},i.prototype.edge=function(t,e,n){var r=1===arguments.length?u(this._isDirected,arguments[0]):s(this._isDirected,t,e,n);return this._edgeLabels[r]},i.prototype.hasEdge=function(t,e,n){var i=1===arguments.length?u(this._isDirected,arguments[0]):s(this._isDirected,t,e,n);return r.has(this._edgeLabels,i)},i.prototype.removeEdge=function(t,e,n){var r=1===arguments.length?u(this._isDirected,arguments[0]):s(this._isDirected,t,e,n),i=this._edgeObjs[r];return i&&(t=i.v,e=i.w,delete this._edgeLabels[r],delete this._edgeObjs[r],o(this._preds[e],t),o(this._sucs[t],e),delete this._in[e][r],delete this._out[t][r],this._edgeCount--),this},i.prototype.inEdges=function(t,e){var n=this._in[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.v===e})):i}},i.prototype.outEdges=function(t,e){var n=this._out[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.w===e})):i}},i.prototype.nodeEdges=function(t,e){var n=this.inEdges(t,e);if(n)return n.concat(this.outEdges(t,e))}},function(t,e,n){var r=n(33)(n(16),"Map");t.exports=r},function(t,e,n){var r=n(217),i=n(224),a=n(226),o=n(227),s=n(228);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r&&!1!==e(t[n],n,t););return t}},function(t,e){t.exports=function(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=9007199254740991}},function(t,e,n){(function(t){var r=n(109),i=e&&!e.nodeType&&e,a=i&&"object"==typeof t&&t&&!t.nodeType&&t,o=a&&a.exports===i&&r.process,s=function(){try{var t=a&&a.require&&a.require("util").types;return t||o&&o.binding&&o.binding("util")}catch(t){}}();t.exports=s}).call(this,n(7)(t))},function(t,e,n){var r=n(62),i=n(234),a=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return i(t);var e=[];for(var n in Object(t))a.call(t,n)&&"constructor"!=n&&e.push(n);return e}},function(t,e,n){var r=n(116),i=n(117),a=Object.prototype.propertyIsEnumerable,o=Object.getOwnPropertySymbols,s=o?function(t){return null==t?[]:(t=Object(t),r(o(t),(function(e){return a.call(t,e)})))}:i;t.exports=s},function(t,e){t.exports=function(t,e){for(var n=-1,r=e.length,i=t.length;++n<r;)t[i+n]=e[n];return t}},function(t,e,n){var r=n(122);t.exports=function(t){var e=new t.constructor(t.byteLength);return new r(e).set(new r(t)),e}},function(t,e){t.exports=function(t){return function(){return t}}},function(t,e,n){t.exports=n(126)},function(t,e,n){var r=n(89),i=n(30);t.exports=function(t,e){return t&&r(t,e,i)}},function(t,e,n){var r=n(253)();t.exports=r},function(t,e){t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t){n[++e]=t})),n}},function(t,e,n){var r=n(65),i=n(49);t.exports=function(t,e){for(var n=0,a=(e=r(e,t)).length;null!=t&&n<a;)t=t[i(e[n++])];return n&&n==a?t:void 0}},function(t,e,n){var r=n(5),i=n(42),a=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,o=/^\w*$/;t.exports=function(t,e){if(r(t))return!1;var n=typeof t;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=t&&!i(t))||(o.test(t)||!a.test(t)||null!=e&&t in Object(e))}},function(t,e,n){var r=n(275),i=n(137);t.exports=function(t,e){return null!=t&&i(t,e,r)}},function(t,e,n){var r=n(84),i=n(287);t.exports=function t(e,n,a,o,s){var c=-1,u=e.length;for(a||(a=i),s||(s=[]);++c<u;){var l=e[c];n>0&&a(l)?n>1?t(l,n-1,a,o,s):r(s,l):o||(s[s.length]=l)}return s}},function(t,e,n){var r=n(42);t.exports=function(t,e,n){for(var i=-1,a=t.length;++i<a;){var o=t[i],s=e(o);if(null!=s&&(void 0===c?s==s&&!r(s):n(s,c)))var c=s,u=o}return u}},function(t,e){t.exports=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);r.y<a&&(l=-l);return{x:i+u,y:a+l}}},function(t,e,n){var r=n(372),i=n(50),a=n(373);t.exports=function(t,e,n){var o=e.label,s=t.append("g");"svg"===e.labelType?a(s,e):"string"!=typeof o||"html"===e.labelType?i(s,e):r(s,e);var c,u=s.node().getBBox();switch(n){case"top":c=-e.height/2;break;case"bottom":c=e.height/2-u.height;break;default:c=-u.height/2}return s.attr("transform","translate("+-u.width/2+","+c+")"),s}},function(t,e){},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a=n(178),o={re:/^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i,parse:function(t){if(35===t.charCodeAt(0)){var e=t.match(o.re);if(e){var n=e[1],r=parseInt(n,16),a=n.length,s=a%4==0,c=a>4,u=c?1:17,l=c?8:4,h=s?0:-1,f=c?255:15;return i.default.set({r:(r>>l*(h+3)&f)*u,g:(r>>l*(h+2)&f)*u,b:(r>>l*(h+1)&f)*u,a:s?(r&f)*u/255:1},t)}}},stringify:function(t){return t.a<1?"#"+a.DEC2HEX[Math.round(t.r)]+a.DEC2HEX[Math.round(t.g)]+a.DEC2HEX[Math.round(t.b)]+r.default.unit.frac2hex(t.a):"#"+a.DEC2HEX[Math.round(t.r)]+a.DEC2HEX[Math.round(t.g)]+a.DEC2HEX[Math.round(t.b)]}};e.default=o},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a=n(15);e.default=function(t,e,n,o){void 0===o&&(o=1);var s=i.default.set({h:r.default.channel.clamp.h(t),s:r.default.channel.clamp.s(e),l:r.default.channel.clamp.l(n),a:r.default.channel.clamp.a(o)});return a.default.stringify(s)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"a")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t){var e=i.default.parse(t),n=e.r,a=e.g,o=e.b,s=.2126*r.default.channel.toLinear(n)+.7152*r.default.channel.toLinear(a)+.0722*r.default.channel.toLinear(o);return r.default.lang.round(s)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(102);e.default=function(t){return r.default(t)>=.5}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"a",e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"a",-e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15),i=n(52);e.default=function(t,e){var n=r.default.parse(t),a={};for(var o in e)e[o]&&(a[o]=n[o]+e[o]);return i.default(t,a)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15),i=n(51);e.default=function(t,e,n){void 0===n&&(n=50);var a=r.default.parse(t),o=a.r,s=a.g,c=a.b,u=a.a,l=r.default.parse(e),h=l.r,f=l.g,d=l.b,p=l.a,g=n/100,y=2*g-1,v=u-p,m=((y*v==-1?y:(y+v)/(1+y*v))+1)/2,b=1-m,x=o*m+h*b,_=s*m+f*b,k=c*m+d*b,w=u*g+p*(1-g);return i.default(x,_,k,w)}},function(t,e,n){var r=n(53),i=n(79),a=n(58),o=n(229),s=n(235),c=n(114),u=n(115),l=n(238),h=n(239),f=n(119),d=n(240),p=n(41),g=n(244),y=n(245),v=n(124),m=n(5),b=n(39),x=n(249),_=n(11),k=n(251),w=n(30),E={};E["[object Arguments]"]=E["[object Array]"]=E["[object ArrayBuffer]"]=E["[object DataView]"]=E["[object Boolean]"]=E["[object Date]"]=E["[object Float32Array]"]=E["[object Float64Array]"]=E["[object Int8Array]"]=E["[object Int16Array]"]=E["[object Int32Array]"]=E["[object Map]"]=E["[object Number]"]=E["[object Object]"]=E["[object RegExp]"]=E["[object Set]"]=E["[object String]"]=E["[object Symbol]"]=E["[object Uint8Array]"]=E["[object Uint8ClampedArray]"]=E["[object Uint16Array]"]=E["[object Uint32Array]"]=!0,E["[object Error]"]=E["[object Function]"]=E["[object WeakMap]"]=!1,t.exports=function t(e,n,T,C,A,S){var M,O=1&n,D=2&n,N=4&n;if(T&&(M=A?T(e,C,A,S):T(e)),void 0!==M)return M;if(!_(e))return e;var B=m(e);if(B){if(M=g(e),!O)return u(e,M)}else{var L=p(e),P="[object Function]"==L||"[object GeneratorFunction]"==L;if(b(e))return c(e,O);if("[object Object]"==L||"[object Arguments]"==L||P&&!A){if(M=D||P?{}:v(e),!O)return D?h(e,s(M,e)):l(e,o(M,e))}else{if(!E[L])return A?e:{};M=y(e,L,O)}}S||(S=new r);var I=S.get(e);if(I)return I;S.set(e,M),k(e)?e.forEach((function(r){M.add(t(r,n,T,r,e,S))})):x(e)&&e.forEach((function(r,i){M.set(i,t(r,n,T,i,e,S))}));var F=N?D?d:f:D?keysIn:w,j=B?void 0:F(e);return i(j||e,(function(r,i){j&&(r=e[i=r]),a(M,i,t(r,n,T,i,e,S))})),M}},function(t,e,n){(function(e){var n="object"==typeof e&&e&&e.Object===Object&&e;t.exports=n}).call(this,n(211))},function(t,e){var n=Function.prototype.toString;t.exports=function(t){if(null!=t){try{return n.call(t)}catch(t){}try{return t+""}catch(t){}}return""}},function(t,e,n){var r=n(33),i=function(){try{var t=r(Object,"defineProperty");return t({},"",{}),t}catch(t){}}();t.exports=i},function(t,e,n){var r=n(230),i=n(47),a=n(5),o=n(39),s=n(60),c=n(48),u=Object.prototype.hasOwnProperty;t.exports=function(t,e){var n=a(t),l=!n&&i(t),h=!n&&!l&&o(t),f=!n&&!l&&!h&&c(t),d=n||l||h||f,p=d?r(t.length,String):[],g=p.length;for(var y in t)!e&&!u.call(t,y)||d&&("length"==y||h&&("offset"==y||"parent"==y)||f&&("buffer"==y||"byteLength"==y||"byteOffset"==y)||s(y,g))||p.push(y);return p}},function(t,e){t.exports=function(t,e){return function(n){return t(e(n))}}},function(t,e,n){(function(t){var r=n(16),i=e&&!e.nodeType&&e,a=i&&"object"==typeof t&&t&&!t.nodeType&&t,o=a&&a.exports===i?r.Buffer:void 0,s=o?o.allocUnsafe:void 0;t.exports=function(t,e){if(e)return t.slice();var n=t.length,r=s?s(n):new t.constructor(n);return t.copy(r),r}}).call(this,n(7)(t))},function(t,e){t.exports=function(t,e){var n=-1,r=t.length;for(e||(e=Array(r));++n<r;)e[n]=t[n];return e}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=0,a=[];++n<r;){var o=t[n];e(o,n,t)&&(a[i++]=o)}return a}},function(t,e){t.exports=function(){return[]}},function(t,e,n){var r=n(84),i=n(63),a=n(83),o=n(117),s=Object.getOwnPropertySymbols?function(t){for(var e=[];t;)r(e,a(t)),t=i(t);return e}:o;t.exports=s},function(t,e,n){var r=n(120),i=n(83),a=n(30);t.exports=function(t){return r(t,a,i)}},function(t,e,n){var r=n(84),i=n(5);t.exports=function(t,e,n){var a=e(t);return i(t)?a:r(a,n(t))}},function(t,e,n){var r=n(33)(n(16),"Set");t.exports=r},function(t,e,n){var r=n(16).Uint8Array;t.exports=r},function(t,e,n){var r=n(85);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.length)}},function(t,e,n){var r=n(125),i=n(63),a=n(62);t.exports=function(t){return"function"!=typeof t.constructor||a(t)?{}:r(i(t))}},function(t,e,n){var r=n(11),i=Object.create,a=function(){function t(){}return function(e){if(!r(e))return{};if(i)return i(e);t.prototype=e;var n=new t;return t.prototype=void 0,n}}();t.exports=a},function(t,e,n){var r=n(79),i=n(64),a=n(127),o=n(5);t.exports=function(t,e){return(o(t)?r:i)(t,a(e))}},function(t,e,n){var r=n(35);t.exports=function(t){return"function"==typeof t?t:r}},function(t,e,n){var r=n(116),i=n(255),a=n(25),o=n(5);t.exports=function(t,e){return(o(t)?r:i)(t,a(e,3))}},function(t,e,n){var r=n(258),i=n(21);t.exports=function t(e,n,a,o,s){return e===n||(null==e||null==n||!i(e)&&!i(n)?e!=e&&n!=n:r(e,n,a,o,t,s))}},function(t,e,n){var r=n(131),i=n(261),a=n(132);t.exports=function(t,e,n,o,s,c){var u=1&n,l=t.length,h=e.length;if(l!=h&&!(u&&h>l))return!1;var f=c.get(t);if(f&&c.get(e))return f==e;var d=-1,p=!0,g=2&n?new r:void 0;for(c.set(t,e),c.set(e,t);++d<l;){var y=t[d],v=e[d];if(o)var m=u?o(v,y,d,e,t,c):o(y,v,d,t,e,c);if(void 0!==m){if(m)continue;p=!1;break}if(g){if(!i(e,(function(t,e){if(!a(g,e)&&(y===t||s(y,t,n,o,c)))return g.push(e)}))){p=!1;break}}else if(y!==v&&!s(y,v,n,o,c)){p=!1;break}}return c.delete(t),c.delete(e),p}},function(t,e,n){var r=n(78),i=n(259),a=n(260);function o(t){var e=-1,n=null==t?0:t.length;for(this.__data__=new r;++e<n;)this.add(t[e])}o.prototype.add=o.prototype.push=i,o.prototype.has=a,t.exports=o},function(t,e){t.exports=function(t,e){return t.has(e)}},function(t,e,n){var r=n(11);t.exports=function(t){return t==t&&!r(t)}},function(t,e){t.exports=function(t,e){return function(n){return null!=n&&(n[t]===e&&(void 0!==e||t in Object(n)))}}},function(t,e,n){var r=n(271);t.exports=function(t){return null==t?"":r(t)}},function(t,e,n){var r=n(272),i=n(137);t.exports=function(t,e){return null!=t&&i(t,e,r)}},function(t,e,n){var r=n(65),i=n(47),a=n(5),o=n(60),s=n(80),c=n(49);t.exports=function(t,e,n){for(var u=-1,l=(e=r(e,t)).length,h=!1;++u<l;){var f=c(e[u]);if(!(h=null!=t&&n(t,f)))break;t=t[f]}return h||++u!=l?h:!!(l=null==t?0:t.length)&&s(l)&&o(f,l)&&(a(t)||i(t))}},function(t,e){t.exports=function(t){return function(e){return null==e?void 0:e[t]}}},function(t,e){t.exports=function(t){return void 0===t}},function(t,e,n){var r=n(66),i=n(25),a=n(141),o=n(5);t.exports=function(t,e){return(o(t)?r:a)(t,i(e,3))}},function(t,e,n){var r=n(64),i=n(24);t.exports=function(t,e){var n=-1,a=i(t)?Array(t.length):[];return r(t,(function(t,r,i){a[++n]=e(t,r,i)})),a}},function(t,e,n){var r=n(277),i=n(64),a=n(25),o=n(278),s=n(5);t.exports=function(t,e,n){var c=s(t)?r:o,u=arguments.length<3;return c(t,a(e,4),n,u,i)}},function(t,e,n){var r=n(288),i=Math.max;t.exports=function(t,e,n){return e=i(void 0===e?t.length-1:e,0),function(){for(var a=arguments,o=-1,s=i(a.length-e,0),c=Array(s);++o<s;)c[o]=a[e+o];o=-1;for(var u=Array(e+1);++o<e;)u[o]=a[o];return u[e]=n(c),r(t,this,u)}}},function(t,e,n){var r=n(289),i=n(290)(r);t.exports=i},function(t,e){t.exports=function(t,e,n,r){for(var i=t.length,a=n+(r?1:-1);r?a--:++a<i;)if(e(t[a],a,t))return a;return-1}},function(t,e,n){var r=n(24),i=n(21);t.exports=function(t){return i(t)&&r(t)}},function(t,e,n){var r=n(299),i=n(30);t.exports=function(t){return null==t?[]:r(t,i(t))}},function(t,e,n){var r=n(10),i=n(149);t.exports=function(t,e,n,r){return function(t,e,n,r){var a,o,s={},c=new i,u=function(t){var e=t.v!==a?t.v:t.w,r=s[e],i=n(t),u=o.distance+i;if(i<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+t+" Weight: "+i);u<r.distance&&(r.distance=u,r.predecessor=a,c.decrease(e,u))};t.nodes().forEach((function(t){var n=t===e?0:Number.POSITIVE_INFINITY;s[t]={distance:n},c.add(t,n)}));for(;c.size()>0&&(a=c.removeMin(),(o=s[a]).distance!==Number.POSITIVE_INFINITY);)r(a).forEach(u);return s}(t,String(e),n||a,r||function(e){return t.outEdges(e)})};var a=r.constant(1)},function(t,e,n){var r=n(10);function i(){this._arr=[],this._keyIndices={}}t.exports=i,i.prototype.size=function(){return this._arr.length},i.prototype.keys=function(){return this._arr.map((function(t){return t.key}))},i.prototype.has=function(t){return r.has(this._keyIndices,t)},i.prototype.priority=function(t){var e=this._keyIndices[t];if(void 0!==e)return this._arr[e].priority},i.prototype.min=function(){if(0===this.size())throw new Error("Queue underflow");return this._arr[0].key},i.prototype.add=function(t,e){var n=this._keyIndices;if(t=String(t),!r.has(n,t)){var i=this._arr,a=i.length;return n[t]=a,i.push({key:t,priority:e}),this._decrease(a),!0}return!1},i.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key},i.prototype.decrease=function(t,e){var n=this._keyIndices[t];if(e>this._arr[n].priority)throw new Error("New priority is greater than current priority. Key: "+t+" Old: "+this._arr[n].priority+" New: "+e);this._arr[n].priority=e,this._decrease(n)},i.prototype._heapify=function(t){var e=this._arr,n=2*t,r=n+1,i=t;n<e.length&&(i=e[n].priority<e[i].priority?n:i,r<e.length&&(i=e[r].priority<e[i].priority?r:i),i!==t&&(this._swap(t,i),this._heapify(i)))},i.prototype._decrease=function(t){for(var e,n=this._arr,r=n[t].priority;0!==t&&!(n[e=t>>1].priority<r);)this._swap(t,e),t=e},i.prototype._swap=function(t,e){var n=this._arr,r=this._keyIndices,i=n[t],a=n[e];n[t]=a,n[e]=i,r[a.key]=t,r[i.key]=e}},function(t,e,n){var r=n(10);t.exports=function(t){var e=0,n=[],i={},a=[];return t.nodes().forEach((function(o){r.has(i,o)||function o(s){var c=i[s]={onStack:!0,lowlink:e,index:e++};if(n.push(s),t.successors(s).forEach((function(t){r.has(i,t)?i[t].onStack&&(c.lowlink=Math.min(c.lowlink,i[t].index)):(o(t),c.lowlink=Math.min(c.lowlink,i[t].lowlink))})),c.lowlink===c.index){var u,l=[];do{u=n.pop(),i[u].onStack=!1,l.push(u)}while(s!==u);a.push(l)}}(o)})),a}},function(t,e,n){var r=n(10);function i(t){var e={},n={},i=[];if(r.each(t.sinks(),(function o(s){if(r.has(n,s))throw new a;r.has(e,s)||(n[s]=!0,e[s]=!0,r.each(t.predecessors(s),o),delete n[s],i.push(s))})),r.size(e)!==t.nodeCount())throw new a;return i}function a(){}t.exports=i,i.CycleException=a,a.prototype=new Error},function(t,e,n){var r=n(10);t.exports=function(t,e,n){r.isArray(e)||(e=[e]);var i=(t.isDirected()?t.successors:t.neighbors).bind(t),a=[],o={};return r.each(e,(function(e){if(!t.hasNode(e))throw new Error("Graph does not have node: "+e);!function t(e,n,i,a,o,s){r.has(a,n)||(a[n]=!0,i||s.push(n),r.each(o(n),(function(n){t(e,n,i,a,o,s)})),i&&s.push(n))}(t,e,"post"===n,o,i,a)})),a}},function(t,e,n){var r;try{r=n(18)}catch(t){}r||(r=window.dagre),t.exports=r},function(t,e,n){var r=n(67),i=n(36),a=n(68),o=n(40),s=Object.prototype,c=s.hasOwnProperty,u=r((function(t,e){t=Object(t);var n=-1,r=e.length,u=r>2?e[2]:void 0;for(u&&a(e[0],e[1],u)&&(r=1);++n<r;)for(var l=e[n],h=o(l),f=-1,d=h.length;++f<d;){var p=h[f],g=t[p];(void 0===g||i(g,s[p])&&!c.call(t,p))&&(t[p]=l[p])}return t}));t.exports=u},function(t,e,n){var r=n(318);t.exports=function(t){return t?(t=r(t))===1/0||t===-1/0?17976931348623157e292*(t<0?-1:1):t==t?t:0:0===t?t:0}},function(t,e,n){var r=n(94);t.exports=function(t){return(null==t?0:t.length)?r(t,1):[]}},function(t,e,n){var r=n(59),i=n(36);t.exports=function(t,e,n){(void 0===n||i(t[e],n))&&(void 0!==n||e in t)||r(t,e,n)}},function(t,e,n){var r=n(34),i=n(63),a=n(21),o=Function.prototype,s=Object.prototype,c=o.toString,u=s.hasOwnProperty,l=c.call(Object);t.exports=function(t){if(!a(t)||"[object Object]"!=r(t))return!1;var e=i(t);if(null===e)return!0;var n=u.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&c.call(n)==l}},function(t,e){t.exports=function(t,e){if(("constructor"!==e||"function"!=typeof t[e])&&"__proto__"!=e)return t[e]}},function(t,e){t.exports=function(t,e){return t<e}},function(t,e,n){var r=n(332),i=n(335)((function(t,e){return null==t?{}:r(t,e)}));t.exports=i},function(t,e,n){var r=n(336)();t.exports=r},function(t,e,n){var r=n(135),i=0;t.exports=function(t){var e=++i;return r(t)+e}},function(t,e,n){"use strict";var r=n(4),i=n(17).Graph,a=n(69).slack;function o(t,e){return r.forEach(t.nodes(),(function n(i){r.forEach(e.nodeEdges(i),(function(r){var o=r.v,s=i===o?r.w:o;t.hasNode(s)||a(e,r)||(t.setNode(s,{}),t.setEdge(i,s,{}),n(s))}))})),t.nodeCount()}function s(t,e){return r.minBy(e.edges(),(function(n){if(t.hasNode(n.v)!==t.hasNode(n.w))return a(e,n)}))}function c(t,e,n){r.forEach(t.nodes(),(function(t){e.node(t).rank+=n}))}t.exports=function(t){var e,n,r=new i({directed:!1}),u=t.nodes()[0],l=t.nodeCount();r.setNode(u,{});for(;o(r,t)<l;)e=s(r,t),n=r.hasNode(e.v)?a(t,e):-a(t,e),c(r,t,n);return r}},function(t,e){t.exports=function(t,e){return t.intersect(e)}},function(t,e,n){var r=n(96);t.exports=function(t,e,n){return r(t,e,e,n)}},function(t,e,n){var r=n(369);t.exports=function(t,e,n){var i=t.x,a=t.y,o=[],s=Number.POSITIVE_INFINITY,c=Number.POSITIVE_INFINITY;e.forEach((function(t){s=Math.min(s,t.x),c=Math.min(c,t.y)}));for(var u=i-t.width/2-s,l=a-t.height/2-c,h=0;h<e.length;h++){var f=e[h],d=e[h<e.length-1?h+1:0],p=r(t,n,{x:u+f.x,y:l+f.y},{x:u+d.x,y:l+d.y});p&&o.push(p)}if(!o.length)return console.log("NO INTERSECTION FOUND, RETURN NODE CENTER",t),t;o.length>1&&o.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1}));return o[0]}},function(t,e){t.exports=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o);return{x:i+n,y:a+r}}},function(t,e,n){t.exports=function t(e){"use strict";var n=/^\0+/g,r=/[\0\r\f]/g,i=/: */g,a=/zoo|gra/,o=/([,: ])(transform)/g,s=/,+\s*(?![^(]*[)])/g,c=/ +\s*(?![^(]*[)])/g,u=/ *[\0] */g,l=/,\r+?/g,h=/([\t\r\n ])*\f?&/g,f=/:global\(((?:[^\(\)\[\]]*|\[.*\]|\([^\(\)]*\))*)\)/g,d=/\W+/g,p=/@(k\w+)\s*(\S*)\s*/,g=/::(place)/g,y=/:(read-only)/g,v=/\s+(?=[{\];=:>])/g,m=/([[}=:>])\s+/g,b=/(\{[^{]+?);(?=\})/g,x=/\s{2,}/g,_=/([^\(])(:+) */g,k=/[svh]\w+-[tblr]{2}/,w=/\(\s*(.*)\s*\)/g,E=/([\s\S]*?);/g,T=/-self|flex-/g,C=/[^]*?(:[rp][el]a[\w-]+)[^]*/,A=/stretch|:\s*\w+\-(?:conte|avail)/,S=/([^-])(image-set\()/,M="-webkit-",O="-moz-",D="-ms-",N=1,B=1,L=0,P=1,I=1,F=1,j=0,R=0,Y=0,z=[],U=[],$=0,W=null,H=0,V=1,G="",q="",X="";function Z(t,e,i,a,o){for(var s,c,l=0,h=0,f=0,d=0,v=0,m=0,b=0,x=0,k=0,E=0,T=0,C=0,A=0,S=0,O=0,D=0,j=0,U=0,W=0,K=i.length,it=K-1,at="",ot="",st="",ct="",ut="",lt="";O<K;){if(b=i.charCodeAt(O),O===it&&h+d+f+l!==0&&(0!==h&&(b=47===h?10:47),d=f=l=0,K++,it++),h+d+f+l===0){if(O===it&&(D>0&&(ot=ot.replace(r,"")),ot.trim().length>0)){switch(b){case 32:case 9:case 59:case 13:case 10:break;default:ot+=i.charAt(O)}b=59}if(1===j)switch(b){case 123:case 125:case 59:case 34:case 39:case 40:case 41:case 44:j=0;case 9:case 13:case 10:case 32:break;default:for(j=0,W=O,v=b,O--,b=59;W<K;)switch(i.charCodeAt(W++)){case 10:case 13:case 59:++O,b=v,W=K;break;case 58:D>0&&(++O,b=v);case 123:W=K}}switch(b){case 123:for(v=(ot=ot.trim()).charCodeAt(0),T=1,W=++O;O<K;){switch(b=i.charCodeAt(O)){case 123:T++;break;case 125:T--;break;case 47:switch(m=i.charCodeAt(O+1)){case 42:case 47:O=rt(m,O,it,i)}break;case 91:b++;case 40:b++;case 34:case 39:for(;O++<it&&i.charCodeAt(O)!==b;);}if(0===T)break;O++}switch(st=i.substring(W,O),0===v&&(v=(ot=ot.replace(n,"").trim()).charCodeAt(0)),v){case 64:switch(D>0&&(ot=ot.replace(r,"")),m=ot.charCodeAt(1)){case 100:case 109:case 115:case 45:s=e;break;default:s=z}if(W=(st=Z(e,s,st,m,o+1)).length,Y>0&&0===W&&(W=ot.length),$>0&&(c=nt(3,st,s=J(z,ot,U),e,B,N,W,m,o,a),ot=s.join(""),void 0!==c&&0===(W=(st=c.trim()).length)&&(m=0,st="")),W>0)switch(m){case 115:ot=ot.replace(w,et);case 100:case 109:case 45:st=ot+"{"+st+"}";break;case 107:st=(ot=ot.replace(p,"$1 $2"+(V>0?G:"")))+"{"+st+"}",st=1===I||2===I&&tt("@"+st,3)?"@"+M+st+"@"+st:"@"+st;break;default:st=ot+st,112===a&&(ct+=st,st="")}else st="";break;default:st=Z(e,J(e,ot,U),st,a,o+1)}ut+=st,C=0,j=0,S=0,D=0,U=0,A=0,ot="",st="",b=i.charCodeAt(++O);break;case 125:case 59:if((W=(ot=(D>0?ot.replace(r,""):ot).trim()).length)>1)switch(0===S&&(45===(v=ot.charCodeAt(0))||v>96&&v<123)&&(W=(ot=ot.replace(" ",":")).length),$>0&&void 0!==(c=nt(1,ot,e,t,B,N,ct.length,a,o,a))&&0===(W=(ot=c.trim()).length)&&(ot="\0\0"),v=ot.charCodeAt(0),m=ot.charCodeAt(1),v){case 0:break;case 64:if(105===m||99===m){lt+=ot+i.charAt(O);break}default:if(58===ot.charCodeAt(W-1))break;ct+=Q(ot,v,m,ot.charCodeAt(2))}C=0,j=0,S=0,D=0,U=0,ot="",b=i.charCodeAt(++O)}}switch(b){case 13:case 10:if(h+d+f+l+R===0)switch(E){case 41:case 39:case 34:case 64:case 126:case 62:case 42:case 43:case 47:case 45:case 58:case 44:case 59:case 123:case 125:break;default:S>0&&(j=1)}47===h?h=0:P+C===0&&107!==a&&ot.length>0&&(D=1,ot+="\0"),$*H>0&&nt(0,ot,e,t,B,N,ct.length,a,o,a),N=1,B++;break;case 59:case 125:if(h+d+f+l===0){N++;break}default:switch(N++,at=i.charAt(O),b){case 9:case 32:if(d+l+h===0)switch(x){case 44:case 58:case 9:case 32:at="";break;default:32!==b&&(at=" ")}break;case 0:at="\\0";break;case 12:at="\\f";break;case 11:at="\\v";break;case 38:d+h+l===0&&P>0&&(U=1,D=1,at="\f"+at);break;case 108:if(d+h+l+L===0&&S>0)switch(O-S){case 2:112===x&&58===i.charCodeAt(O-3)&&(L=x);case 8:111===k&&(L=k)}break;case 58:d+h+l===0&&(S=O);break;case 44:h+f+d+l===0&&(D=1,at+="\r");break;case 34:case 39:0===h&&(d=d===b?0:0===d?b:d);break;case 91:d+h+f===0&&l++;break;case 93:d+h+f===0&&l--;break;case 41:d+h+l===0&&f--;break;case 40:if(d+h+l===0){if(0===C)switch(2*x+3*k){case 533:break;default:T=0,C=1}f++}break;case 64:h+f+d+l+S+A===0&&(A=1);break;case 42:case 47:if(d+l+f>0)break;switch(h){case 0:switch(2*b+3*i.charCodeAt(O+1)){case 235:h=47;break;case 220:W=O,h=42}break;case 42:47===b&&42===x&&W+2!==O&&(33===i.charCodeAt(W+2)&&(ct+=i.substring(W,O+1)),at="",h=0)}}if(0===h){if(P+d+l+A===0&&107!==a&&59!==b)switch(b){case 44:case 126:case 62:case 43:case 41:case 40:if(0===C){switch(x){case 9:case 32:case 10:case 13:at+="\0";break;default:at="\0"+at+(44===b?"":"\0")}D=1}else switch(b){case 40:S+7===O&&108===x&&(S=0),C=++T;break;case 41:0==(C=--T)&&(D=1,at+="\0")}break;case 9:case 32:switch(x){case 0:case 123:case 125:case 59:case 44:case 12:case 9:case 32:case 10:case 13:break;default:0===C&&(D=1,at+="\0")}}ot+=at,32!==b&&9!==b&&(E=b)}}k=x,x=b,O++}if(W=ct.length,Y>0&&0===W&&0===ut.length&&0===e[0].length==0&&(109!==a||1===e.length&&(P>0?q:X)===e[0])&&(W=e.join(",").length+2),W>0){if(s=0===P&&107!==a?function(t){for(var e,n,i=0,a=t.length,o=Array(a);i<a;++i){for(var s=t[i].split(u),c="",l=0,h=0,f=0,d=0,p=s.length;l<p;++l)if(!(0===(h=(n=s[l]).length)&&p>1)){if(f=c.charCodeAt(c.length-1),d=n.charCodeAt(0),e="",0!==l)switch(f){case 42:case 126:case 62:case 43:case 32:case 40:break;default:e=" "}switch(d){case 38:n=e+q;case 126:case 62:case 43:case 32:case 41:case 40:break;case 91:n=e+n+q;break;case 58:switch(2*n.charCodeAt(1)+3*n.charCodeAt(2)){case 530:if(F>0){n=e+n.substring(8,h-1);break}default:(l<1||s[l-1].length<1)&&(n=e+q+n)}break;case 44:e="";default:n=h>1&&n.indexOf(":")>0?e+n.replace(_,"$1"+q+"$2"):e+n+q}c+=n}o[i]=c.replace(r,"").trim()}return o}(e):e,$>0&&void 0!==(c=nt(2,ct,s,t,B,N,W,a,o,a))&&0===(ct=c).length)return lt+ct+ut;if(ct=s.join(",")+"{"+ct+"}",I*L!=0){switch(2!==I||tt(ct,2)||(L=0),L){case 111:ct=ct.replace(y,":-moz-$1")+ct;break;case 112:ct=ct.replace(g,"::-webkit-input-$1")+ct.replace(g,"::-moz-$1")+ct.replace(g,":-ms-input-$1")+ct}L=0}}return lt+ct+ut}function J(t,e,n){var r=e.trim().split(l),i=r,a=r.length,o=t.length;switch(o){case 0:case 1:for(var s=0,c=0===o?"":t[0]+" ";s<a;++s)i[s]=K(c,i[s],n,o).trim();break;default:s=0;var u=0;for(i=[];s<a;++s)for(var h=0;h<o;++h)i[u++]=K(t[h]+" ",r[s],n,o).trim()}return i}function K(t,e,n,r){var i=e,a=i.charCodeAt(0);switch(a<33&&(a=(i=i.trim()).charCodeAt(0)),a){case 38:switch(P+r){case 0:case 1:if(0===t.trim().length)break;default:return i.replace(h,"$1"+t.trim())}break;case 58:switch(i.charCodeAt(1)){case 103:if(F>0&&P>0)return i.replace(f,"$1").replace(h,"$1"+X);break;default:return t.trim()+i.replace(h,"$1"+t.trim())}default:if(n*P>0&&i.indexOf("\f")>0)return i.replace(h,(58===t.charCodeAt(0)?"":"$1")+t.trim())}return t+i}function Q(t,e,n,r){var u,l=0,h=t+";",f=2*e+3*n+4*r;if(944===f)return function(t){var e=t.length,n=t.indexOf(":",9)+1,r=t.substring(0,n).trim(),i=t.substring(n,e-1).trim();switch(t.charCodeAt(9)*V){case 0:break;case 45:if(110!==t.charCodeAt(10))break;default:var a=i.split((i="",s)),o=0;for(n=0,e=a.length;o<e;n=0,++o){for(var u=a[o],l=u.split(c);u=l[n];){var h=u.charCodeAt(0);if(1===V&&(h>64&&h<90||h>96&&h<123||95===h||45===h&&45!==u.charCodeAt(1)))switch(isNaN(parseFloat(u))+(-1!==u.indexOf("("))){case 1:switch(u){case"infinite":case"alternate":case"backwards":case"running":case"normal":case"forwards":case"both":case"none":case"linear":case"ease":case"ease-in":case"ease-out":case"ease-in-out":case"paused":case"reverse":case"alternate-reverse":case"inherit":case"initial":case"unset":case"step-start":case"step-end":break;default:u+=G}}l[n++]=u}i+=(0===o?"":",")+l.join(" ")}}return i=r+i+";",1===I||2===I&&tt(i,1)?M+i+i:i}(h);if(0===I||2===I&&!tt(h,1))return h;switch(f){case 1015:return 97===h.charCodeAt(10)?M+h+h:h;case 951:return 116===h.charCodeAt(3)?M+h+h:h;case 963:return 110===h.charCodeAt(5)?M+h+h:h;case 1009:if(100!==h.charCodeAt(4))break;case 969:case 942:return M+h+h;case 978:return M+h+O+h+h;case 1019:case 983:return M+h+O+h+D+h+h;case 883:return 45===h.charCodeAt(8)?M+h+h:h.indexOf("image-set(",11)>0?h.replace(S,"$1-webkit-$2")+h:h;case 932:if(45===h.charCodeAt(4))switch(h.charCodeAt(5)){case 103:return M+"box-"+h.replace("-grow","")+M+h+D+h.replace("grow","positive")+h;case 115:return M+h+D+h.replace("shrink","negative")+h;case 98:return M+h+D+h.replace("basis","preferred-size")+h}return M+h+D+h+h;case 964:return M+h+D+"flex-"+h+h;case 1023:if(99!==h.charCodeAt(8))break;return u=h.substring(h.indexOf(":",15)).replace("flex-","").replace("space-between","justify"),M+"box-pack"+u+M+h+D+"flex-pack"+u+h;case 1005:return a.test(h)?h.replace(i,":"+M)+h.replace(i,":"+O)+h:h;case 1e3:switch(l=(u=h.substring(13).trim()).indexOf("-")+1,u.charCodeAt(0)+u.charCodeAt(l)){case 226:u=h.replace(k,"tb");break;case 232:u=h.replace(k,"tb-rl");break;case 220:u=h.replace(k,"lr");break;default:return h}return M+h+D+u+h;case 1017:if(-1===h.indexOf("sticky",9))return h;case 975:switch(l=(h=t).length-10,f=(u=(33===h.charCodeAt(l)?h.substring(0,l):h).substring(t.indexOf(":",7)+1).trim()).charCodeAt(0)+(0|u.charCodeAt(7))){case 203:if(u.charCodeAt(8)<111)break;case 115:h=h.replace(u,M+u)+";"+h;break;case 207:case 102:h=h.replace(u,M+(f>102?"inline-":"")+"box")+";"+h.replace(u,M+u)+";"+h.replace(u,D+u+"box")+";"+h}return h+";";case 938:if(45===h.charCodeAt(5))switch(h.charCodeAt(6)){case 105:return u=h.replace("-items",""),M+h+M+"box-"+u+D+"flex-"+u+h;case 115:return M+h+D+"flex-item-"+h.replace(T,"")+h;default:return M+h+D+"flex-line-pack"+h.replace("align-content","").replace(T,"")+h}break;case 973:case 989:if(45!==h.charCodeAt(3)||122===h.charCodeAt(4))break;case 931:case 953:if(!0===A.test(t))return 115===(u=t.substring(t.indexOf(":")+1)).charCodeAt(0)?Q(t.replace("stretch","fill-available"),e,n,r).replace(":fill-available",":stretch"):h.replace(u,M+u)+h.replace(u,O+u.replace("fill-",""))+h;break;case 962:if(h=M+h+(102===h.charCodeAt(5)?D+h:"")+h,n+r===211&&105===h.charCodeAt(13)&&h.indexOf("transform",10)>0)return h.substring(0,h.indexOf(";",27)+1).replace(o,"$1-webkit-$2")+h}return h}function tt(t,e){var n=t.indexOf(1===e?":":"{"),r=t.substring(0,3!==e?n:10),i=t.substring(n+1,t.length-1);return W(2!==e?r:r.replace(C,"$1"),i,e)}function et(t,e){var n=Q(e,e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2));return n!==e+";"?n.replace(E," or ($1)").substring(4):"("+e+")"}function nt(t,e,n,r,i,a,o,s,c,u){for(var l,h=0,f=e;h<$;++h)switch(l=U[h].call(at,t,f,n,r,i,a,o,s,c,u)){case void 0:case!1:case!0:case null:break;default:f=l}if(f!==e)return f}function rt(t,e,n,r){for(var i=e+1;i<n;++i)switch(r.charCodeAt(i)){case 47:if(42===t&&42===r.charCodeAt(i-1)&&e+2!==i)return i+1;break;case 10:if(47===t)return i+1}return i}function it(t){for(var e in t){var n=t[e];switch(e){case"keyframe":V=0|n;break;case"global":F=0|n;break;case"cascade":P=0|n;break;case"compress":j=0|n;break;case"semicolon":R=0|n;break;case"preserve":Y=0|n;break;case"prefix":W=null,n?"function"!=typeof n?I=1:(I=2,W=n):I=0}}return it}function at(e,n){if(void 0!==this&&this.constructor===at)return t(e);var i=e,a=i.charCodeAt(0);a<33&&(a=(i=i.trim()).charCodeAt(0)),V>0&&(G=i.replace(d,91===a?"":"-")),a=1,1===P?X=i:q=i;var o,s=[X];$>0&&void 0!==(o=nt(-1,n,s,s,B,N,0,0,0,0))&&"string"==typeof o&&(n=o);var c=Z(z,s,n,0,0);return $>0&&void 0!==(o=nt(-2,c,s,s,B,N,c.length,0,0,0))&&"string"!=typeof(c=o)&&(a=0),G="",X="",q="",L=0,B=1,N=1,j*a==0?c:function(t){return t.replace(r,"").replace(v,"").replace(m,"$1").replace(b,"$1").replace(x," ")}(c)}return at.use=function t(e){switch(e){case void 0:case null:$=U.length=0;break;default:if("function"==typeof e)U[$++]=e;else if("object"==typeof e)for(var n=0,r=e.length;n<r;++n)t(e[n]);else H=0|!!e}return t},at.set=it,void 0!==e&&it(e),at}(null)},function(t,e){t.exports=function(t,e){return t.intersect(e)}},function(t,e,n){var r={"./locale":98,"./locale.js":98};function i(t){var e=a(t);return n(e)}function a(t){if(!n.o(r,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}return r[t]}i.keys=function(){return Object.keys(r)},i.resolve=a,t.exports=i,i.id=171},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(51);e.hex=r.default;var i=n(51);e.rgb=i.default;var a=n(51);e.rgba=a.default;var o=n(100);e.hsl=o.default;var s=n(100);e.hsla=s.default;var c=n(29);e.channel=c.default;var u=n(182);e.red=u.default;var l=n(183);e.green=l.default;var h=n(184);e.blue=h.default;var f=n(185);e.hue=f.default;var d=n(186);e.saturation=d.default;var p=n(187);e.lightness=p.default;var g=n(101);e.alpha=g.default;var y=n(101);e.opacity=y.default;var v=n(102);e.luminance=v.default;var m=n(188);e.isDark=m.default;var b=n(103);e.isLight=b.default;var x=n(189);e.isValid=x.default;var _=n(190);e.saturate=_.default;var k=n(191);e.desaturate=k.default;var w=n(192);e.lighten=w.default;var E=n(193);e.darken=E.default;var T=n(104);e.opacify=T.default;var C=n(104);e.fadeIn=C.default;var A=n(105);e.transparentize=A.default;var S=n(105);e.fadeOut=S.default;var M=n(194);e.complement=M.default;var O=n(195);e.grayscale=O.default;var D=n(106);e.adjust=D.default;var N=n(52);e.change=N.default;var B=n(196);e.invert=B.default;var L=n(107);e.mix=L.default;var P=n(197);e.scale=P.default},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:function(t){return t>=255?255:t<0?0:t},g:function(t){return t>=255?255:t<0?0:t},b:function(t){return t>=255?255:t<0?0:t},h:function(t){return t%360},s:function(t){return t>=100?100:t<0?0:t},l:function(t){return t>=100?100:t<0?0:t},a:function(t){return t>=1?1:t<0?0:t}},toLinear:function(t){var e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},hue2rgb:function(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t},hsl2rgb:function(t,e){var n=t.h,i=t.s,a=t.l;if(100===i)return 2.55*a;n/=360,i/=100;var o=(a/=100)<.5?a*(1+i):a+i-a*i,s=2*a-o;switch(e){case"r":return 255*r.hue2rgb(s,o,n+1/3);case"g":return 255*r.hue2rgb(s,o,n);case"b":return 255*r.hue2rgb(s,o,n-1/3)}},rgb2hsl:function(t,e){var n=t.r,r=t.g,i=t.b;n/=255,r/=255,i/=255;var a=Math.max(n,r,i),o=Math.min(n,r,i),s=(a+o)/2;if("l"===e)return 100*s;if(a===o)return 0;var c=a-o;if("s"===e)return 100*(s>.5?c/(2-a-o):c/(a+o));switch(a){case n:return 60*((r-i)/c+(r<i?6:0));case r:return 60*((i-n)/c+2);case i:return 60*((n-r)/c+4);default:return-1}}};e.default=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r={round:function(t){return Math.round(1e10*t)/1e10}};e.default=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r={frac2hex:function(t){var e=Math.round(255*t).toString(16);return e.length>1?e:"0"+e},dec2hex:function(t){var e=Math.round(t).toString(16);return e.length>1?e:"0"+e}};e.default=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(75),a=n(177),o=function(){function t(t,e){this.color=e,this.changed=!1,this.data=t,this.type=new a.default}return t.prototype.set=function(t,e){return this.color=e,this.changed=!1,this.data=t,this.type.type=i.TYPE.ALL,this},t.prototype._ensureHSL=function(){void 0===this.data.h&&(this.data.h=r.default.channel.rgb2hsl(this.data,"h")),void 0===this.data.s&&(this.data.s=r.default.channel.rgb2hsl(this.data,"s")),void 0===this.data.l&&(this.data.l=r.default.channel.rgb2hsl(this.data,"l"))},t.prototype._ensureRGB=function(){void 0===this.data.r&&(this.data.r=r.default.channel.hsl2rgb(this.data,"r")),void 0===this.data.g&&(this.data.g=r.default.channel.hsl2rgb(this.data,"g")),void 0===this.data.b&&(this.data.b=r.default.channel.hsl2rgb(this.data,"b"))},Object.defineProperty(t.prototype,"r",{get:function(){return this.type.is(i.TYPE.HSL)||void 0===this.data.r?(this._ensureHSL(),r.default.channel.hsl2rgb(this.data,"r")):this.data.r},set:function(t){this.type.set(i.TYPE.RGB),this.changed=!0,this.data.r=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"g",{get:function(){return this.type.is(i.TYPE.HSL)||void 0===this.data.g?(this._ensureHSL(),r.default.channel.hsl2rgb(this.data,"g")):this.data.g},set:function(t){this.type.set(i.TYPE.RGB),this.changed=!0,this.data.g=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"b",{get:function(){return this.type.is(i.TYPE.HSL)||void 0===this.data.b?(this._ensureHSL(),r.default.channel.hsl2rgb(this.data,"b")):this.data.b},set:function(t){this.type.set(i.TYPE.RGB),this.changed=!0,this.data.b=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"h",{get:function(){return this.type.is(i.TYPE.RGB)||void 0===this.data.h?(this._ensureRGB(),r.default.channel.rgb2hsl(this.data,"h")):this.data.h},set:function(t){this.type.set(i.TYPE.HSL),this.changed=!0,this.data.h=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"s",{get:function(){return this.type.is(i.TYPE.RGB)||void 0===this.data.s?(this._ensureRGB(),r.default.channel.rgb2hsl(this.data,"s")):this.data.s},set:function(t){this.type.set(i.TYPE.HSL),this.changed=!0,this.data.s=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"l",{get:function(){return this.type.is(i.TYPE.RGB)||void 0===this.data.l?(this._ensureRGB(),r.default.channel.rgb2hsl(this.data,"l")):this.data.l},set:function(t){this.type.set(i.TYPE.HSL),this.changed=!0,this.data.l=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"a",{get:function(){return this.data.a},set:function(t){this.changed=!0,this.data.a=t},enumerable:!0,configurable:!0}),t}();e.default=o},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(75),i=function(){function t(){this.type=r.TYPE.ALL}return t.prototype.get=function(){return this.type},t.prototype.set=function(t){if(this.type&&this.type!==t)throw new Error("Cannot change both RGB and HSL channels at the same time");this.type=t},t.prototype.reset=function(){this.type=r.TYPE.ALL},t.prototype.is=function(t){return this.type===t},t}();e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i={};e.DEC2HEX=i;for(var a=0;a<=255;a++)i[a]=r.default.unit.dec2hex(a)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(99),i={colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyanaqua:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",transparent:"#00000000",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},parse:function(t){t=t.toLowerCase();var e=i.colors[t];if(e)return r.default.parse(e)},stringify:function(t){var e=r.default.stringify(t);for(var n in i.colors)if(i.colors[n]===e)return n}};e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a={re:/^rgba?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?)))?\s*?\)$/i,parse:function(t){var e=t.charCodeAt(0);if(114===e||82===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5],h=n[6],f=n[7],d=n[8];return i.default.set({r:r.default.channel.clamp.r(s?2.55*parseFloat(o):parseFloat(o)),g:r.default.channel.clamp.g(u?2.55*parseFloat(c):parseFloat(c)),b:r.default.channel.clamp.b(h?2.55*parseFloat(l):parseFloat(l)),a:f?r.default.channel.clamp.a(d?parseFloat(f)/100:parseFloat(f)):1},t)}}},stringify:function(t){return t.a<1?"rgba("+r.default.lang.round(t.r)+", "+r.default.lang.round(t.g)+", "+r.default.lang.round(t.b)+", "+r.default.lang.round(t.a)+")":"rgb("+r.default.lang.round(t.r)+", "+r.default.lang.round(t.g)+", "+r.default.lang.round(t.b)+")"}};e.default=a},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a={re:/^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i,hueRe:/^(.+?)(deg|grad|rad|turn)$/i,_hue2deg:function(t){var e=t.match(a.hueRe);if(e){var n=e[1];switch(e[2]){case"grad":return r.default.channel.clamp.h(.9*parseFloat(n));case"rad":return r.default.channel.clamp.h(180*parseFloat(n)/Math.PI);case"turn":return r.default.channel.clamp.h(360*parseFloat(n))}}return r.default.channel.clamp.h(parseFloat(t))},parse:function(t){var e=t.charCodeAt(0);if(104===e||72===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5];return i.default.set({h:a._hue2deg(o),s:r.default.channel.clamp.s(parseFloat(s)),l:r.default.channel.clamp.l(parseFloat(c)),a:u?r.default.channel.clamp.a(l?parseFloat(u)/100:parseFloat(u)):1},t)}}},stringify:function(t){return t.a<1?"hsla("+r.default.lang.round(t.h)+", "+r.default.lang.round(t.s)+"%, "+r.default.lang.round(t.l)+"%, "+t.a+")":"hsl("+r.default.lang.round(t.h)+", "+r.default.lang.round(t.s)+"%, "+r.default.lang.round(t.l)+"%)"}};e.default=a},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"r")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"g")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"b")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"h")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"s")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"l")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(103);e.default=function(t){return!r.default(t)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15);e.default=function(t){try{return r.default.parse(t),!0}catch(t){return!1}}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"s",e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"s",-e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"l",e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"l",-e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t){return r.default(t,"h",180)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(52);e.default=function(t){return r.default(t,{s:0})}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15),i=n(107);e.default=function(t,e){void 0===e&&(e=100);var n=r.default.parse(t);return n.r=255-n.r,n.g=255-n.g,n.b=255-n.b,i.default(n,t,e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15),a=n(106);e.default=function(t,e){var n,o,s,c=i.default.parse(t),u={};for(var l in e)u[l]=(n=c[l],o=e[l],s=r.default.channel.max[l],o>0?(s-n)*o/100:n*o/100);return a.default(t,u)}},function(t,e,n){t.exports={Graph:n(76),version:n(300)}},function(t,e,n){var r=n(108);t.exports=function(t){return r(t,4)}},function(t,e){t.exports=function(){this.__data__=[],this.size=0}},function(t,e,n){var r=n(55),i=Array.prototype.splice;t.exports=function(t){var e=this.__data__,n=r(e,t);return!(n<0)&&(n==e.length-1?e.pop():i.call(e,n,1),--this.size,!0)}},function(t,e,n){var r=n(55);t.exports=function(t){var e=this.__data__,n=r(e,t);return n<0?void 0:e[n][1]}},function(t,e,n){var r=n(55);t.exports=function(t){return r(this.__data__,t)>-1}},function(t,e,n){var r=n(55);t.exports=function(t,e){var n=this.__data__,i=r(n,t);return i<0?(++this.size,n.push([t,e])):n[i][1]=e,this}},function(t,e,n){var r=n(54);t.exports=function(){this.__data__=new r,this.size=0}},function(t,e){t.exports=function(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}},function(t,e){t.exports=function(t){return this.__data__.get(t)}},function(t,e){t.exports=function(t){return this.__data__.has(t)}},function(t,e,n){var r=n(54),i=n(77),a=n(78);t.exports=function(t,e){var n=this.__data__;if(n instanceof r){var o=n.__data__;if(!i||o.length<199)return o.push([t,e]),this.size=++n.size,this;n=this.__data__=new a(o)}return n.set(t,e),this.size=n.size,this}},function(t,e,n){var r=n(37),i=n(214),a=n(11),o=n(110),s=/^\[object .+?Constructor\]$/,c=Function.prototype,u=Object.prototype,l=c.toString,h=u.hasOwnProperty,f=RegExp("^"+l.call(h).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=function(t){return!(!a(t)||i(t))&&(r(t)?f:s).test(o(t))}},function(t,e){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e,n){var r=n(38),i=Object.prototype,a=i.hasOwnProperty,o=i.toString,s=r?r.toStringTag:void 0;t.exports=function(t){var e=a.call(t,s),n=t[s];try{t[s]=void 0;var r=!0}catch(t){}var i=o.call(t);return r&&(e?t[s]=n:delete t[s]),i}},function(t,e){var n=Object.prototype.toString;t.exports=function(t){return n.call(t)}},function(t,e,n){var r,i=n(215),a=(r=/[^.]+$/.exec(i&&i.keys&&i.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";t.exports=function(t){return!!a&&a in t}},function(t,e,n){var r=n(16)["__core-js_shared__"];t.exports=r},function(t,e){t.exports=function(t,e){return null==t?void 0:t[e]}},function(t,e,n){var r=n(218),i=n(54),a=n(77);t.exports=function(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}},function(t,e,n){var r=n(219),i=n(220),a=n(221),o=n(222),s=n(223);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},function(t,e,n){var r=n(56);t.exports=function(){this.__data__=r?r(null):{},this.size=0}},function(t,e){t.exports=function(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}},function(t,e,n){var r=n(56),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;if(r){var n=e[t];return"__lodash_hash_undefined__"===n?void 0:n}return i.call(e,t)?e[t]:void 0}},function(t,e,n){var r=n(56),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;return r?void 0!==e[t]:i.call(e,t)}},function(t,e,n){var r=n(56);t.exports=function(t,e){var n=this.__data__;return this.size+=this.has(t)?0:1,n[t]=r&&void 0===e?"__lodash_hash_undefined__":e,this}},function(t,e,n){var r=n(57);t.exports=function(t){var e=r(this,t).delete(t);return this.size-=e?1:0,e}},function(t,e){t.exports=function(t){var e=typeof t;return"string"==e||"number"==e||"symbol"==e||"boolean"==e?"__proto__"!==t:null===t}},function(t,e,n){var r=n(57);t.exports=function(t){return r(this,t).get(t)}},function(t,e,n){var r=n(57);t.exports=function(t){return r(this,t).has(t)}},function(t,e,n){var r=n(57);t.exports=function(t,e){var n=r(this,t),i=n.size;return n.set(t,e),this.size+=n.size==i?0:1,this}},function(t,e,n){var r=n(46),i=n(30);t.exports=function(t,e){return t&&r(e,i(e),t)}},function(t,e){t.exports=function(t,e){for(var n=-1,r=Array(t);++n<t;)r[n]=e(n);return r}},function(t,e,n){var r=n(34),i=n(21);t.exports=function(t){return i(t)&&"[object Arguments]"==r(t)}},function(t,e){t.exports=function(){return!1}},function(t,e,n){var r=n(34),i=n(80),a=n(21),o={};o["[object Float32Array]"]=o["[object Float64Array]"]=o["[object Int8Array]"]=o["[object Int16Array]"]=o["[object Int32Array]"]=o["[object Uint8Array]"]=o["[object Uint8ClampedArray]"]=o["[object Uint16Array]"]=o["[object Uint32Array]"]=!0,o["[object Arguments]"]=o["[object Array]"]=o["[object ArrayBuffer]"]=o["[object Boolean]"]=o["[object DataView]"]=o["[object Date]"]=o["[object Error]"]=o["[object Function]"]=o["[object Map]"]=o["[object Number]"]=o["[object Object]"]=o["[object RegExp]"]=o["[object Set]"]=o["[object String]"]=o["[object WeakMap]"]=!1,t.exports=function(t){return a(t)&&i(t.length)&&!!o[r(t)]}},function(t,e,n){var r=n(113)(Object.keys,Object);t.exports=r},function(t,e,n){var r=n(46),i=n(40);t.exports=function(t,e){return t&&r(e,i(e),t)}},function(t,e,n){var r=n(11),i=n(62),a=n(237),o=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return a(t);var e=i(t),n=[];for(var s in t)("constructor"!=s||!e&&o.call(t,s))&&n.push(s);return n}},function(t,e){t.exports=function(t){var e=[];if(null!=t)for(var n in Object(t))e.push(n);return e}},function(t,e,n){var r=n(46),i=n(83);t.exports=function(t,e){return r(t,i(t),e)}},function(t,e,n){var r=n(46),i=n(118);t.exports=function(t,e){return r(t,i(t),e)}},function(t,e,n){var r=n(120),i=n(118),a=n(40);t.exports=function(t){return r(t,a,i)}},function(t,e,n){var r=n(33)(n(16),"DataView");t.exports=r},function(t,e,n){var r=n(33)(n(16),"Promise");t.exports=r},function(t,e,n){var r=n(33)(n(16),"WeakMap");t.exports=r},function(t,e){var n=Object.prototype.hasOwnProperty;t.exports=function(t){var e=t.length,r=new t.constructor(e);return e&&"string"==typeof t[0]&&n.call(t,"index")&&(r.index=t.index,r.input=t.input),r}},function(t,e,n){var r=n(85),i=n(246),a=n(247),o=n(248),s=n(123);t.exports=function(t,e,n){var c=t.constructor;switch(e){case"[object ArrayBuffer]":return r(t);case"[object Boolean]":case"[object Date]":return new c(+t);case"[object DataView]":return i(t,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return s(t,n);case"[object Map]":return new c;case"[object Number]":case"[object String]":return new c(t);case"[object RegExp]":return a(t);case"[object Set]":return new c;case"[object Symbol]":return o(t)}}},function(t,e,n){var r=n(85);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}},function(t,e){var n=/\w*$/;t.exports=function(t){var e=new t.constructor(t.source,n.exec(t));return e.lastIndex=t.lastIndex,e}},function(t,e,n){var r=n(38),i=r?r.prototype:void 0,a=i?i.valueOf:void 0;t.exports=function(t){return a?Object(a.call(t)):{}}},function(t,e,n){var r=n(250),i=n(61),a=n(81),o=a&&a.isMap,s=o?i(o):r;t.exports=s},function(t,e,n){var r=n(41),i=n(21);t.exports=function(t){return i(t)&&"[object Map]"==r(t)}},function(t,e,n){var r=n(252),i=n(61),a=n(81),o=a&&a.isSet,s=o?i(o):r;t.exports=s},function(t,e,n){var r=n(41),i=n(21);t.exports=function(t){return i(t)&&"[object Set]"==r(t)}},function(t,e){t.exports=function(t){return function(e,n,r){for(var i=-1,a=Object(e),o=r(e),s=o.length;s--;){var c=o[t?s:++i];if(!1===n(a[c],c,a))break}return e}}},function(t,e,n){var r=n(24);t.exports=function(t,e){return function(n,i){if(null==n)return n;if(!r(n))return t(n,i);for(var a=n.length,o=e?a:-1,s=Object(n);(e?o--:++o<a)&&!1!==i(s[o],o,s););return n}}},function(t,e,n){var r=n(64);t.exports=function(t,e){var n=[];return r(t,(function(t,r,i){e(t,r,i)&&n.push(t)})),n}},function(t,e,n){var r=n(257),i=n(265),a=n(134);t.exports=function(t){var e=i(t);return 1==e.length&&e[0][2]?a(e[0][0],e[0][1]):function(n){return n===t||r(n,t,e)}}},function(t,e,n){var r=n(53),i=n(129);t.exports=function(t,e,n,a){var o=n.length,s=o,c=!a;if(null==t)return!s;for(t=Object(t);o--;){var u=n[o];if(c&&u[2]?u[1]!==t[u[0]]:!(u[0]in t))return!1}for(;++o<s;){var l=(u=n[o])[0],h=t[l],f=u[1];if(c&&u[2]){if(void 0===h&&!(l in t))return!1}else{var d=new r;if(a)var p=a(h,f,l,t,e,d);if(!(void 0===p?i(f,h,3,a,d):p))return!1}}return!0}},function(t,e,n){var r=n(53),i=n(130),a=n(262),o=n(264),s=n(41),c=n(5),u=n(39),l=n(48),h="[object Object]",f=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,d,p,g){var y=c(t),v=c(e),m=y?"[object Array]":s(t),b=v?"[object Array]":s(e),x=(m="[object Arguments]"==m?h:m)==h,_=(b="[object Arguments]"==b?h:b)==h,k=m==b;if(k&&u(t)){if(!u(e))return!1;y=!0,x=!1}if(k&&!x)return g||(g=new r),y||l(t)?i(t,e,n,d,p,g):a(t,e,m,n,d,p,g);if(!(1&n)){var w=x&&f.call(t,"__wrapped__"),E=_&&f.call(e,"__wrapped__");if(w||E){var T=w?t.value():t,C=E?e.value():e;return g||(g=new r),p(T,C,n,d,g)}}return!!k&&(g||(g=new r),o(t,e,n,d,p,g))}},function(t,e){t.exports=function(t){return this.__data__.set(t,"__lodash_hash_undefined__"),this}},function(t,e){t.exports=function(t){return this.__data__.has(t)}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r;)if(e(t[n],n,t))return!0;return!1}},function(t,e,n){var r=n(38),i=n(122),a=n(36),o=n(130),s=n(263),c=n(90),u=r?r.prototype:void 0,l=u?u.valueOf:void 0;t.exports=function(t,e,n,r,u,h,f){switch(n){case"[object DataView]":if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case"[object ArrayBuffer]":return!(t.byteLength!=e.byteLength||!h(new i(t),new i(e)));case"[object Boolean]":case"[object Date]":case"[object Number]":return a(+t,+e);case"[object Error]":return t.name==e.name&&t.message==e.message;case"[object RegExp]":case"[object String]":return t==e+"";case"[object Map]":var d=s;case"[object Set]":var p=1&r;if(d||(d=c),t.size!=e.size&&!p)return!1;var g=f.get(t);if(g)return g==e;r|=2,f.set(t,e);var y=o(d(t),d(e),r,u,h,f);return f.delete(t),y;case"[object Symbol]":if(l)return l.call(t)==l.call(e)}return!1}},function(t,e){t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t,r){n[++e]=[r,t]})),n}},function(t,e,n){var r=n(119),i=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,a,o,s){var c=1&n,u=r(t),l=u.length;if(l!=r(e).length&&!c)return!1;for(var h=l;h--;){var f=u[h];if(!(c?f in e:i.call(e,f)))return!1}var d=s.get(t);if(d&&s.get(e))return d==e;var p=!0;s.set(t,e),s.set(e,t);for(var g=c;++h<l;){var y=t[f=u[h]],v=e[f];if(a)var m=c?a(v,y,f,e,t,s):a(y,v,f,t,e,s);if(!(void 0===m?y===v||o(y,v,n,a,s):m)){p=!1;break}g||(g="constructor"==f)}if(p&&!g){var b=t.constructor,x=e.constructor;b!=x&&"constructor"in t&&"constructor"in e&&!("function"==typeof b&&b instanceof b&&"function"==typeof x&&x instanceof x)&&(p=!1)}return s.delete(t),s.delete(e),p}},function(t,e,n){var r=n(133),i=n(30);t.exports=function(t){for(var e=i(t),n=e.length;n--;){var a=e[n],o=t[a];e[n]=[a,o,r(o)]}return e}},function(t,e,n){var r=n(129),i=n(267),a=n(136),o=n(92),s=n(133),c=n(134),u=n(49);t.exports=function(t,e){return o(t)&&s(e)?c(u(t),e):function(n){var o=i(n,t);return void 0===o&&o===e?a(n,t):r(e,o,3)}}},function(t,e,n){var r=n(91);t.exports=function(t,e,n){var i=null==t?void 0:r(t,e);return void 0===i?n:i}},function(t,e,n){var r=n(269),i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,a=/\\(\\)?/g,o=r((function(t){var e=[];return 46===t.charCodeAt(0)&&e.push(""),t.replace(i,(function(t,n,r,i){e.push(r?i.replace(a,"$1"):n||t)})),e}));t.exports=o},function(t,e,n){var r=n(270);t.exports=function(t){var e=r(t,(function(t){return 500===n.size&&n.clear(),t})),n=e.cache;return e}},function(t,e,n){var r=n(78);function i(t,e){if("function"!=typeof t||null!=e&&"function"!=typeof e)throw new TypeError("Expected a function");var n=function(){var r=arguments,i=e?e.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=t.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(i.Cache||r),n}i.Cache=r,t.exports=i},function(t,e,n){var r=n(38),i=n(66),a=n(5),o=n(42),s=r?r.prototype:void 0,c=s?s.toString:void 0;t.exports=function t(e){if("string"==typeof e)return e;if(a(e))return i(e,t)+"";if(o(e))return c?c.call(e):"";var n=e+"";return"0"==n&&1/e==-1/0?"-0":n}},function(t,e){t.exports=function(t,e){return null!=t&&e in Object(t)}},function(t,e,n){var r=n(138),i=n(274),a=n(92),o=n(49);t.exports=function(t){return a(t)?r(o(t)):i(t)}},function(t,e,n){var r=n(91);t.exports=function(t){return function(e){return r(e,t)}}},function(t,e){var n=Object.prototype.hasOwnProperty;t.exports=function(t,e){return null!=t&&n.call(t,e)}},function(t,e,n){var r=n(82),i=n(41),a=n(47),o=n(5),s=n(24),c=n(39),u=n(62),l=n(48),h=Object.prototype.hasOwnProperty;t.exports=function(t){if(null==t)return!0;if(s(t)&&(o(t)||"string"==typeof t||"function"==typeof t.splice||c(t)||l(t)||a(t)))return!t.length;var e=i(t);if("[object Map]"==e||"[object Set]"==e)return!t.size;if(u(t))return!r(t).length;for(var n in t)if(h.call(t,n))return!1;return!0}},function(t,e){t.exports=function(t,e,n,r){var i=-1,a=null==t?0:t.length;for(r&&a&&(n=t[++i]);++i<a;)n=e(n,t[i],i,t);return n}},function(t,e){t.exports=function(t,e,n,r,i){return i(t,(function(t,i,a){n=r?(r=!1,t):e(n,t,i,a)})),n}},function(t,e,n){var r=n(82),i=n(41),a=n(24),o=n(280),s=n(281);t.exports=function(t){if(null==t)return 0;if(a(t))return o(t)?s(t):t.length;var e=i(t);return"[object Map]"==e||"[object Set]"==e?t.size:r(t).length}},function(t,e,n){var r=n(34),i=n(5),a=n(21);t.exports=function(t){return"string"==typeof t||!i(t)&&a(t)&&"[object String]"==r(t)}},function(t,e,n){var r=n(282),i=n(283),a=n(284);t.exports=function(t){return i(t)?a(t):r(t)}},function(t,e,n){var r=n(138)("length");t.exports=r},function(t,e){var n=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");t.exports=function(t){return n.test(t)}},function(t,e){var n="[\\ud800-\\udfff]",r="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",i="\\ud83c[\\udffb-\\udfff]",a="[^\\ud800-\\udfff]",o="(?:\\ud83c[\\udde6-\\uddff]){2}",s="[\\ud800-\\udbff][\\udc00-\\udfff]",c="(?:"+r+"|"+i+")"+"?",u="[\\ufe0e\\ufe0f]?"+c+("(?:\\u200d(?:"+[a,o,s].join("|")+")[\\ufe0e\\ufe0f]?"+c+")*"),l="(?:"+[a+r+"?",r,o,s,n].join("|")+")",h=RegExp(i+"(?="+i+")|"+l+u,"g");t.exports=function(t){for(var e=h.lastIndex=0;h.test(t);)++e;return e}},function(t,e,n){var r=n(79),i=n(125),a=n(88),o=n(25),s=n(63),c=n(5),u=n(39),l=n(37),h=n(11),f=n(48);t.exports=function(t,e,n){var d=c(t),p=d||u(t)||f(t);if(e=o(e,4),null==n){var g=t&&t.constructor;n=p?d?new g:[]:h(t)&&l(g)?i(s(t)):{}}return(p?r:a)(t,(function(t,r,i){return e(n,t,r,i)})),n}},function(t,e,n){var r=n(94),i=n(67),a=n(291),o=n(146),s=i((function(t){return a(r(t,1,o,!0))}));t.exports=s},function(t,e,n){var r=n(38),i=n(47),a=n(5),o=r?r.isConcatSpreadable:void 0;t.exports=function(t){return a(t)||i(t)||!!(o&&t&&t[o])}},function(t,e){t.exports=function(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}},function(t,e,n){var r=n(86),i=n(111),a=n(35),o=i?function(t,e){return i(t,"toString",{configurable:!0,enumerable:!1,value:r(e),writable:!0})}:a;t.exports=o},function(t,e){var n=Date.now;t.exports=function(t){var e=0,r=0;return function(){var i=n(),a=16-(i-r);if(r=i,a>0){if(++e>=800)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}},function(t,e,n){var r=n(131),i=n(292),a=n(296),o=n(132),s=n(297),c=n(90);t.exports=function(t,e,n){var u=-1,l=i,h=t.length,f=!0,d=[],p=d;if(n)f=!1,l=a;else if(h>=200){var g=e?null:s(t);if(g)return c(g);f=!1,l=o,p=new r}else p=e?[]:d;t:for(;++u<h;){var y=t[u],v=e?e(y):y;if(y=n||0!==y?y:0,f&&v==v){for(var m=p.length;m--;)if(p[m]===v)continue t;e&&p.push(v),d.push(y)}else l(p,v,n)||(p!==d&&p.push(v),d.push(y))}return d}},function(t,e,n){var r=n(293);t.exports=function(t,e){return!!(null==t?0:t.length)&&r(t,e,0)>-1}},function(t,e,n){var r=n(145),i=n(294),a=n(295);t.exports=function(t,e,n){return e==e?a(t,e,n):r(t,i,n)}},function(t,e){t.exports=function(t){return t!=t}},function(t,e){t.exports=function(t,e,n){for(var r=n-1,i=t.length;++r<i;)if(t[r]===e)return r;return-1}},function(t,e){t.exports=function(t,e,n){for(var r=-1,i=null==t?0:t.length;++r<i;)if(n(e,t[r]))return!0;return!1}},function(t,e,n){var r=n(121),i=n(298),a=n(90),o=r&&1/a(new r([,-0]))[1]==1/0?function(t){return new r(t)}:i;t.exports=o},function(t,e){t.exports=function(){}},function(t,e,n){var r=n(66);t.exports=function(t,e){return r(e,(function(e){return t[e]}))}},function(t,e){t.exports="2.1.8"},function(t,e,n){var r=n(10),i=n(76);function a(t){return r.map(t.nodes(),(function(e){var n=t.node(e),i=t.parent(e),a={v:e};return r.isUndefined(n)||(a.value=n),r.isUndefined(i)||(a.parent=i),a}))}function o(t){return r.map(t.edges(),(function(e){var n=t.edge(e),i={v:e.v,w:e.w};return r.isUndefined(e.name)||(i.name=e.name),r.isUndefined(n)||(i.value=n),i}))}t.exports={write:function(t){var e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:a(t),edges:o(t)};r.isUndefined(t.graph())||(e.value=r.clone(t.graph()));return e},read:function(t){var e=new i(t.options).setGraph(t.value);return r.each(t.nodes,(function(t){e.setNode(t.v,t.value),t.parent&&e.setParent(t.v,t.parent)})),r.each(t.edges,(function(t){e.setEdge({v:t.v,w:t.w,name:t.name},t.value)})),e}}},function(t,e,n){t.exports={components:n(303),dijkstra:n(148),dijkstraAll:n(304),findCycles:n(305),floydWarshall:n(306),isAcyclic:n(307),postorder:n(308),preorder:n(309),prim:n(310),tarjan:n(150),topsort:n(151)}},function(t,e,n){var r=n(10);t.exports=function(t){var e,n={},i=[];function a(i){r.has(n,i)||(n[i]=!0,e.push(i),r.each(t.successors(i),a),r.each(t.predecessors(i),a))}return r.each(t.nodes(),(function(t){e=[],a(t),e.length&&i.push(e)})),i}},function(t,e,n){var r=n(148),i=n(10);t.exports=function(t,e,n){return i.transform(t.nodes(),(function(i,a){i[a]=r(t,a,e,n)}),{})}},function(t,e,n){var r=n(10),i=n(150);t.exports=function(t){return r.filter(i(t),(function(e){return e.length>1||1===e.length&&t.hasEdge(e[0],e[0])}))}},function(t,e,n){var r=n(10);t.exports=function(t,e,n){return function(t,e,n){var r={},i=t.nodes();return i.forEach((function(t){r[t]={},r[t][t]={distance:0},i.forEach((function(e){t!==e&&(r[t][e]={distance:Number.POSITIVE_INFINITY})})),n(t).forEach((function(n){var i=n.v===t?n.w:n.v,a=e(n);r[t][i]={distance:a,predecessor:t}}))})),i.forEach((function(t){var e=r[t];i.forEach((function(n){var a=r[n];i.forEach((function(n){var r=a[t],i=e[n],o=a[n],s=r.distance+i.distance;s<o.distance&&(o.distance=s,o.predecessor=i.predecessor)}))}))})),r}(t,e||i,n||function(e){return t.outEdges(e)})};var i=r.constant(1)},function(t,e,n){var r=n(151);t.exports=function(t){try{r(t)}catch(t){if(t instanceof r.CycleException)return!1;throw t}return!0}},function(t,e,n){var r=n(152);t.exports=function(t,e){return r(t,e,"post")}},function(t,e,n){var r=n(152);t.exports=function(t,e){return r(t,e,"pre")}},function(t,e,n){var r=n(10),i=n(76),a=n(149);t.exports=function(t,e){var n,o=new i,s={},c=new a;function u(t){var r=t.v===n?t.w:t.v,i=c.priority(r);if(void 0!==i){var a=e(t);a<i&&(s[r]=n,c.decrease(r,a))}}if(0===t.nodeCount())return o;r.each(t.nodes(),(function(t){c.add(t,Number.POSITIVE_INFINITY),o.setNode(t)})),c.decrease(t.nodes()[0],0);var l=!1;for(;c.size()>0;){if(n=c.removeMin(),r.has(s,n))o.setEdge(n,s[n]);else{if(l)throw new Error("Input graph is not connected: "+t);l=!0}t.nodeEdges(n).forEach(u)}return o}},function(t,e,n){var r;try{r=n(3)}catch(t){}r||(r=window.graphlib),t.exports=r},function(t,e,n){"use strict";var r=n(4),i=n(345),a=n(348),o=n(349),s=n(8).normalizeRanks,c=n(351),u=n(8).removeEmptyRanks,l=n(352),h=n(353),f=n(354),d=n(355),p=n(364),g=n(8),y=n(17).Graph;t.exports=function(t,e){var n=e&&e.debugTiming?g.time:g.notime;n("layout",(function(){var e=n(" buildLayoutGraph",(function(){return function(t){var e=new y({multigraph:!0,compound:!0}),n=C(t.graph());return e.setGraph(r.merge({},m,T(n,v),r.pick(n,b))),r.forEach(t.nodes(),(function(n){var i=C(t.node(n));e.setNode(n,r.defaults(T(i,x),_)),e.setParent(n,t.parent(n))})),r.forEach(t.edges(),(function(n){var i=C(t.edge(n));e.setEdge(n,r.merge({},w,T(i,k),r.pick(i,E)))})),e}(t)}));n(" runLayout",(function(){!function(t,e){e(" makeSpaceForEdgeLabels",(function(){!function(t){var e=t.graph();e.ranksep/=2,r.forEach(t.edges(),(function(n){var r=t.edge(n);r.minlen*=2,"c"!==r.labelpos.toLowerCase()&&("TB"===e.rankdir||"BT"===e.rankdir?r.width+=r.labeloffset:r.height+=r.labeloffset)}))}(t)})),e(" removeSelfEdges",(function(){!function(t){r.forEach(t.edges(),(function(e){if(e.v===e.w){var n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e:e,label:t.edge(e)}),t.removeEdge(e)}}))}(t)})),e(" acyclic",(function(){i.run(t)})),e(" nestingGraph.run",(function(){l.run(t)})),e(" rank",(function(){o(g.asNonCompoundGraph(t))})),e(" injectEdgeLabelProxies",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.width&&n.height){var r=t.node(e.v),i={rank:(t.node(e.w).rank-r.rank)/2+r.rank,e:e};g.addDummyNode(t,"edge-proxy",i,"_ep")}}))}(t)})),e(" removeEmptyRanks",(function(){u(t)})),e(" nestingGraph.cleanup",(function(){l.cleanup(t)})),e(" normalizeRanks",(function(){s(t)})),e(" assignRankMinMax",(function(){!function(t){var e=0;r.forEach(t.nodes(),(function(n){var i=t.node(n);i.borderTop&&(i.minRank=t.node(i.borderTop).rank,i.maxRank=t.node(i.borderBottom).rank,e=r.max(e,i.maxRank))})),t.graph().maxRank=e}(t)})),e(" removeEdgeLabelProxies",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);"edge-proxy"===n.dummy&&(t.edge(n.e).labelRank=n.rank,t.removeNode(e))}))}(t)})),e(" normalize.run",(function(){a.run(t)})),e(" parentDummyChains",(function(){c(t)})),e(" addBorderSegments",(function(){h(t)})),e(" order",(function(){d(t)})),e(" insertSelfEdges",(function(){!function(t){var e=g.buildLayerMatrix(t);r.forEach(e,(function(e){var n=0;r.forEach(e,(function(e,i){var a=t.node(e);a.order=i+n,r.forEach(a.selfEdges,(function(e){g.addDummyNode(t,"selfedge",{width:e.label.width,height:e.label.height,rank:a.rank,order:i+ ++n,e:e.e,label:e.label},"_se")})),delete a.selfEdges}))}))}(t)})),e(" adjustCoordinateSystem",(function(){f.adjust(t)})),e(" position",(function(){p(t)})),e(" positionSelfEdges",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);if("selfedge"===n.dummy){var r=t.node(n.e.v),i=r.x+r.width/2,a=r.y,o=n.x-i,s=r.height/2;t.setEdge(n.e,n.label),t.removeNode(e),n.label.points=[{x:i+2*o/3,y:a-s},{x:i+5*o/6,y:a-s},{x:i+o,y:a},{x:i+5*o/6,y:a+s},{x:i+2*o/3,y:a+s}],n.label.x=n.x,n.label.y=n.y}}))}(t)})),e(" removeBorderNodes",(function(){!function(t){r.forEach(t.nodes(),(function(e){if(t.children(e).length){var n=t.node(e),i=t.node(n.borderTop),a=t.node(n.borderBottom),o=t.node(r.last(n.borderLeft)),s=t.node(r.last(n.borderRight));n.width=Math.abs(s.x-o.x),n.height=Math.abs(a.y-i.y),n.x=o.x+n.width/2,n.y=i.y+n.height/2}})),r.forEach(t.nodes(),(function(e){"border"===t.node(e).dummy&&t.removeNode(e)}))}(t)})),e(" normalize.undo",(function(){a.undo(t)})),e(" fixupEdgeLabelCoords",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(r.has(n,"x"))switch("l"!==n.labelpos&&"r"!==n.labelpos||(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset}}))}(t)})),e(" undoCoordinateSystem",(function(){f.undo(t)})),e(" translateGraph",(function(){!function(t){var e=Number.POSITIVE_INFINITY,n=0,i=Number.POSITIVE_INFINITY,a=0,o=t.graph(),s=o.marginx||0,c=o.marginy||0;function u(t){var r=t.x,o=t.y,s=t.width,c=t.height;e=Math.min(e,r-s/2),n=Math.max(n,r+s/2),i=Math.min(i,o-c/2),a=Math.max(a,o+c/2)}r.forEach(t.nodes(),(function(e){u(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.has(n,"x")&&u(n)})),e-=s,i-=c,r.forEach(t.nodes(),(function(n){var r=t.node(n);r.x-=e,r.y-=i})),r.forEach(t.edges(),(function(n){var a=t.edge(n);r.forEach(a.points,(function(t){t.x-=e,t.y-=i})),r.has(a,"x")&&(a.x-=e),r.has(a,"y")&&(a.y-=i)})),o.width=n-e+s,o.height=a-i+c}(t)})),e(" assignNodeIntersects",(function(){!function(t){r.forEach(t.edges(),(function(e){var n,r,i=t.edge(e),a=t.node(e.v),o=t.node(e.w);i.points?(n=i.points[0],r=i.points[i.points.length-1]):(i.points=[],n=o,r=a),i.points.unshift(g.intersectRect(a,n)),i.points.push(g.intersectRect(o,r))}))}(t)})),e(" reversePoints",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);n.reversed&&n.points.reverse()}))}(t)})),e(" acyclic.undo",(function(){i.undo(t)}))}(e,n)})),n(" updateInputGraph",(function(){!function(t,e){r.forEach(t.nodes(),(function(n){var r=t.node(n),i=e.node(n);r&&(r.x=i.x,r.y=i.y,e.children(n).length&&(r.width=i.width,r.height=i.height))})),r.forEach(t.edges(),(function(n){var i=t.edge(n),a=e.edge(n);i.points=a.points,r.has(a,"x")&&(i.x=a.x,i.y=a.y)})),t.graph().width=e.graph().width,t.graph().height=e.graph().height}(t,e)}))}))};var v=["nodesep","edgesep","ranksep","marginx","marginy"],m={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},b=["acyclicer","ranker","rankdir","align"],x=["width","height"],_={width:0,height:0},k=["minlen","weight","width","height","labeloffset"],w={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},E=["labelpos"];function T(t,e){return r.mapValues(r.pick(t,e),Number)}function C(t){var e={};return r.forEach(t,(function(t,n){e[n.toLowerCase()]=t})),e}},function(t,e,n){var r=n(108);t.exports=function(t){return r(t,5)}},function(t,e,n){var r=n(315)(n(316));t.exports=r},function(t,e,n){var r=n(25),i=n(24),a=n(30);t.exports=function(t){return function(e,n,o){var s=Object(e);if(!i(e)){var c=r(n,3);e=a(e),n=function(t){return c(s[t],t,s)}}var u=t(e,n,o);return u>-1?s[c?e[u]:u]:void 0}}},function(t,e,n){var r=n(145),i=n(25),a=n(317),o=Math.max;t.exports=function(t,e,n){var s=null==t?0:t.length;if(!s)return-1;var c=null==n?0:a(n);return c<0&&(c=o(s+c,0)),r(t,i(e,3),c)}},function(t,e,n){var r=n(155);t.exports=function(t){var e=r(t),n=e%1;return e==e?n?e-n:e:0}},function(t,e,n){var r=n(11),i=n(42),a=/^\s+|\s+$/g,o=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,c=/^0o[0-7]+$/i,u=parseInt;t.exports=function(t){if("number"==typeof t)return t;if(i(t))return NaN;if(r(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=r(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=t.replace(a,"");var n=s.test(t);return n||c.test(t)?u(t.slice(2),n?2:8):o.test(t)?NaN:+t}},function(t,e,n){var r=n(89),i=n(127),a=n(40);t.exports=function(t,e){return null==t?t:r(t,i(e),a)}},function(t,e){t.exports=function(t){var e=null==t?0:t.length;return e?t[e-1]:void 0}},function(t,e,n){var r=n(59),i=n(88),a=n(25);t.exports=function(t,e){var n={};return e=a(e,3),i(t,(function(t,i,a){r(n,i,e(t,i,a))})),n}},function(t,e,n){var r=n(95),i=n(323),a=n(35);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},function(t,e){t.exports=function(t,e){return t>e}},function(t,e,n){var r=n(325),i=n(328)((function(t,e,n){r(t,e,n)}));t.exports=i},function(t,e,n){var r=n(53),i=n(157),a=n(89),o=n(326),s=n(11),c=n(40),u=n(159);t.exports=function t(e,n,l,h,f){e!==n&&a(n,(function(a,c){if(f||(f=new r),s(a))o(e,n,c,l,t,h,f);else{var d=h?h(u(e,c),a,c+"",e,n,f):void 0;void 0===d&&(d=a),i(e,c,d)}}),c)}},function(t,e,n){var r=n(157),i=n(114),a=n(123),o=n(115),s=n(124),c=n(47),u=n(5),l=n(146),h=n(39),f=n(37),d=n(11),p=n(158),g=n(48),y=n(159),v=n(327);t.exports=function(t,e,n,m,b,x,_){var k=y(t,n),w=y(e,n),E=_.get(w);if(E)r(t,n,E);else{var T=x?x(k,w,n+"",t,e,_):void 0,C=void 0===T;if(C){var A=u(w),S=!A&&h(w),M=!A&&!S&&g(w);T=w,A||S||M?u(k)?T=k:l(k)?T=o(k):S?(C=!1,T=i(w,!0)):M?(C=!1,T=a(w,!0)):T=[]:p(w)||c(w)?(T=k,c(k)?T=v(k):d(k)&&!f(k)||(T=s(w))):C=!1}C&&(_.set(w,T),b(T,w,m,x,_),_.delete(w)),r(t,n,T)}}},function(t,e,n){var r=n(46),i=n(40);t.exports=function(t){return r(t,i(t))}},function(t,e,n){var r=n(67),i=n(68);t.exports=function(t){return r((function(e,n){var r=-1,a=n.length,o=a>1?n[a-1]:void 0,s=a>2?n[2]:void 0;for(o=t.length>3&&"function"==typeof o?(a--,o):void 0,s&&i(n[0],n[1],s)&&(o=a<3?void 0:o,a=1),e=Object(e);++r<a;){var c=n[r];c&&t(e,c,r,o)}return e}))}},function(t,e,n){var r=n(95),i=n(160),a=n(35);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},function(t,e,n){var r=n(95),i=n(25),a=n(160);t.exports=function(t,e){return t&&t.length?r(t,i(e,2),a):void 0}},function(t,e,n){var r=n(16);t.exports=function(){return r.Date.now()}},function(t,e,n){var r=n(333),i=n(136);t.exports=function(t,e){return r(t,e,(function(e,n){return i(t,n)}))}},function(t,e,n){var r=n(91),i=n(334),a=n(65);t.exports=function(t,e,n){for(var o=-1,s=e.length,c={};++o<s;){var u=e[o],l=r(t,u);n(l,u)&&i(c,a(u,t),l)}return c}},function(t,e,n){var r=n(58),i=n(65),a=n(60),o=n(11),s=n(49);t.exports=function(t,e,n,c){if(!o(t))return t;for(var u=-1,l=(e=i(e,t)).length,h=l-1,f=t;null!=f&&++u<l;){var d=s(e[u]),p=n;if(u!=h){var g=f[d];void 0===(p=c?c(g,d,f):void 0)&&(p=o(g)?g:a(e[u+1])?[]:{})}r(f,d,p),f=f[d]}return t}},function(t,e,n){var r=n(156),i=n(143),a=n(144);t.exports=function(t){return a(i(t,void 0,r),t+"")}},function(t,e,n){var r=n(337),i=n(68),a=n(155);t.exports=function(t){return function(e,n,o){return o&&"number"!=typeof o&&i(e,n,o)&&(n=o=void 0),e=a(e),void 0===n?(n=e,e=0):n=a(n),o=void 0===o?e<n?1:-1:a(o),r(e,n,o,t)}}},function(t,e){var n=Math.ceil,r=Math.max;t.exports=function(t,e,i,a){for(var o=-1,s=r(n((e-t)/(i||1)),0),c=Array(s);s--;)c[a?s:++o]=t,t+=i;return c}},function(t,e,n){var r=n(94),i=n(339),a=n(67),o=n(68),s=a((function(t,e){if(null==t)return[];var n=e.length;return n>1&&o(t,e[0],e[1])?e=[]:n>2&&o(e[0],e[1],e[2])&&(e=[e[0]]),i(t,r(e,1),[])}));t.exports=s},function(t,e,n){var r=n(66),i=n(25),a=n(141),o=n(340),s=n(61),c=n(341),u=n(35);t.exports=function(t,e,n){var l=-1;e=r(e.length?e:[u],s(i));var h=a(t,(function(t,n,i){return{criteria:r(e,(function(e){return e(t)})),index:++l,value:t}}));return o(h,(function(t,e){return c(t,e,n)}))}},function(t,e){t.exports=function(t,e){var n=t.length;for(t.sort(e);n--;)t[n]=t[n].value;return t}},function(t,e,n){var r=n(342);t.exports=function(t,e,n){for(var i=-1,a=t.criteria,o=e.criteria,s=a.length,c=n.length;++i<s;){var u=r(a[i],o[i]);if(u)return i>=c?u:u*("desc"==n[i]?-1:1)}return t.index-e.index}},function(t,e,n){var r=n(42);t.exports=function(t,e){if(t!==e){var n=void 0!==t,i=null===t,a=t==t,o=r(t),s=void 0!==e,c=null===e,u=e==e,l=r(e);if(!c&&!l&&!o&&t>e||o&&s&&u&&!c&&!l||i&&s&&u||!n&&u||!a)return 1;if(!i&&!o&&!l&&t<e||l&&n&&a&&!i&&!o||c&&n&&a||!s&&a||!u)return-1}return 0}},function(t,e,n){var r=n(58),i=n(344);t.exports=function(t,e){return i(t||[],e||[],r)}},function(t,e){t.exports=function(t,e,n){for(var r=-1,i=t.length,a=e.length,o={};++r<i;){var s=r<a?e[r]:void 0;n(o,t[r],s)}return o}},function(t,e,n){"use strict";var r=n(4),i=n(346);t.exports={run:function(t){var e="greedy"===t.graph().acyclicer?i(t,function(t){return function(e){return t.edge(e).weight}}(t)):function(t){var e=[],n={},i={};function a(o){r.has(i,o)||(i[o]=!0,n[o]=!0,r.forEach(t.outEdges(o),(function(t){r.has(n,t.w)?e.push(t):a(t.w)})),delete n[o])}return r.forEach(t.nodes(),a),e}(t);r.forEach(e,(function(e){var n=t.edge(e);t.removeEdge(e),n.forwardName=e.name,n.reversed=!0,t.setEdge(e.w,e.v,n,r.uniqueId("rev"))}))},undo:function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.reversed){t.removeEdge(e);var r=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,r)}}))}}},function(t,e,n){var r=n(4),i=n(17).Graph,a=n(347);t.exports=function(t,e){if(t.nodeCount()<=1)return[];var n=function(t,e){var n=new i,o=0,s=0;r.forEach(t.nodes(),(function(t){n.setNode(t,{v:t,in:0,out:0})})),r.forEach(t.edges(),(function(t){var r=n.edge(t.v,t.w)||0,i=e(t),a=r+i;n.setEdge(t.v,t.w,a),s=Math.max(s,n.node(t.v).out+=i),o=Math.max(o,n.node(t.w).in+=i)}));var u=r.range(s+o+3).map((function(){return new a})),l=o+1;return r.forEach(n.nodes(),(function(t){c(u,l,n.node(t))})),{graph:n,buckets:u,zeroIdx:l}}(t,e||o),u=function(t,e,n){var r,i=[],a=e[e.length-1],o=e[0];for(;t.nodeCount();){for(;r=o.dequeue();)s(t,e,n,r);for(;r=a.dequeue();)s(t,e,n,r);if(t.nodeCount())for(var c=e.length-2;c>0;--c)if(r=e[c].dequeue()){i=i.concat(s(t,e,n,r,!0));break}}return i}(n.graph,n.buckets,n.zeroIdx);return r.flatten(r.map(u,(function(e){return t.outEdges(e.v,e.w)})),!0)};var o=r.constant(1);function s(t,e,n,i,a){var o=a?[]:void 0;return r.forEach(t.inEdges(i.v),(function(r){var i=t.edge(r),s=t.node(r.v);a&&o.push({v:r.v,w:r.w}),s.out-=i,c(e,n,s)})),r.forEach(t.outEdges(i.v),(function(r){var i=t.edge(r),a=r.w,o=t.node(a);o.in-=i,c(e,n,o)})),t.removeNode(i.v),o}function c(t,e,n){n.out?n.in?t[n.out-n.in+e].enqueue(n):t[t.length-1].enqueue(n):t[0].enqueue(n)}},function(t,e){function n(){var t={};t._next=t._prev=t,this._sentinel=t}function r(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function i(t,e){if("_next"!==t&&"_prev"!==t)return e}t.exports=n,n.prototype.dequeue=function(){var t=this._sentinel,e=t._prev;if(e!==t)return r(e),e},n.prototype.enqueue=function(t){var e=this._sentinel;t._prev&&t._next&&r(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e},n.prototype.toString=function(){for(var t=[],e=this._sentinel,n=e._prev;n!==e;)t.push(JSON.stringify(n,i)),n=n._prev;return"["+t.join(", ")+"]"}},function(t,e,n){"use strict";var r=n(4),i=n(8);t.exports={run:function(t){t.graph().dummyChains=[],r.forEach(t.edges(),(function(e){!function(t,e){var n,r,a,o=e.v,s=t.node(o).rank,c=e.w,u=t.node(c).rank,l=e.name,h=t.edge(e),f=h.labelRank;if(u===s+1)return;for(t.removeEdge(e),a=0,++s;s<u;++a,++s)h.points=[],r={width:0,height:0,edgeLabel:h,edgeObj:e,rank:s},n=i.addDummyNode(t,"edge",r,"_d"),s===f&&(r.width=h.width,r.height=h.height,r.dummy="edge-label",r.labelpos=h.labelpos),t.setEdge(o,n,{weight:h.weight},l),0===a&&t.graph().dummyChains.push(n),o=n;t.setEdge(o,c,{weight:h.weight},l)}(t,e)}))},undo:function(t){r.forEach(t.graph().dummyChains,(function(e){var n,r=t.node(e),i=r.edgeLabel;for(t.setEdge(r.edgeObj,i);r.dummy;)n=t.successors(e)[0],t.removeNode(e),i.points.push({x:r.x,y:r.y}),"edge-label"===r.dummy&&(i.x=r.x,i.y=r.y,i.width=r.width,i.height=r.height),e=n,r=t.node(e)}))}}},function(t,e,n){"use strict";var r=n(69).longestPath,i=n(164),a=n(350);t.exports=function(t){switch(t.graph().ranker){case"network-simplex":s(t);break;case"tight-tree":!function(t){r(t),i(t)}(t);break;case"longest-path":o(t);break;default:s(t)}};var o=r;function s(t){a(t)}},function(t,e,n){"use strict";var r=n(4),i=n(164),a=n(69).slack,o=n(69).longestPath,s=n(17).alg.preorder,c=n(17).alg.postorder,u=n(8).simplify;function l(t){t=u(t),o(t);var e,n=i(t);for(d(n),h(n,t);e=g(n);)v(n,t,e,y(n,t,e))}function h(t,e){var n=c(t,t.nodes());n=n.slice(0,n.length-1),r.forEach(n,(function(n){!function(t,e,n){var r=t.node(n).parent;t.edge(n,r).cutvalue=f(t,e,n)}(t,e,n)}))}function f(t,e,n){var i=t.node(n).parent,a=!0,o=e.edge(n,i),s=0;return o||(a=!1,o=e.edge(i,n)),s=o.weight,r.forEach(e.nodeEdges(n),(function(r){var o,c,u=r.v===n,l=u?r.w:r.v;if(l!==i){var h=u===a,f=e.edge(r).weight;if(s+=h?f:-f,o=n,c=l,t.hasEdge(o,c)){var d=t.edge(n,l).cutvalue;s+=h?-d:d}}})),s}function d(t,e){arguments.length<2&&(e=t.nodes()[0]),p(t,{},1,e)}function p(t,e,n,i,a){var o=n,s=t.node(i);return e[i]=!0,r.forEach(t.neighbors(i),(function(a){r.has(e,a)||(n=p(t,e,n,a,i))})),s.low=o,s.lim=n++,a?s.parent=a:delete s.parent,n}function g(t){return r.find(t.edges(),(function(e){return t.edge(e).cutvalue<0}))}function y(t,e,n){var i=n.v,o=n.w;e.hasEdge(i,o)||(i=n.w,o=n.v);var s=t.node(i),c=t.node(o),u=s,l=!1;s.lim>c.lim&&(u=c,l=!0);var h=r.filter(e.edges(),(function(e){return l===m(t,t.node(e.v),u)&&l!==m(t,t.node(e.w),u)}));return r.minBy(h,(function(t){return a(e,t)}))}function v(t,e,n,i){var a=n.v,o=n.w;t.removeEdge(a,o),t.setEdge(i.v,i.w,{}),d(t),h(t,e),function(t,e){var n=r.find(t.nodes(),(function(t){return!e.node(t).parent})),i=s(t,n);i=i.slice(1),r.forEach(i,(function(n){var r=t.node(n).parent,i=e.edge(n,r),a=!1;i||(i=e.edge(r,n),a=!0),e.node(n).rank=e.node(r).rank+(a?i.minlen:-i.minlen)}))}(t,e)}function m(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}t.exports=l,l.initLowLimValues=d,l.initCutValues=h,l.calcCutValue=f,l.leaveEdge=g,l.enterEdge=y,l.exchangeEdges=v},function(t,e,n){var r=n(4);t.exports=function(t){var e=function(t){var e={},n=0;function i(a){var o=n;r.forEach(t.children(a),i),e[a]={low:o,lim:n++}}return r.forEach(t.children(),i),e}(t);r.forEach(t.graph().dummyChains,(function(n){for(var r=t.node(n),i=r.edgeObj,a=function(t,e,n,r){var i,a,o=[],s=[],c=Math.min(e[n].low,e[r].low),u=Math.max(e[n].lim,e[r].lim);i=n;do{i=t.parent(i),o.push(i)}while(i&&(e[i].low>c||u>e[i].lim));a=i,i=r;for(;(i=t.parent(i))!==a;)s.push(i);return{path:o.concat(s.reverse()),lca:a}}(t,e,i.v,i.w),o=a.path,s=a.lca,c=0,u=o[c],l=!0;n!==i.w;){if(r=t.node(n),l){for(;(u=o[c])!==s&&t.node(u).maxRank<r.rank;)c++;u===s&&(l=!1)}if(!l){for(;c<o.length-1&&t.node(u=o[c+1]).minRank<=r.rank;)c++;u=o[c]}t.setParent(n,u),n=t.successors(n)[0]}}))}},function(t,e,n){var r=n(4),i=n(8);t.exports={run:function(t){var e=i.addDummyNode(t,"root",{},"_root"),n=function(t){var e={};return r.forEach(t.children(),(function(n){!function n(i,a){var o=t.children(i);o&&o.length&&r.forEach(o,(function(t){n(t,a+1)}));e[i]=a}(n,1)})),e}(t),a=r.max(r.values(n))-1,o=2*a+1;t.graph().nestingRoot=e,r.forEach(t.edges(),(function(e){t.edge(e).minlen*=o}));var s=function(t){return r.reduce(t.edges(),(function(e,n){return e+t.edge(n).weight}),0)}(t)+1;r.forEach(t.children(),(function(c){!function t(e,n,a,o,s,c,u){var l=e.children(u);if(!l.length)return void(u!==n&&e.setEdge(n,u,{weight:0,minlen:a}));var h=i.addBorderNode(e,"_bt"),f=i.addBorderNode(e,"_bb"),d=e.node(u);e.setParent(h,u),d.borderTop=h,e.setParent(f,u),d.borderBottom=f,r.forEach(l,(function(r){t(e,n,a,o,s,c,r);var i=e.node(r),l=i.borderTop?i.borderTop:r,d=i.borderBottom?i.borderBottom:r,p=i.borderTop?o:2*o,g=l!==d?1:s-c[u]+1;e.setEdge(h,l,{weight:p,minlen:g,nestingEdge:!0}),e.setEdge(d,f,{weight:p,minlen:g,nestingEdge:!0})})),e.parent(u)||e.setEdge(n,h,{weight:0,minlen:s+c[u]})}(t,e,o,s,a,n,c)})),t.graph().nodeRankFactor=o},cleanup:function(t){var e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,r.forEach(t.edges(),(function(e){t.edge(e).nestingEdge&&t.removeEdge(e)}))}}},function(t,e,n){var r=n(4),i=n(8);function a(t,e,n,r,a,o){var s={width:0,height:0,rank:o,borderType:e},c=a[e][o-1],u=i.addDummyNode(t,"border",s,n);a[e][o]=u,t.setParent(u,r),c&&t.setEdge(c,u,{weight:1})}t.exports=function(t){r.forEach(t.children(),(function e(n){var i=t.children(n),o=t.node(n);if(i.length&&r.forEach(i,e),r.has(o,"minRank")){o.borderLeft=[],o.borderRight=[];for(var s=o.minRank,c=o.maxRank+1;s<c;++s)a(t,"borderLeft","_bl",n,o,s),a(t,"borderRight","_br",n,o,s)}}))}},function(t,e,n){"use strict";var r=n(4);function i(t){r.forEach(t.nodes(),(function(e){a(t.node(e))})),r.forEach(t.edges(),(function(e){a(t.edge(e))}))}function a(t){var e=t.width;t.width=t.height,t.height=e}function o(t){t.y=-t.y}function s(t){var e=t.x;t.x=t.y,t.y=e}t.exports={adjust:function(t){var e=t.graph().rankdir.toLowerCase();"lr"!==e&&"rl"!==e||i(t)},undo:function(t){var e=t.graph().rankdir.toLowerCase();"bt"!==e&&"rl"!==e||function(t){r.forEach(t.nodes(),(function(e){o(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,o),r.has(n,"y")&&o(n)}))}(t);"lr"!==e&&"rl"!==e||(!function(t){r.forEach(t.nodes(),(function(e){s(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,s),r.has(n,"x")&&s(n)}))}(t),i(t))}}},function(t,e,n){"use strict";var r=n(4),i=n(356),a=n(357),o=n(358),s=n(362),c=n(363),u=n(17).Graph,l=n(8);function h(t,e,n){return r.map(e,(function(e){return s(t,e,n)}))}function f(t,e){var n=new u;r.forEach(t,(function(t){var i=t.graph().root,a=o(t,i,n,e);r.forEach(a.vs,(function(e,n){t.node(e).order=n})),c(t,n,a.vs)}))}function d(t,e){r.forEach(e,(function(e){r.forEach(e,(function(e,n){t.node(e).order=n}))}))}t.exports=function(t){var e=l.maxRank(t),n=h(t,r.range(1,e+1),"inEdges"),o=h(t,r.range(e-1,-1,-1),"outEdges"),s=i(t);d(t,s);for(var c,u=Number.POSITIVE_INFINITY,p=0,g=0;g<4;++p,++g){f(p%2?n:o,p%4>=2),s=l.buildLayerMatrix(t);var y=a(t,s);y<u&&(g=0,c=r.cloneDeep(s),u=y)}d(t,c)}},function(t,e,n){"use strict";var r=n(4);t.exports=function(t){var e={},n=r.filter(t.nodes(),(function(e){return!t.children(e).length})),i=r.max(r.map(n,(function(e){return t.node(e).rank}))),a=r.map(r.range(i+1),(function(){return[]}));var o=r.sortBy(n,(function(e){return t.node(e).rank}));return r.forEach(o,(function n(i){if(r.has(e,i))return;e[i]=!0;var o=t.node(i);a[o.rank].push(i),r.forEach(t.successors(i),n)})),a}},function(t,e,n){"use strict";var r=n(4);function i(t,e,n){for(var i=r.zipObject(n,r.map(n,(function(t,e){return e}))),a=r.flatten(r.map(e,(function(e){return r.sortBy(r.map(t.outEdges(e),(function(e){return{pos:i[e.w],weight:t.edge(e).weight}})),"pos")})),!0),o=1;o<n.length;)o<<=1;var s=2*o-1;o-=1;var c=r.map(new Array(s),(function(){return 0})),u=0;return r.forEach(a.forEach((function(t){var e=t.pos+o;c[e]+=t.weight;for(var n=0;e>0;)e%2&&(n+=c[e+1]),c[e=e-1>>1]+=t.weight;u+=t.weight*n}))),u}t.exports=function(t,e){for(var n=0,r=1;r<e.length;++r)n+=i(t,e[r-1],e[r]);return n}},function(t,e,n){var r=n(4),i=n(359),a=n(360),o=n(361);t.exports=function t(e,n,s,c){var u=e.children(n),l=e.node(n),h=l?l.borderLeft:void 0,f=l?l.borderRight:void 0,d={};h&&(u=r.filter(u,(function(t){return t!==h&&t!==f})));var p=i(e,u);r.forEach(p,(function(n){if(e.children(n.v).length){var i=t(e,n.v,s,c);d[n.v]=i,r.has(i,"barycenter")&&(a=n,o=i,r.isUndefined(a.barycenter)?(a.barycenter=o.barycenter,a.weight=o.weight):(a.barycenter=(a.barycenter*a.weight+o.barycenter*o.weight)/(a.weight+o.weight),a.weight+=o.weight))}var a,o}));var g=a(p,s);!function(t,e){r.forEach(t,(function(t){t.vs=r.flatten(t.vs.map((function(t){return e[t]?e[t].vs:t})),!0)}))}(g,d);var y=o(g,c);if(h&&(y.vs=r.flatten([h,y.vs,f],!0),e.predecessors(h).length)){var v=e.node(e.predecessors(h)[0]),m=e.node(e.predecessors(f)[0]);r.has(y,"barycenter")||(y.barycenter=0,y.weight=0),y.barycenter=(y.barycenter*y.weight+v.order+m.order)/(y.weight+2),y.weight+=2}return y}},function(t,e,n){var r=n(4);t.exports=function(t,e){return r.map(e,(function(e){var n=t.inEdges(e);if(n.length){var i=r.reduce(n,(function(e,n){var r=t.edge(n),i=t.node(n.v);return{sum:e.sum+r.weight*i.order,weight:e.weight+r.weight}}),{sum:0,weight:0});return{v:e,barycenter:i.sum/i.weight,weight:i.weight}}return{v:e}}))}},function(t,e,n){"use strict";var r=n(4);t.exports=function(t,e){var n={};return r.forEach(t,(function(t,e){var i=n[t.v]={indegree:0,in:[],out:[],vs:[t.v],i:e};r.isUndefined(t.barycenter)||(i.barycenter=t.barycenter,i.weight=t.weight)})),r.forEach(e.edges(),(function(t){var e=n[t.v],i=n[t.w];r.isUndefined(e)||r.isUndefined(i)||(i.indegree++,e.out.push(n[t.w]))})),function(t){var e=[];function n(t){return function(e){e.merged||(r.isUndefined(e.barycenter)||r.isUndefined(t.barycenter)||e.barycenter>=t.barycenter)&&function(t,e){var n=0,r=0;t.weight&&(n+=t.barycenter*t.weight,r+=t.weight);e.weight&&(n+=e.barycenter*e.weight,r+=e.weight);t.vs=e.vs.concat(t.vs),t.barycenter=n/r,t.weight=r,t.i=Math.min(e.i,t.i),e.merged=!0}(t,e)}}function i(e){return function(n){n.in.push(e),0==--n.indegree&&t.push(n)}}for(;t.length;){var a=t.pop();e.push(a),r.forEach(a.in.reverse(),n(a)),r.forEach(a.out,i(a))}return r.map(r.filter(e,(function(t){return!t.merged})),(function(t){return r.pick(t,["vs","i","barycenter","weight"])}))}(r.filter(n,(function(t){return!t.indegree})))}},function(t,e,n){var r=n(4),i=n(8);function a(t,e,n){for(var i;e.length&&(i=r.last(e)).i<=n;)e.pop(),t.push(i.vs),n++;return n}t.exports=function(t,e){var n=i.partition(t,(function(t){return r.has(t,"barycenter")})),o=n.lhs,s=r.sortBy(n.rhs,(function(t){return-t.i})),c=[],u=0,l=0,h=0;o.sort((f=!!e,function(t,e){return t.barycenter<e.barycenter?-1:t.barycenter>e.barycenter?1:f?e.i-t.i:t.i-e.i})),h=a(c,s,h),r.forEach(o,(function(t){h+=t.vs.length,c.push(t.vs),u+=t.barycenter*t.weight,l+=t.weight,h=a(c,s,h)}));var f;var d={vs:r.flatten(c,!0)};l&&(d.barycenter=u/l,d.weight=l);return d}},function(t,e,n){var r=n(4),i=n(17).Graph;t.exports=function(t,e,n){var a=function(t){var e;for(;t.hasNode(e=r.uniqueId("_root")););return e}(t),o=new i({compound:!0}).setGraph({root:a}).setDefaultNodeLabel((function(e){return t.node(e)}));return r.forEach(t.nodes(),(function(i){var s=t.node(i),c=t.parent(i);(s.rank===e||s.minRank<=e&&e<=s.maxRank)&&(o.setNode(i),o.setParent(i,c||a),r.forEach(t[n](i),(function(e){var n=e.v===i?e.w:e.v,a=o.edge(n,i),s=r.isUndefined(a)?0:a.weight;o.setEdge(n,i,{weight:t.edge(e).weight+s})})),r.has(s,"minRank")&&o.setNode(i,{borderLeft:s.borderLeft[e],borderRight:s.borderRight[e]}))})),o}},function(t,e,n){var r=n(4);t.exports=function(t,e,n){var i,a={};r.forEach(n,(function(n){for(var r,o,s=t.parent(n);s;){if((r=t.parent(s))?(o=a[r],a[r]=s):(o=i,i=s),o&&o!==s)return void e.setEdge(o,s);s=r}}))}},function(t,e,n){"use strict";var r=n(4),i=n(8),a=n(365).positionX;t.exports=function(t){(function(t){var e=i.buildLayerMatrix(t),n=t.graph().ranksep,a=0;r.forEach(e,(function(e){var i=r.max(r.map(e,(function(e){return t.node(e).height})));r.forEach(e,(function(e){t.node(e).y=a+i/2})),a+=i+n}))})(t=i.asNonCompoundGraph(t)),r.forEach(a(t),(function(e,n){t.node(n).x=e}))}},function(t,e,n){"use strict";var r=n(4),i=n(17).Graph,a=n(8);function o(t,e){var n={};return r.reduce(e,(function(e,i){var a=0,o=0,s=e.length,u=r.last(i);return r.forEach(i,(function(e,l){var h=function(t,e){if(t.node(e).dummy)return r.find(t.predecessors(e),(function(e){return t.node(e).dummy}))}(t,e),f=h?t.node(h).order:s;(h||e===u)&&(r.forEach(i.slice(o,l+1),(function(e){r.forEach(t.predecessors(e),(function(r){var i=t.node(r),o=i.order;!(o<a||f<o)||i.dummy&&t.node(e).dummy||c(n,r,e)}))})),o=l+1,a=f)})),i})),n}function s(t,e){var n={};function i(e,i,a,o,s){var u;r.forEach(r.range(i,a),(function(i){u=e[i],t.node(u).dummy&&r.forEach(t.predecessors(u),(function(e){var r=t.node(e);r.dummy&&(r.order<o||r.order>s)&&c(n,e,u)}))}))}return r.reduce(e,(function(e,n){var a,o=-1,s=0;return r.forEach(n,(function(r,c){if("border"===t.node(r).dummy){var u=t.predecessors(r);u.length&&(a=t.node(u[0]).order,i(n,s,c,o,a),s=c,o=a)}i(n,s,n.length,a,e.length)})),n})),n}function c(t,e,n){if(e>n){var r=e;e=n,n=r}var i=t[e];i||(t[e]=i={}),i[n]=!0}function u(t,e,n){if(e>n){var i=e;e=n,n=i}return r.has(t[e],n)}function l(t,e,n,i){var a={},o={},s={};return r.forEach(e,(function(t){r.forEach(t,(function(t,e){a[t]=t,o[t]=t,s[t]=e}))})),r.forEach(e,(function(t){var e=-1;r.forEach(t,(function(t){var c=i(t);if(c.length)for(var l=((c=r.sortBy(c,(function(t){return s[t]}))).length-1)/2,h=Math.floor(l),f=Math.ceil(l);h<=f;++h){var d=c[h];o[t]===t&&e<s[d]&&!u(n,t,d)&&(o[d]=t,o[t]=a[t]=a[d],e=s[d])}}))})),{root:a,align:o}}function h(t,e,n,a,o){var s={},c=function(t,e,n,a){var o=new i,s=t.graph(),c=function(t,e,n){return function(i,a,o){var s,c=i.node(a),u=i.node(o),l=0;if(l+=c.width/2,r.has(c,"labelpos"))switch(c.labelpos.toLowerCase()){case"l":s=-c.width/2;break;case"r":s=c.width/2}if(s&&(l+=n?s:-s),s=0,l+=(c.dummy?e:t)/2,l+=(u.dummy?e:t)/2,l+=u.width/2,r.has(u,"labelpos"))switch(u.labelpos.toLowerCase()){case"l":s=u.width/2;break;case"r":s=-u.width/2}return s&&(l+=n?s:-s),s=0,l}}(s.nodesep,s.edgesep,a);return r.forEach(e,(function(e){var i;r.forEach(e,(function(e){var r=n[e];if(o.setNode(r),i){var a=n[i],s=o.edge(a,r);o.setEdge(a,r,Math.max(c(t,e,i),s||0))}i=e}))})),o}(t,e,n,o),u=o?"borderLeft":"borderRight";function l(t,e){for(var n=c.nodes(),r=n.pop(),i={};r;)i[r]?t(r):(i[r]=!0,n.push(r),n=n.concat(e(r))),r=n.pop()}return l((function(t){s[t]=c.inEdges(t).reduce((function(t,e){return Math.max(t,s[e.v]+c.edge(e))}),0)}),c.predecessors.bind(c)),l((function(e){var n=c.outEdges(e).reduce((function(t,e){return Math.min(t,s[e.w]-c.edge(e))}),Number.POSITIVE_INFINITY),r=t.node(e);n!==Number.POSITIVE_INFINITY&&r.borderType!==u&&(s[e]=Math.max(s[e],n))}),c.successors.bind(c)),r.forEach(a,(function(t){s[t]=s[n[t]]})),s}function f(t,e){return r.minBy(r.values(e),(function(e){var n=Number.NEGATIVE_INFINITY,i=Number.POSITIVE_INFINITY;return r.forIn(e,(function(e,r){var a=function(t,e){return t.node(e).width}(t,r)/2;n=Math.max(e+a,n),i=Math.min(e-a,i)})),n-i}))}function d(t,e){var n=r.values(e),i=r.min(n),a=r.max(n);r.forEach(["u","d"],(function(n){r.forEach(["l","r"],(function(o){var s,c=n+o,u=t[c];if(u!==e){var l=r.values(u);(s="l"===o?i-r.min(l):a-r.max(l))&&(t[c]=r.mapValues(u,(function(t){return t+s})))}}))}))}function p(t,e){return r.mapValues(t.ul,(function(n,i){if(e)return t[e.toLowerCase()][i];var a=r.sortBy(r.map(t,i));return(a[1]+a[2])/2}))}t.exports={positionX:function(t){var e,n=a.buildLayerMatrix(t),i=r.merge(o(t,n),s(t,n)),c={};r.forEach(["u","d"],(function(a){e="u"===a?n:r.values(n).reverse(),r.forEach(["l","r"],(function(n){"r"===n&&(e=r.map(e,(function(t){return r.values(t).reverse()})));var o=("u"===a?t.predecessors:t.successors).bind(t),s=l(t,e,i,o),u=h(t,e,s.root,s.align,"r"===n);"r"===n&&(u=r.mapValues(u,(function(t){return-t}))),c[a+n]=u}))}));var u=f(t,c);return d(c,u),p(c,t.graph().align)},findType1Conflicts:o,findType2Conflicts:s,addConflict:c,hasConflict:u,verticalAlignment:l,horizontalCompaction:h,alignCoordinates:d,findSmallestWidthAlignment:f,balance:p}},function(t,e,n){var r=n(4),i=n(8),a=n(17).Graph;t.exports={debugOrdering:function(t){var e=i.buildLayerMatrix(t),n=new a({compound:!0,multigraph:!0}).setGraph({});return r.forEach(t.nodes(),(function(e){n.setNode(e,{label:e}),n.setParent(e,"layer"+t.node(e).rank)})),r.forEach(t.edges(),(function(t){n.setEdge(t.v,t.w,{},t.name)})),r.forEach(e,(function(t,e){var i="layer"+e;n.setNode(i,{rank:"same"}),r.reduce(t,(function(t,e){return n.setEdge(t,e,{style:"invis"}),e}))})),n}}},function(t,e){t.exports="0.8.5"},function(t,e,n){t.exports={node:n(165),circle:n(166),ellipse:n(96),polygon:n(167),rect:n(168)}},function(t,e){function n(t,e){return t*e>0}t.exports=function(t,e,r,i){var a,o,s,c,u,l,h,f,d,p,g,y,v;if(a=e.y-t.y,s=t.x-e.x,u=e.x*t.y-t.x*e.y,d=a*r.x+s*r.y+u,p=a*i.x+s*i.y+u,0!==d&&0!==p&&n(d,p))return;if(o=i.y-r.y,c=r.x-i.x,l=i.x*r.y-r.x*i.y,h=o*t.x+c*t.y+l,f=o*e.x+c*e.y+l,0!==h&&0!==f&&n(h,f))return;if(0===(g=a*c-o*s))return;return y=Math.abs(g/2),{x:(v=s*l-c*u)<0?(v-y)/g:(v+y)/g,y:(v=o*u-a*l)<0?(v-y)/g:(v+y)/g}}},function(t,e,n){var r=n(43),i=n(31),a=n(153).layout;t.exports=function(){var t=n(371),e=n(374),i=n(375),u=n(376),l=n(377),h=n(378),f=n(379),d=n(380),p=n(381),g=function(n,g){!function(t){t.nodes().forEach((function(e){var n=t.node(e);r.has(n,"label")||t.children(e).length||(n.label=e),r.has(n,"paddingX")&&r.defaults(n,{paddingLeft:n.paddingX,paddingRight:n.paddingX}),r.has(n,"paddingY")&&r.defaults(n,{paddingTop:n.paddingY,paddingBottom:n.paddingY}),r.has(n,"padding")&&r.defaults(n,{paddingLeft:n.padding,paddingRight:n.padding,paddingTop:n.padding,paddingBottom:n.padding}),r.defaults(n,o),r.each(["paddingLeft","paddingRight","paddingTop","paddingBottom"],(function(t){n[t]=Number(n[t])})),r.has(n,"width")&&(n._prevWidth=n.width),r.has(n,"height")&&(n._prevHeight=n.height)})),t.edges().forEach((function(e){var n=t.edge(e);r.has(n,"label")||(n.label=""),r.defaults(n,s)}))}(g);var y=c(n,"output"),v=c(y,"clusters"),m=c(y,"edgePaths"),b=i(c(y,"edgeLabels"),g),x=t(c(y,"nodes"),g,d);a(g),l(x,g),h(b,g),u(m,g,p);var _=e(v,g);f(_,g),function(t){r.each(t.nodes(),(function(e){var n=t.node(e);r.has(n,"_prevWidth")?n.width=n._prevWidth:delete n.width,r.has(n,"_prevHeight")?n.height=n._prevHeight:delete n.height,delete n._prevWidth,delete n._prevHeight}))}(g)};return g.createNodes=function(e){return arguments.length?(t=e,g):t},g.createClusters=function(t){return arguments.length?(e=t,g):e},g.createEdgeLabels=function(t){return arguments.length?(i=t,g):i},g.createEdgePaths=function(t){return arguments.length?(u=t,g):u},g.shapes=function(t){return arguments.length?(d=t,g):d},g.arrows=function(t){return arguments.length?(p=t,g):p},g};var o={paddingLeft:10,paddingRight:10,paddingTop:10,paddingBottom:10,rx:0,ry:0,shape:"rect"},s={arrowhead:"normal",curve:i.curveLinear};function c(t,e){var n=t.select("g."+e);return n.empty()&&(n=t.append("g").attr("class",e)),n}},function(t,e,n){"use strict";var r=n(43),i=n(97),a=n(12),o=n(31);t.exports=function(t,e,n){var s,c=e.nodes().filter((function(t){return!a.isSubgraph(e,t)})),u=t.selectAll("g.node").data(c,(function(t){return t})).classed("update",!0);u.exit().remove(),u.enter().append("g").attr("class","node").style("opacity",0),(u=t.selectAll("g.node")).each((function(t){var s=e.node(t),c=o.select(this);a.applyClass(c,s.class,(c.classed("update")?"update ":"")+"node"),c.select("g.label").remove();var u=c.append("g").attr("class","label"),l=i(u,s),h=n[s.shape],f=r.pick(l.node().getBBox(),"width","height");s.elem=this,s.id&&c.attr("id",s.id),s.labelId&&u.attr("id",s.labelId),r.has(s,"width")&&(f.width=s.width),r.has(s,"height")&&(f.height=s.height),f.width+=s.paddingLeft+s.paddingRight,f.height+=s.paddingTop+s.paddingBottom,u.attr("transform","translate("+(s.paddingLeft-s.paddingRight)/2+","+(s.paddingTop-s.paddingBottom)/2+")");var d=o.select(this);d.select(".label-container").remove();var p=h(d,f,s).classed("label-container",!0);a.applyStyle(p,s.style);var g=p.node().getBBox();s.width=g.width,s.height=g.height})),s=u.exit?u.exit():u.selectAll(null);return a.applyTransition(s,e).style("opacity",0).remove(),u}},function(t,e,n){var r=n(12);t.exports=function(t,e){for(var n=t.append("text"),i=function(t){for(var e,n="",r=!1,i=0;i<t.length;++i)if(e=t[i],r){switch(e){case"n":n+="\n";break;default:n+=e}r=!1}else"\\"===e?r=!0:n+=e;return n}(e.label).split("\n"),a=0;a<i.length;a++)n.append("tspan").attr("xml:space","preserve").attr("dy","1em").attr("x","1").text(i[a]);return r.applyStyle(n,e.labelStyle),n}},function(t,e,n){var r=n(12);t.exports=function(t,e){var n=t;return n.node().appendChild(e.label),r.applyStyle(n,e.labelStyle),n}},function(t,e,n){var r=n(12),i=n(31),a=n(97);t.exports=function(t,e){var n,o=e.nodes().filter((function(t){return r.isSubgraph(e,t)})),s=t.selectAll("g.cluster").data(o,(function(t){return t}));s.selectAll("*").remove(),s.enter().append("g").attr("class","cluster").attr("id",(function(t){return e.node(t).id})).style("opacity",0),s=t.selectAll("g.cluster"),r.applyTransition(s,e).style("opacity",1),s.each((function(t){var n=e.node(t),r=i.select(this);i.select(this).append("rect");var o=r.append("g").attr("class","label");a(o,n,n.clusterLabelPos)})),s.selectAll("rect").each((function(t){var n=e.node(t),a=i.select(this);r.applyStyle(a,n.style)})),n=s.exit?s.exit():s.selectAll(null);return r.applyTransition(n,e).style("opacity",0).remove(),s}},function(t,e,n){"use strict";var r=n(43),i=n(97),a=n(12),o=n(31);t.exports=function(t,e){var n,s=t.selectAll("g.edgeLabel").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0);s.exit().remove(),s.enter().append("g").classed("edgeLabel",!0).style("opacity",0),(s=t.selectAll("g.edgeLabel")).each((function(t){var n=o.select(this);n.select(".label").remove();var a=e.edge(t),s=i(n,e.edge(t),0,0).classed("label",!0),c=s.node().getBBox();a.labelId&&s.attr("id",a.labelId),r.has(a,"width")||(a.width=c.width),r.has(a,"height")||(a.height=c.height)})),n=s.exit?s.exit():s.selectAll(null);return a.applyTransition(n,e).style("opacity",0).remove(),s}},function(t,e,n){"use strict";var r=n(43),i=n(165),a=n(12),o=n(31);function s(t,e){var n=(o.line||o.svg.line)().x((function(t){return t.x})).y((function(t){return t.y}));return(n.curve||n.interpolate)(t.curve),n(e)}t.exports=function(t,e,n){var c=t.selectAll("g.edgePath").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0),u=function(t,e){var n=t.enter().append("g").attr("class","edgePath").style("opacity",0);return n.append("path").attr("class","path").attr("d",(function(t){var n=e.edge(t),i=e.node(t.v).elem;return s(n,r.range(n.points.length).map((function(){return e=(t=i).getBBox(),{x:(n=t.ownerSVGElement.getScreenCTM().inverse().multiply(t.getScreenCTM()).translate(e.width/2,e.height/2)).e,y:n.f};var t,e,n})))})),n.append("defs"),n}(c,e);!function(t,e){var n=t.exit();a.applyTransition(n,e).style("opacity",0).remove()}(c,e);var l=void 0!==c.merge?c.merge(u):c;return a.applyTransition(l,e).style("opacity",1),l.each((function(t){var n=o.select(this),r=e.edge(t);r.elem=this,r.id&&n.attr("id",r.id),a.applyClass(n,r.class,(n.classed("update")?"update ":"")+"edgePath")})),l.selectAll("path.path").each((function(t){var n=e.edge(t);n.arrowheadId=r.uniqueId("arrowhead");var c=o.select(this).attr("marker-end",(function(){return"url("+(t=location.href,e=n.arrowheadId,t.split("#")[0]+"#"+e)+")";var t,e})).style("fill","none");a.applyTransition(c,e).attr("d",(function(t){return function(t,e){var n=t.edge(e),r=t.node(e.v),a=t.node(e.w),o=n.points.slice(1,n.points.length-1);return o.unshift(i(r,o[0])),o.push(i(a,o[o.length-1])),s(n,o)}(e,t)})),a.applyStyle(c,n.style)})),l.selectAll("defs *").remove(),l.selectAll("defs").each((function(t){var r=e.edge(t);(0,n[r.arrowhead])(o.select(this),r.arrowheadId,r,"arrowhead")})),l}},function(t,e,n){"use strict";var r=n(12),i=n(31);t.exports=function(t,e){function n(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},function(t,e,n){"use strict";var r=n(12),i=n(31),a=n(43);t.exports=function(t,e){function n(t){var n=e.edge(t);return a.has(n,"x")?"translate("+n.x+","+n.y+")":""}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},function(t,e,n){"use strict";var r=n(12),i=n(31);t.exports=function(t,e){var n=t.filter((function(){return!i.select(this).classed("update")}));function a(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}n.attr("transform",a),r.applyTransition(t,e).style("opacity",1).attr("transform",a),r.applyTransition(n.selectAll("rect"),e).attr("width",(function(t){return e.node(t).width})).attr("height",(function(t){return e.node(t).height})).attr("x",(function(t){return-e.node(t).width/2})).attr("y",(function(t){return-e.node(t).height/2}))}},function(t,e,n){"use strict";var r=n(168),i=n(96),a=n(166),o=n(167);t.exports={rect:function(t,e,n){var i=t.insert("rect",":first-child").attr("rx",n.rx).attr("ry",n.ry).attr("x",-e.width/2).attr("y",-e.height/2).attr("width",e.width).attr("height",e.height);return n.intersect=function(t){return r(n,t)},i},ellipse:function(t,e,n){var r=e.width/2,a=e.height/2,o=t.insert("ellipse",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("rx",r).attr("ry",a);return n.intersect=function(t){return i(n,r,a,t)},o},circle:function(t,e,n){var r=Math.max(e.width,e.height)/2,i=t.insert("circle",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("r",r);return n.intersect=function(t){return a(n,r,t)},i},diamond:function(t,e,n){var r=e.width*Math.SQRT2/2,i=e.height*Math.SQRT2/2,a=[{x:0,y:-i},{x:-r,y:0},{x:0,y:i},{x:r,y:0}],s=t.insert("polygon",":first-child").attr("points",a.map((function(t){return t.x+","+t.y})).join(" "));return n.intersect=function(t){return o(n,a,t)},s}}},function(t,e,n){var r=n(12);function i(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}t.exports={default:i,normal:i,vee:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 L 4 5 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])},undirected:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 5 L 10 5").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}}},function(t,e){t.exports="0.6.4"},function(t,e,n){"use strict";var r;function i(t){return r=r||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),r.innerHTML=t,unescape(r.textContent)}n.r(e);var a=n(23),o=n.n(a),s={debug:1,info:2,warn:3,error:4,fatal:5},c={debug:function(){},info:function(){},warn:function(){},error:function(){},fatal:function(){}},u=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"fatal";isNaN(t)&&(t=t.toLowerCase(),void 0!==s[t]&&(t=s[t])),c.trace=function(){},c.debug=function(){},c.info=function(){},c.warn=function(){},c.error=function(){},c.fatal=function(){},t<=s.fatal&&(c.fatal=console.error?console.error.bind(console,l("FATAL"),"color: orange"):console.log.bind(console,"",l("FATAL"))),t<=s.error&&(c.error=console.error?console.error.bind(console,l("ERROR"),"color: orange"):console.log.bind(console,"",l("ERROR"))),t<=s.warn&&(c.warn=console.warn?console.warn.bind(console,l("WARN"),"color: orange"):console.log.bind(console,"",l("WARN"))),t<=s.info&&(c.info=console.info?console.info.bind(console,l("INFO"),"color: lightblue"):console.log.bind(console,"",l("INFO"))),t<=s.debug&&(c.debug=console.debug?console.debug.bind(console,l("DEBUG"),"color: lightgreen"):console.log.bind(console,"",l("DEBUG")))},l=function(t){var e=o()().format("ss.SSS");return"%c".concat(e," : ").concat(t," : ")},h=n(169),f=n.n(h),d=n(0),p=n(44),g=n(70),y=function(t){for(var e="",n=0;n>=0;){if(!((n=t.indexOf("<script"))>=0)){e+=t,n=-1;break}e+=t.substr(0,n),(n=(t=t.substr(n+1)).indexOf("<\/script>"))>=0&&(n+=9,t=t.substr(n))}return e},v=/<br\s*\/?>/gi,m=function(t){return t.replace(v,"#br#")},b=function(t){return t.replace(/#br#/g,"<br/>")},x={getRows:function(t){if(!t)return 1;var e=m(t);return(e=e.replace(/\\n/g,"#br#")).split("#br#")},sanitizeText:function(t,e){var n=t,r=!0;if(!e.flowchart||!1!==e.flowchart.htmlLabels&&"false"!==e.flowchart.htmlLabels||(r=!1),r){var i=e.securityLevel;"antiscript"===i?n=y(n):"loose"!==i&&(n=(n=(n=m(n)).replace(/</g,"&lt;").replace(/>/g,"&gt;")).replace(/=/g,"&equals;"),n=b(n))}return n},hasBreaks:function(t){return/<br\s*[/]?>/gi.test(t)},splitBreaks:function(t){return t.split(/<br\s*[/]?>/gi)},lineBreakRegex:v,removeScript:y};function _(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function k(t){return(k="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function w(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var E={curveBasis:d.curveBasis,curveBasisClosed:d.curveBasisClosed,curveBasisOpen:d.curveBasisOpen,curveLinear:d.curveLinear,curveLinearClosed:d.curveLinearClosed,curveMonotoneX:d.curveMonotoneX,curveMonotoneY:d.curveMonotoneY,curveNatural:d.curveNatural,curveStep:d.curveStep,curveStepAfter:d.curveStepAfter,curveStepBefore:d.curveStepBefore},T=/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,C=/\s*(?:(?:(\w+)(?=:):|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,A=/\s*%%.*\n/gm,S=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;try{var n=new RegExp("[%]{2}(?![{]".concat(C.source,")(?=[}][%]{2}).*\n"),"ig");t=t.trim().replace(n,"").replace(/'/gm,'"'),c.debug("Detecting diagram directive".concat(null!==e?" type:"+e:""," based on the text:").concat(t));for(var r,i=[];null!==(r=T.exec(t));)if(r.index===T.lastIndex&&T.lastIndex++,r&&!e||e&&r[1]&&r[1].match(e)||e&&r[2]&&r[2].match(e)){var a=r[1]?r[1]:r[2],o=r[3]?r[3].trim():r[4]?JSON.parse(r[4].trim()):null;i.push({type:a,args:o})}return 0===i.length&&i.push({type:t,args:null}),1===i.length?i[0]:i}catch(n){return c.error("ERROR: ".concat(n.message," - Unable to parse directive").concat(null!==e?" type:"+e:""," based on the text:").concat(t)),{type:null,args:null}}},M=function(t){return t=t.replace(T,"").replace(A,"\n"),c.debug("Detecting diagram type based on the text "+t),t.match(/^\s*sequenceDiagram/)?"sequence":t.match(/^\s*gantt/)?"gantt":t.match(/^\s*classDiagram-v2/)?"classDiagram":t.match(/^\s*classDiagram/)?"class":t.match(/^\s*stateDiagram-v2/)?"stateDiagram":t.match(/^\s*stateDiagram/)?"state":t.match(/^\s*gitGraph/)?"git":t.match(/^\s*flowchart/)?"flowchart-v2":t.match(/^\s*info/)?"info":t.match(/^\s*pie/)?"pie":t.match(/^\s*erDiagram/)?"er":t.match(/^\s*journey/)?"journey":"flowchart"},O=function(t,e){var n={};return function(){for(var r=arguments.length,i=new Array(r),a=0;a<r;a++)i[a]=arguments[a];var o=e?e.apply(void 0,i):i[0];if(o in n)return n[o];var s=t.apply(void 0,i);return n[o]=s,s}},D=function(t,e){if(!t)return e;var n="curve".concat(t.charAt(0).toUpperCase()+t.slice(1));return E[n]||e},N=function(t,e){return t&&e?Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2)):0},B=function(t){for(var e="",n="",r=0;r<t.length;r++)void 0!==t[r]&&(t[r].startsWith("color:")||t[r].startsWith("text-align:")?n=n+t[r]+";":e=e+t[r]+";");return{style:e,labelStyle:n}},L=0,P=function(){return L++,"id-"+Math.random().toString(36).substr(2,12)+"-"+L};var I=function(t){return function(t){for(var e="",n="0123456789abcdef".length,r=0;r<t;r++)e+="0123456789abcdef".charAt(Math.floor(Math.random()*n));return e}(t.length)},F=function t(e,n,r){var i=Object.assign({depth:2,clobber:!1},r),a=i.depth,o=i.clobber;return Array.isArray(n)&&!Array.isArray(e)?(n.forEach((function(n){return t(e,n,r)})),e):Array.isArray(n)&&Array.isArray(e)?(n.forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),e):void 0===e||a<=0?null!=e&&"object"===k(e)&&"object"===k(n)?Object.assign(e,n):n:(void 0!==n&&"object"===k(e)&&"object"===k(n)&&Object.keys(n).forEach((function(r){"object"!==k(n[r])||void 0!==e[r]&&"object"!==k(e[r])?(o||"object"!==k(e[r])&&"object"!==k(n[r]))&&(e[r]=n[r]):(void 0===e[r]&&(e[r]=Array.isArray(n[r])?[]:{}),e[r]=t(e[r],n[r],{depth:a-1,clobber:o}))})),e)},j=function(t,e){var n=e.text.replace(x.lineBreakRegex," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.style("text-anchor",e.anchor),r.style("font-family",e.fontFamily),r.style("font-size",e.fontSize),r.style("font-weight",e.fontWeight),r.attr("fill",e.fill),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.attr("fill",e.fill),i.text(n),r},R=O((function(t,e,n){if(!t)return t;if(n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",joinWith:"<br/>"},n),x.lineBreakRegex.test(t))return t;var r=t.split(" "),i=[],a="";return r.forEach((function(t,o){var s=z("".concat(t," "),n),c=z(a,n);if(s>e){var u=Y(t,e,"-",n),l=u.hyphenatedStrings,h=u.remainingWord;i.push.apply(i,[a].concat(w(l))),a=h}else c+s>=e?(i.push(a),a=t):a=[a,t].filter(Boolean).join(" ");o+1===r.length&&i.push(a)})),i.filter((function(t){return""!==t})).join(n.joinWith)}),(function(t,e,n){return"".concat(t,"-").concat(e,"-").concat(n.fontSize,"-").concat(n.fontWeight,"-").concat(n.fontFamily,"-").concat(n.joinWith)})),Y=O((function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;r=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},r);var i=t.split(""),a=[],o="";return i.forEach((function(t,s){var c="".concat(o).concat(t);if(z(c,r)>=e){var u=s+1,l=i.length===u,h="".concat(c).concat(n);a.push(l?c:h),o=""}else o=c})),{hyphenatedStrings:a,remainingWord:o}}),(function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;return"".concat(t,"-").concat(e,"-").concat(n,"-").concat(r.fontSize,"-").concat(r.fontWeight,"-").concat(r.fontFamily)})),z=function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),U(t,e).width},U=O((function(t,e){var n=e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),r=n.fontSize,i=n.fontFamily,a=n.fontWeight;if(!t)return{width:0,height:0};var o=["sans-serif",i],s=t.split(x.lineBreakRegex),c=[],u=Object(d.select)("body");if(!u.remove)return{width:0,height:0,lineHeight:0};for(var l=u.append("svg"),h=0,f=o;h<f.length;h++){var p=f[h],g=0,y={width:0,height:0,lineHeight:0},v=!0,m=!1,b=void 0;try{for(var _,k=s[Symbol.iterator]();!(v=(_=k.next()).done);v=!0){var w=_.value,E={x:0,y:0,fill:void 0,anchor:"start",style:"#666",width:100,height:100,textMargin:0,rx:0,ry:0,valign:void 0};E.text=w;var T=j(l,E).style("font-size",r).style("font-weight",a).style("font-family",p),C=(T._groups||T)[0][0].getBBox();y.width=Math.round(Math.max(y.width,C.width)),g=Math.round(C.height),y.height+=g,y.lineHeight=Math.round(Math.max(y.lineHeight,g))}}catch(t){m=!0,b=t}finally{try{v||null==k.return||k.return()}finally{if(m)throw b}}c.push(y)}return l.remove(),c[isNaN(c[1].height)||isNaN(c[1].width)||isNaN(c[1].lineHeight)||c[0].height>c[1].height&&c[0].width>c[1].width&&c[0].lineHeight>c[1].lineHeight?0:1]}),(function(t,e){return"".concat(t,"-").concat(e.fontSize,"-").concat(e.fontWeight,"-").concat(e.fontFamily)})),$=function(t,e,n){var r=new Map;return r.set("height",t),n?(r.set("width","100%"),r.set("style","max-width: ".concat(e,"px;"))):r.set("width",e),r},W=function(t,e,n,r){!function(t,e){var n=!0,r=!1,i=void 0;try{for(var a,o=e[Symbol.iterator]();!(n=(a=o.next()).done);n=!0){var s=a.value;t.attr(s[0],s[1])}}catch(t){r=!0,i=t}finally{try{n||null==o.return||o.return()}finally{if(r)throw i}}}(t,$(e,n,r))},H={assignWithDepth:F,wrapLabel:R,calculateTextHeight:function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:15},e),U(t,e).height},calculateTextWidth:z,calculateTextDimensions:U,calculateSvgSizeAttrs:$,configureSvgSize:W,detectInit:function(t){var e=S(t,/(?:init\b)|(?:initialize\b)/),n={};if(Array.isArray(e)){var r=e.map((function(t){return t.args}));n=F(n,w(r))}else n=e.args;if(n){var i=M(t);["config"].forEach((function(t){void 0!==n[t]&&("flowchart-v2"===i&&(i="flowchart"),n[i]=n[t],delete n[t])}))}return n},detectDirective:S,detectType:M,isSubstringInArray:function(t,e){for(var n=0;n<e.length;n++)if(e[n].match(t))return n;return-1},interpolateToCurve:D,calcLabelPosition:function(t){return function(t){var e,n=0;t.forEach((function(t){n+=N(t,e),e=t}));var r=n/2,i=void 0;return e=void 0,t.forEach((function(t){if(e&&!i){var n=N(t,e);if(n<r)r-=n;else{var a=r/n;a<=0&&(i=e),a>=1&&(i={x:t.x,y:t.y}),a>0&&a<1&&(i={x:(1-a)*e.x+a*t.x,y:(1-a)*e.y+a*t.y})}}e=t})),i}(t)},calcCardinalityPosition:function(t,e,n){var r;c.info("our points",e),e[0]!==n&&(e=e.reverse()),e.forEach((function(t){N(t,r),r=t}));var i,a=25;r=void 0,e.forEach((function(t){if(r&&!i){var e=N(t,r);if(e<a)a-=e;else{var n=a/e;n<=0&&(i=r),n>=1&&(i={x:t.x,y:t.y}),n>0&&n<1&&(i={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var o=t?10:5,s=Math.atan2(e[0].y-i.y,e[0].x-i.x),u={x:0,y:0};return u.x=Math.sin(s)*o+(e[0].x+i.x)/2,u.y=-Math.cos(s)*o+(e[0].y+i.y)/2,u},calcTerminalLabelPosition:function(t,e,n){var r,i=JSON.parse(JSON.stringify(n));c.info("our points",i),"start_left"!==e&&"start_right"!==e&&(i=i.reverse()),i.forEach((function(t){N(t,r),r=t}));var a,o=25;r=void 0,i.forEach((function(t){if(r&&!a){var e=N(t,r);if(e<o)o-=e;else{var n=o/e;n<=0&&(a=r),n>=1&&(a={x:t.x,y:t.y}),n>0&&n<1&&(a={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var s=10,u=Math.atan2(i[0].y-a.y,i[0].x-a.x),l={x:0,y:0};return l.x=Math.sin(u)*s+(i[0].x+a.x)/2,l.y=-Math.cos(u)*s+(i[0].y+a.y)/2,"start_left"===e&&(l.x=Math.sin(u+Math.PI)*s+(i[0].x+a.x)/2,l.y=-Math.cos(u+Math.PI)*s+(i[0].y+a.y)/2),"end_right"===e&&(l.x=Math.sin(u-Math.PI)*s+(i[0].x+a.x)/2-5,l.y=-Math.cos(u-Math.PI)*s+(i[0].y+a.y)/2-5),"end_left"===e&&(l.x=Math.sin(u)*s+(i[0].x+a.x)/2-5,l.y=-Math.cos(u)*s+(i[0].y+a.y)/2-5),l},formatUrl:function(t,e){var n=t.trim();if(n)return"loose"!==e.securityLevel?Object(g.sanitizeUrl)(n):n},getStylesFromArray:B,generateId:P,random:I,memoize:O,runFunc:function(t){for(var e,n=t.split("."),r=n.length-1,i=n[r],a=window,o=0;o<r;o++)if(!(a=a[n[o]]))return;for(var s=arguments.length,c=new Array(s>1?s-1:0),u=1;u<s;u++)c[u-1]=arguments[u];(e=a)[i].apply(e,c)},initIdGeneratior:function(t,e){return t?new(function(){function t(){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.count=e?e.length:0}var n,r,i;return n=t,(r=[{key:"next",value:function(){return this.count++}}])&&_(n.prototype,r),i&&_(n,i),t}()):{next:function(){return Date.now()}}}},V=n(3),G=n.n(V),q=n(1),X=function(t,e){return e?Object(q.adjust)(t,{s:-40,l:10}):Object(q.adjust)(t,{s:-40,l:-10})};function Z(t){return(Z="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function J(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var K=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.darkMode=!1,this.primaryColor="#fff4dd",this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#ddd":"#333"),this.secondaryColor=this.secondaryColor||Object(q.adjust)(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Object(q.adjust)(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||X(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||X(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||X(this.noteBkgColor,this.darkMode),this.secondaryTextColor=this.secondaryTextColor||Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||Object(q.invert)(this.tertiaryColor),this.lineColor=this.lineColor||Object(q.invert)(this.background),this.textColor=this.textColor||this.primaryTextColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?Object(q.darken)(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||"grey",this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||Object(q.darken)(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||Object(q.invert)(this.lineColor),this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||Object(q.lighten)(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.labelColor=this.labelColor||this.primaryTextColor,this.altBackground=this.altBackground||this.tertiaryColor,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.classText=this.classText||this.textColor,this.fillType0=this.fillType0||this.primaryColor,this.fillType1=this.fillType1||this.secondaryColor,this.fillType2=this.fillType2||Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=this.fillType3||Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=this.fillType4||Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=this.fillType5||Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=this.fillType6||Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=this.fillType7||Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===Z(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&J(e.prototype,n),r&&J(e,r),t}();function Q(t){return(Q="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function tt(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var et=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=Object(q.lighten)(this.primaryColor,16),this.tertiaryColor=Object(q.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.tertiaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.mainBkg="#1f2020",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=Object(q.lighten)(Object(q.invert)("#323D47"),10),this.lineColor="calculated",this.border1="#81B1DB",this.border2=Object(q.rgba)(255,255,255,.25),this.arrowheadColor="calculated",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#181818",this.textColor="#ccc",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#F9FFFE",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="calculated",this.activationBkgColor="calculated",this.sequenceNumberColor="black",this.sectionBkgColor=Object(q.darken)("#EAE8D9",30),this.altSectionBkgColor="calculated",this.sectionBkgColor2="#EAE8D9",this.taskBorderColor=Object(q.rgba)(255,255,255,70),this.taskBkgColor="calculated",this.taskTextColor="calculated",this.taskTextLightColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor=Object(q.rgba)(255,255,255,50),this.activeTaskBkgColor="#81B1DB",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="grey",this.critBorderColor="#E83737",this.critBkgColor="#E83737",this.taskTextDarkColor="calculated",this.todayLineColor="#DB5757",this.labelColor="calculated",this.errorBkgColor="#a44141",this.errorTextColor="#ddd"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.secondBkg=Object(q.lighten)(this.mainBkg,16),this.lineColor=this.mainContrastColor,this.arrowheadColor=this.mainContrastColor,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.edgeLabelBackground=Object(q.lighten)(this.labelBackground,25),this.actorBorder=this.border1,this.actorBkg=this.mainBkg,this.actorTextColor=this.mainContrastColor,this.actorLineColor=this.mainContrastColor,this.signalColor=this.mainContrastColor,this.signalTextColor=this.mainContrastColor,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.mainContrastColor,this.loopTextColor=this.mainContrastColor,this.noteBorderColor=this.border2,this.noteTextColor=this.mainBkg,this.activationBorderColor=this.border1,this.activationBkgColor=this.secondBkg,this.altSectionBkgColor=this.background,this.taskBkgColor=Object(q.lighten)(this.mainBkg,23),this.taskTextColor=this.darkTextColor,this.taskTextLightColor=this.mainContrastColor,this.taskTextOutsideColor=this.taskTextLightColor,this.gridColor=this.mainContrastColor,this.doneTaskBkgColor=this.mainContrastColor,this.taskTextDarkColor=this.darkTextColor,this.labelColor=this.textColor,this.altBackground=Object(q.lighten)(this.background,20),this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128}),this.classText=this.primaryTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===Q(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&tt(e.prototype,n),r&&tt(e,r),t}();function nt(t){return(nt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function rt(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var it=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#ECECFF",this.secondaryColor=Object(q.adjust)(this.primaryColor,{h:120}),this.secondaryColor="#ffffde",this.tertiaryColor=Object(q.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.tertiaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.background="white",this.mainBkg="#ECECFF",this.secondBkg="#ffffde",this.lineColor="#333333",this.border1="#9370DB",this.border2="#aaaa33",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#e8e8e8",this.textColor="#333",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="calculated",this.sectionBkgColor2="calculated",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="calculated",this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor="calculated",this.taskTextOutsideColor=this.taskTextDarkColor,this.taskTextClickableColor="calculated",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBorderColor="calculated",this.critBkgColor="calculated",this.todayLineColor="calculated",this.sectionBkgColor=Object(q.rgba)(102,102,255,.49),this.altSectionBkgColor="white",this.sectionBkgColor2="#fff400",this.taskBorderColor="#534fbc",this.taskBkgColor="#8a90dd",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="#534fbc",this.activeTaskBkgColor="#bfc7ff",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.updateColors()}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.textColor,this.edgeLabelBackground=this.labelBackground,this.actorBorder=Object(q.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.signalColor=this.textColor,this.signalTextColor=this.textColor,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===nt(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&rt(e.prototype,n),r&&rt(e,r),t}();function at(t){return(at="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ot(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var st=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#cde498",this.secondaryColor="#cdffb2",this.background="white",this.mainBkg="#cde498",this.secondBkg="#cdffb2",this.lineColor="green",this.border1="#13540c",this.border2="#6eaa49",this.arrowheadColor="green",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.tertiaryColor=Object(q.lighten)("#cde498",10),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.primaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#333",this.edgeLabelBackground="#e8e8e8",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="#333",this.signalTextColor="#333",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="#326932",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="#6eaa49",this.altSectionBkgColor="white",this.sectionBkgColor2="#6eaa49",this.taskBorderColor="calculated",this.taskBkgColor="#487e3a",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.actorBorder=Object(q.darken)(this.mainBkg,20),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskBorderColor=this.border1,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===at(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&ot(e.prototype,n),r&&ot(e,r),t}();function ct(t){return(ct="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ut(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var lt=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.primaryColor="#eee",this.contrast="#26a",this.secondaryColor=Object(q.lighten)(this.contrast,55),this.background="#ffffff",this.tertiaryColor=Object(q.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.tertiaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.altBackground=Object(q.lighten)(this.contrast,55),this.mainBkg="#eee",this.secondBkg="calculated",this.lineColor="#666",this.border1="#999",this.border2="calculated",this.note="#ffa",this.text="#333",this.critical="#d42",this.done="#bbb",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="white",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="calculated",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="white",this.sectionBkgColor2="calculated",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBkgColor="calculated",this.critBorderColor="calculated",this.todayLineColor="calculated",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.secondBkg=Object(q.lighten)(this.contrast,55),this.border2=this.contrast,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.text,this.actorBorder=Object(q.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.actorTextColor=this.text,this.actorLineColor=this.lineColor,this.signalColor=this.text,this.signalTextColor=this.text,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.text,this.loopTextColor=this.text,this.noteBorderColor=Object(q.darken)(this.note,60),this.noteBkgColor=this.note,this.noteTextColor=this.actorTextColor,this.sectionBkgColor=Object(q.lighten)(this.contrast,30),this.sectionBkgColor2=Object(q.lighten)(this.contrast,30),this.taskBorderColor=Object(q.darken)(this.contrast,10),this.taskBkgColor=this.contrast,this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor=this.text,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.gridColor=Object(q.lighten)(this.border1,30),this.doneTaskBkgColor=this.done,this.doneTaskBorderColor=this.lineColor,this.critBkgColor=this.critical,this.critBorderColor=Object(q.darken)(this.critBkgColor,10),this.todayLineColor=this.critBkgColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===ct(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&ut(e.prototype,n),r&&ut(e,r),t}(),ht={base:{getThemeVariables:function(t){var e=new K;return e.calculate(t),e}},dark:{getThemeVariables:function(t){var e=new et;return e.calculate(t),e}},default:{getThemeVariables:function(t){var e=new it;return e.calculate(t),e}},forest:{getThemeVariables:function(t){var e=new st;return e.calculate(t),e}},neutral:{getThemeVariables:function(t){var e=new lt;return e.calculate(t),e}}},ft={theme:"default",themeVariables:ht.default.getThemeVariables(),themeCSS:void 0,maxTextSize:5e4,fontFamily:'"trebuchet ms", verdana, arial, sans-serif;',logLevel:5,securityLevel:"strict",startOnLoad:!0,arrowMarkerAbsolute:!1,secure:["secure","securityLevel","startOnLoad","maxTextSize"],deterministicIds:!1,deterministicIDSeed:void 0,flowchart:{diagramPadding:8,htmlLabels:!0,nodeSpacing:50,rankSpacing:50,curve:"linear",padding:15,useMaxWidth:!0},sequence:{activationWidth:10,diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",mirrorActors:!0,bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1,showSequenceNumbers:!1,actorFontSize:14,actorFontFamily:'"Open-Sans", "sans-serif"',actorFontWeight:400,noteFontSize:14,noteFontFamily:'"trebuchet ms", verdana, arial, sans-serif',noteFontWeight:400,noteAlign:"center",messageFontSize:16,messageFontFamily:'"trebuchet ms", verdana, arial, sans-serif',messageFontWeight:400,wrap:!1,wrapPadding:10,labelBoxWidth:50,labelBoxHeight:20,messageFont:function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},noteFont:function(){return{fontFamily:this.noteFontFamily,fontSize:this.noteFontSize,fontWeight:this.noteFontWeight}},actorFont:function(){return{fontFamily:this.actorFontFamily,fontSize:this.actorFontSize,fontWeight:this.actorFontWeight}}},gantt:{titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,leftPadding:75,gridLineStartPadding:35,fontSize:11,fontFamily:'"Open-Sans", "sans-serif"',numberSectionStyles:4,axisFormat:"%Y-%m-%d",useMaxWidth:!0,useWidth:void 0},journey:{diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1},class:{arrowMarkerAbsolute:!1,useMaxWidth:!0},git:{arrowMarkerAbsolute:!1,useWidth:void 0,useMaxWidth:!0},state:{dividerMargin:10,sizeUnit:5,padding:8,textHeight:10,titleShift:-15,noteMargin:10,forkWidth:70,forkHeight:7,miniPadding:2,fontSizeFactor:5.02,fontSize:24,labelHeight:16,edgeLengthFactor:"20",compositTitleSize:35,radius:5,useMaxWidth:!0},er:{diagramPadding:20,layoutDirection:"TB",minEntityWidth:100,minEntityHeight:75,entityPadding:15,stroke:"gray",fill:"honeydew",fontSize:12,useMaxWidth:!0},pie:{useWidth:void 0,useMaxWidth:!0}};ft.class.arrowMarkerAbsolute=ft.arrowMarkerAbsolute,ft.git.arrowMarkerAbsolute=ft.arrowMarkerAbsolute;var dt,pt=ft,gt=Object.freeze(pt),yt=F({},gt),vt=[],mt=F({},gt),bt=function(t,e){for(var n=F({},t),r={},i=0;i<e.length;i++){var a=e[i];kt(a),r=F(r,a)}if(n=F(n,r),r.theme){var o=F(dt.themeVariables||{},r.themeVariables);n.themeVariables=ht[n.theme].getThemeVariables(o)}return mt=n,n},xt=function(){return F({},yt)},_t=function(){return F({},mt)},kt=function(t){Object.keys(yt.secure).forEach((function(e){void 0!==t[yt.secure[e]]&&(c.debug("Denied attempt to modify a secure key ".concat(yt.secure[e]),t[yt.secure[e]]),delete t[yt.secure[e]])}))},wt=function(t){t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),vt.push(t),bt(yt,vt)},Et=function(){bt(yt,vt=[])};function Tt(t){return(Tt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function Ct(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var At,St,Mt=0,Ot=_t(),Dt={},Nt=[],Bt=[],Lt=[],Pt={},It={},Ft=0,jt=!0,Rt=[],Yt=function(t){for(var e=Object.keys(Dt),n=0;n<e.length;n++)if(Dt[e[n]].id===t)return Dt[e[n]].domId;return t},zt=function(t,e,n,r){var i={start:t,end:e,type:void 0,text:""};void 0!==(r=n.text)&&(i.text=x.sanitizeText(r.trim(),Ot),'"'===i.text[0]&&'"'===i.text[i.text.length-1]&&(i.text=i.text.substring(1,i.text.length-1))),void 0!==n&&(i.type=n.type,i.stroke=n.stroke,i.length=n.length),Nt.push(i)},Ut=function(t,e){t.split(",").forEach((function(t){var n=t;void 0!==Dt[n]&&Dt[n].classes.push(e),void 0!==Pt[n]&&Pt[n].classes.push(e)}))},$t=function(t){var e=Object(d.select)(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=Object(d.select)("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),Object(d.select)(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=Object(d.select)(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),Object(d.select)(this).classed("hover",!1)}))};Rt.push($t);var Wt=function(t){for(var e=0;e<Lt.length;e++)if(Lt[e].id===t)return e;return-1},Ht=-1,Vt=[],Gt=function(t,e){var n=!1;return t.forEach((function(t){t.nodes.indexOf(e)>=0&&(n=!0)})),n},qt=function(t,e){var n=[];return t.nodes.forEach((function(r,i){Gt(e,r)||n.push(t.nodes[i])})),{nodes:n}},Xt={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},defaultConfig:function(){return gt.flowchart},addVertex:function(t,e,n,r,i){var a,o=t;void 0!==o&&0!==o.trim().length&&(void 0===Dt[o]&&(Dt[o]={id:o,domId:"flowchart-"+o+"-"+Mt,styles:[],classes:[]}),Mt++,void 0!==e?(Ot=_t(),'"'===(a=x.sanitizeText(e.trim(),Ot))[0]&&'"'===a[a.length-1]&&(a=a.substring(1,a.length-1)),Dt[o].text=a):void 0===Dt[o].text&&(Dt[o].text=t),void 0!==n&&(Dt[o].type=n),null!=r&&r.forEach((function(t){Dt[o].styles.push(t)})),null!=i&&i.forEach((function(t){Dt[o].classes.push(t)})))},lookUpDomId:Yt,addLink:function(t,e,n,r){var i,a;for(i=0;i<t.length;i++)for(a=0;a<e.length;a++)zt(t[i],e[a],n,r)},updateLinkInterpolate:function(t,e){t.forEach((function(t){"default"===t?Nt.defaultInterpolate=e:Nt[t].interpolate=e}))},updateLink:function(t,e){t.forEach((function(t){"default"===t?Nt.defaultStyle=e:(-1===H.isSubstringInArray("fill",e)&&e.push("fill:none"),Nt[t].style=e)}))},addClass:function(t,e){void 0===Bt[t]&&(Bt[t]={id:t,styles:[],textStyles:[]}),null!=e&&e.forEach((function(e){if(e.match("color")){var n=e.replace("fill","bgFill").replace("color","fill");Bt[t].textStyles.push(n)}Bt[t].styles.push(e)}))},setDirection:function(t){(At=t).match(/.*</)&&(At="RL"),At.match(/.*\^/)&&(At="BT"),At.match(/.*>/)&&(At="LR"),At.match(/.*v/)&&(At="TB")},setClass:Ut,setTooltip:function(t,e){t.split(",").forEach((function(t){void 0!==e&&(It["gen-1"===St?Yt(t):t]=x.sanitizeText(e,Ot))}))},getTooltip:function(t){return It[t]},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){var r=Yt(t);if("loose"===_t().securityLevel&&void 0!==e){var i=[];if("string"==typeof n){i=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var a=0;a<i.length;a++){var o=i[a].trim();'"'===o.charAt(0)&&'"'===o.charAt(o.length-1)&&(o=o.substr(1,o.length-2)),i[a]=o}}0===i.length&&i.push(t),void 0!==Dt[t]&&(Dt[t].haveCallback=!0,Rt.push((function(){var t=document.querySelector('[id="'.concat(r,'"]'));null!==t&&t.addEventListener("click",(function(){H.runFunc.apply(H,[e].concat(Ct(i)))}),!1)})))}}(t,e,n)})),Ut(t,"clickable")},setLink:function(t,e,n){t.split(",").forEach((function(t){void 0!==Dt[t]&&(Dt[t].link=H.formatUrl(e,Ot),Dt[t].linkTarget=n)})),Ut(t,"clickable")},bindFunctions:function(t){Rt.forEach((function(e){e(t)}))},getDirection:function(){return At.trim()},getVertices:function(){return Dt},getEdges:function(){return Nt},getClasses:function(){return Bt},clear:function(t){Dt={},Bt={},Nt=[],(Rt=[]).push($t),Lt=[],Pt={},Ft=0,It=[],jt=!0,St=t||"gen-1"},setGen:function(t){St=t||"gen-1"},defaultStyle:function(){return"fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"},addSubGraph:function(t,e,n){var r=t.trim(),i=n;t===n&&n.match(/\s/)&&(r=void 0);var a,o,s,u=[];if(a=u.concat.apply(u,e),o={boolean:{},number:{},string:{}},s=[],u=a.filter((function(t){var e=Tt(t);return""!==t.trim()&&(e in o?!o[e].hasOwnProperty(t)&&(o[e][t]=!0):!(s.indexOf(t)>=0)&&s.push(t))})),"gen-1"===St){c.warn("LOOKING UP");for(var l=0;l<u.length;l++)u[l]=Yt(u[l])}r=r||"subGraph"+Ft,i=i||"",i=x.sanitizeText(i,Ot),Ft+=1;var h={id:r,nodes:u,title:i.trim(),classes:[]};return c.info("Adding",h.id,h.nodes),h.nodes=qt(h,Lt).nodes,Lt.push(h),Pt[r]=h,r},getDepthFirstPos:function(t){return Vt[t]},indexNodes:function(){Ht=-1,Lt.length>0&&function t(e,n){var r=Lt[n].nodes;if(!((Ht+=1)>2e3)){if(Vt[Ht]=n,Lt[n].id===e)return{result:!0,count:0};for(var i=0,a=1;i<r.length;){var o=Wt(r[i]);if(o>=0){var s=t(e,o);if(s.result)return{result:!0,count:a+s.count};a+=s.count}i+=1}return{result:!1,count:a}}}("none",Lt.length-1)},getSubGraphs:function(){return Lt},destructLink:function(t,e){var n,r=function(t){var e=t.trim(),n=e.slice(0,-1),r="arrow_open";switch(e.slice(-1)){case"x":r="arrow_cross","x"===e[0]&&(r="double_"+r,n=n.slice(1));break;case">":r="arrow_point","<"===e[0]&&(r="double_"+r,n=n.slice(1));break;case"o":r="arrow_circle","o"===e[0]&&(r="double_"+r,n=n.slice(1))}var i="normal",a=n.length-1;"="===n[0]&&(i="thick");var o=function(t,e){for(var n=e.length,r=0,i=0;i<n;++i)e[i]===t&&++r;return r}(".",n);return o&&(i="dotted",a=o),{type:r,stroke:i,length:a}}(t);if(e){if((n=function(t){var e=t.trim(),n="arrow_open";switch(e[0]){case"<":n="arrow_point",e=e.slice(1);break;case"x":n="arrow_cross",e=e.slice(1);break;case"o":n="arrow_circle",e=e.slice(1)}var r="normal";return-1!==e.indexOf("=")&&(r="thick"),-1!==e.indexOf(".")&&(r="dotted"),{type:n,stroke:r}}(e)).stroke!==r.stroke)return{type:"INVALID",stroke:"INVALID"};if("arrow_open"===n.type)n.type=r.type;else{if(n.type!==r.type)return{type:"INVALID",stroke:"INVALID"};n.type="double_"+n.type}return"double_arrow"===n.type&&(n.type="double_arrow_point"),n.length=r.length,n}return r},lex:{firstGraph:function(){return!!jt&&(jt=!1,!0)}},exists:Gt,makeUniq:qt},Zt=n(26),Jt=n.n(Zt),Kt=n(6),Qt=n.n(Kt),te=n(50),ee=n.n(te);function ne(t,e,n){var r=.9*(e.width+e.height),i=[{x:r/2,y:0},{x:r,y:-r/2},{x:r/2,y:-r},{x:0,y:-r/2}],a=de(t,r,r,i);return n.intersect=function(t){return Qt.a.intersect.polygon(n,i,t)},a}function re(t,e,n){var r=e.height,i=r/4,a=e.width+2*i,o=[{x:i,y:0},{x:a-i,y:0},{x:a,y:-r/2},{x:a-i,y:-r},{x:i,y:-r},{x:0,y:-r/2}],s=de(t,a,r,o);return n.intersect=function(t){return Qt.a.intersect.polygon(n,o,t)},s}function ie(t,e,n){var r=e.width,i=e.height,a=[{x:-i/2,y:0},{x:r,y:0},{x:r,y:-i},{x:-i/2,y:-i},{x:0,y:-i/2}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function ae(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function oe(t,e,n){var r=e.width,i=e.height,a=[{x:2*i/6,y:0},{x:r+i/6,y:0},{x:r-2*i/6,y:-i},{x:-i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function se(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r+2*i/6,y:0},{x:r-i/6,y:-i},{x:i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function ce(t,e,n){var r=e.width,i=e.height,a=[{x:i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:-2*i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function ue(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r+i/2,y:0},{x:r,y:-i/2},{x:r+i/2,y:-i},{x:0,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function le(t,e,n){var r=e.height,i=e.width+r/4,a=t.insert("rect",":first-child").attr("rx",r/2).attr("ry",r/2).attr("x",-i/2).attr("y",-r/2).attr("width",i).attr("height",r);return n.intersect=function(t){return Qt.a.intersect.rect(n,t)},a}function he(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r,y:0},{x:r,y:-i},{x:0,y:-i},{x:0,y:0},{x:-8,y:0},{x:r+8,y:0},{x:r+8,y:-i},{x:-8,y:-i},{x:-8,y:0}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function fe(t,e,n){var r=e.width,i=r/2,a=i/(2.5+r/50),o=e.height+a,s="M 0,"+a+" a "+i+","+a+" 0,0,0 "+r+" 0 a "+i+","+a+" 0,0,0 "+-r+" 0 l 0,"+o+" a "+i+","+a+" 0,0,0 "+r+" 0 l 0,"+-o,c=t.attr("label-offset-y",a).insert("path",":first-child").attr("d",s).attr("transform","translate("+-r/2+","+-(o/2+a)+")");return n.intersect=function(t){var e=Qt.a.intersect.rect(n,t),r=e.x-n.x;if(0!=i&&(Math.abs(r)<n.width/2||Math.abs(r)==n.width/2&&Math.abs(e.y-n.y)>n.height/2-a)){var o=a*a*(1-r*r/(i*i));0!=o&&(o=Math.sqrt(o)),o=a-o,t.y-n.y>0&&(o=-o),e.y+=o}return e},c}function de(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("transform","translate("+-e/2+","+n/2+")")}var pe={addToRender:function(t){t.shapes().question=ne,t.shapes().hexagon=re,t.shapes().stadium=le,t.shapes().subroutine=he,t.shapes().cylinder=fe,t.shapes().rect_left_inv_arrow=ie,t.shapes().lean_right=ae,t.shapes().lean_left=oe,t.shapes().trapezoid=se,t.shapes().inv_trapezoid=ce,t.shapes().rect_right_inv_arrow=ue},addToRenderV2:function(t){t({question:ne}),t({hexagon:re}),t({stadium:le}),t({subroutine:he}),t({cylinder:fe}),t({rect_left_inv_arrow:ie}),t({lean_right:ae}),t({lean_left:oe}),t({trapezoid:se}),t({inv_trapezoid:ce}),t({rect_right_inv_arrow:ue})}},ge={},ye=function(t,e,n){var r=Object(d.select)('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var o,s=B(i.styles),u=void 0!==i.text?i.text:i.id;if(_t().flowchart.htmlLabels){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(o=ee()(r,l).node()).parentNode.removeChild(o)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",s.labelStyle.replace("color:","fill:"));for(var f=u.split(x.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}o=h}var g=0,y="";switch(i.type){case"round":g=5,y="rect";break;case"square":y="rect";break;case"diamond":y="question";break;case"hexagon":y="hexagon";break;case"odd":y="rect_left_inv_arrow";break;case"lean_right":y="lean_right";break;case"lean_left":y="lean_left";break;case"trapezoid":y="trapezoid";break;case"inv_trapezoid":y="inv_trapezoid";break;case"odd_right":y="rect_left_inv_arrow";break;case"circle":y="circle";break;case"ellipse":y="ellipse";break;case"stadium":y="stadium";break;case"subroutine":y="subroutine";break;case"cylinder":y="cylinder";break;case"group":y="rect";break;default:y="rect"}c.warn("Adding node",i.id,i.domId),e.setNode(Xt.lookUpDomId(i.id),{labelType:"svg",labelStyle:s.labelStyle,shape:y,label:o,rx:g,ry:g,class:a,style:s.style,id:Xt.lookUpDomId(i.id)})}))},ve=function(t,e){var n,r,i=0;if(void 0!==t.defaultStyle){var a=B(t.defaultStyle);n=a.style,r=a.labelStyle}t.forEach((function(a){i++;var o="L-"+a.start+"-"+a.end,s="LS-"+a.start,c="LE-"+a.end,u={};"arrow_open"===a.type?u.arrowhead="none":u.arrowhead="normal";var l="",h="";if(void 0!==a.style){var f=B(a.style);l=f.style,h=f.labelStyle}else switch(a.stroke){case"normal":l="fill:none",void 0!==n&&(l=n),void 0!==r&&(h=r);break;case"dotted":l="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":l=" stroke-width: 3.5px;fill:none"}u.style=l,u.labelStyle=h,void 0!==a.interpolate?u.curve=D(a.interpolate,d.curveLinear):void 0!==t.defaultInterpolate?u.curve=D(t.defaultInterpolate,d.curveLinear):u.curve=D(ge.curve,d.curveLinear),void 0===a.text?void 0!==a.style&&(u.arrowheadStyle="fill: #333"):(u.arrowheadStyle="fill: #333",u.labelpos="c",_t().flowchart.htmlLabels?(u.labelType="html",u.label='<span id="L-'.concat(o,'" class="edgeLabel L-').concat(s,"' L-").concat(c,'">').concat(a.text.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),"</span>")):(u.labelType="text",u.label=a.text.replace(x.lineBreakRegex,"\n"),void 0===a.style&&(u.style=u.style||"stroke: #333; stroke-width: 1.5px;fill:none"),u.labelStyle=u.labelStyle.replace("color:","fill:"))),u.id=o,u.class=s+" "+c,u.minlen=a.length||1,e.setEdge(Xt.lookUpDomId(a.start),Xt.lookUpDomId(a.end),u,i)}))},me=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)ge[e[n]]=t[e[n]]},be=function(t){c.info("Extracting classes"),Xt.clear();try{var e=Jt.a.parser;return e.yy=Xt,e.parse(t),Xt.getClasses()}catch(t){return}},xe=function(t,e){c.info("Drawing flowchart"),Xt.clear(),Xt.setGen("gen-1");var n=Jt.a.parser;n.yy=Xt,n.parse(t);var r=Xt.getDirection();void 0===r&&(r="TD");for(var i,a=_t().flowchart,o=a.nodeSpacing||50,s=a.rankSpacing||50,u=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:o,ranksep:s,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=Xt.getSubGraphs(),h=l.length-1;h>=0;h--)i=l[h],Xt.addVertex(i.id,i.title,"group",void 0,i.classes);var f=Xt.getVertices();c.warn("Get vertices",f);var p=Xt.getEdges(),g=0;for(g=l.length-1;g>=0;g--){i=l[g],Object(d.selectAll)("cluster").append("text");for(var y=0;y<i.nodes.length;y++)c.warn("Setting subgraph",i.nodes[y],Xt.lookUpDomId(i.nodes[y]),Xt.lookUpDomId(i.id)),u.setParent(Xt.lookUpDomId(i.nodes[y]),Xt.lookUpDomId(i.id))}ye(f,u,e),ve(p,u);var v=new(0,Qt.a.render);pe.addToRender(v),v.arrows().none=function(t,e,n,r){var i=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 0 0 L 0 0 z");Qt.a.util.applyStyle(i,n[r+"Style"])},v.arrows().normal=function(t,e){t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowheadPath").style("stroke-width",1).style("stroke-dasharray","1,0")};var m=Object(d.select)('[id="'.concat(e,'"]'));m.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),c.warn(u);var b=Object(d.select)("#"+e+" g");v(b,u),b.selectAll("g.node").attr("title",(function(){return Xt.getTooltip(this.id)}));var x=a.diagramPadding,_=m.node().getBBox(),k=_.width+2*x,w=_.height+2*x;W(m,w,k,a.useMaxWidth);var E="".concat(_.x-x," ").concat(_.y-x," ").concat(k," ").concat(w);for(c.debug("viewBox ".concat(E)),m.attr("viewBox",E),Xt.indexNodes("subGraph"+g),g=0;g<l.length;g++)if("undefined"!==(i=l[g]).title){var T=document.querySelectorAll("#"+e+' [id="'+Xt.lookUpDomId(i.id)+'"] rect'),C=document.querySelectorAll("#"+e+' [id="'+Xt.lookUpDomId(i.id)+'"]'),A=T[0].x.baseVal.value,S=T[0].y.baseVal.value,M=T[0].width.baseVal.value,O=Object(d.select)(C[0]).select(".label");O.attr("transform","translate(".concat(A+M/2,", ").concat(S+14,")")),O.attr("id",e+"Text");for(var D=0;D<i.classes.length;D++)C[0].classList.add(i.classes[D])}a.htmlLabels;for(var N=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),B=0;B<N.length;B++){var L=N[B],P=L.getBBox(),I=document.createElementNS("http://www.w3.org/2000/svg","rect");I.setAttribute("rx",0),I.setAttribute("ry",0),I.setAttribute("width",P.width),I.setAttribute("height",P.height),L.insertBefore(I,L.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=Object(d.select)("#"+e+' [id="'+Xt.lookUpDomId(t)+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))},_e=n(18),ke=n.n(_e),we={extension:function(t,e,n){c.trace("Making markers for ",n),t.append("defs").append("marker").attr("id",e+"-extensionStart").attr("class","marker extension "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),t.append("defs").append("marker").attr("id",e+"-extensionEnd").attr("class","marker extension "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z")},composition:function(t,e){t.append("defs").append("marker").attr("id",e+"-compositionStart").attr("class","marker composition "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-compositionEnd").attr("class","marker composition "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},aggregation:function(t,e){t.append("defs").append("marker").attr("id",e+"-aggregationStart").attr("class","marker aggregation "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-aggregationEnd").attr("class","marker aggregation "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},dependency:function(t,e){t.append("defs").append("marker").attr("id",e+"-dependencyStart").attr("class","marker dependency "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-dependencyEnd").attr("class","marker dependency "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},point:function(t,e){t.append("marker").attr("id",e+"-pointEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-pointStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",0).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},circle:function(t,e){t.append("marker").attr("id",e+"-circleEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-circleStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},cross:function(t,e){t.append("marker").attr("id",e+"-crossEnd").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-crossStart").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0")},barb:function(t,e){t.append("defs").append("marker").attr("id",e+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","strokeWidth").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")}},Ee=function(t,e,n,r){e.forEach((function(e){we[e](t,n,r)}))};var Te=function(t,e,n,r){var i=t||"";if(_t().flowchart.htmlLabels)return i=i.replace(/\\n|\n/g,"<br />"),c.info("vertexText"+i),function(t){var e,n,r=Object(d.select)(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")),i=r.append("xhtml:div"),a=t.label,o=t.isNode?"nodeLabel":"edgeLabel";return i.html('<span class="'+o+'">'+a+"</span>"),e=i,(n=t.labelStyle)&&e.attr("style",n),i.style("display","inline-block"),i.style("white-space","nowrap"),i.attr("xmlns","http://www.w3.org/1999/xhtml"),r.node()}({isNode:r,label:i.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),labelStyle:e.replace("fill:","color:")});var a=document.createElementNS("http://www.w3.org/2000/svg","text");a.setAttribute("style",e.replace("color:","fill:"));var o=[];o="string"==typeof i?i.split(/\\n|\n|<br\s*\/?>/gi):Array.isArray(i)?i:[];for(var s=0;s<o.length;s++){var u=document.createElementNS("http://www.w3.org/2000/svg","tspan");u.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),u.setAttribute("dy","1em"),u.setAttribute("x","0"),n?u.setAttribute("class","title-row"):u.setAttribute("class","row"),u.textContent=o[s].trim(),a.appendChild(u)}return a},Ce=function(t,e,n,r){var i;i=n||"node default";var a=t.insert("g").attr("class",i).attr("id",e.domId||e.id),o=a.insert("g").attr("class","label").attr("style",e.labelStyle),s=o.node().appendChild(Te(e.labelText,e.labelStyle,!1,r)),c=s.getBBox();if(_t().flowchart.htmlLabels){var u=s.children[0],l=Object(d.select)(s);c=u.getBoundingClientRect(),l.attr("width",c.width),l.attr("height",c.height)}var h=e.padding/2;return o.attr("transform","translate("+-c.width/2+", "+-c.height/2+")"),{shapeSvg:a,bbox:c,halfPadding:h,label:o}},Ae=function(t,e){var n=e.node().getBBox();t.width=n.width,t.height=n.height};function Se(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("class","label-container").attr("transform","translate("+-e/2+","+n/2+")")}var Me={},Oe={},De={},Ne=function(t,e){return c.debug("In isDecendant",e," ",t," = ",Oe[e].indexOf(t)>=0),Oe[e].indexOf(t)>=0},Be=function t(e,n,r,i){c.warn("Copying children of ",e,"root",i,"data",n.node(e),i);var a=n.children(e)||[];e!==i&&a.push(e),c.warn("Copying (nodes) clusterId",e,"nodes",a),a.forEach((function(a){if(n.children(a).length>0)t(a,n,r,i);else{var o=n.node(a);c.info("cp ",a," to ",i," with parent ",e),r.setNode(a,o),i!==n.parent(a)&&(c.warn("Setting parent",a,n.parent(a)),r.setParent(a,n.parent(a))),e!==i&&a!==e?(c.debug("Setting parent",a,e),r.setParent(a,e)):(c.info("In copy ",e,"root",i,"data",n.node(e),i),c.debug("Not Setting parent for node=",a,"cluster!==rootId",e!==i,"node!==clusterId",a!==e));var s=n.edges(a);c.debug("Copying Edges",s),s.forEach((function(t){c.info("Edge",t);var a=n.edge(t.v,t.w,t.name);c.info("Edge data",a,i);try{!function(t,e){return c.info("Decendants of ",e," is ",Oe[e]),c.info("Edge is ",t),t.v!==e&&(t.w!==e&&(Oe[e]?(c.info("Here "),Oe[e].indexOf(t.v)>=0||(!!Ne(t.v,e)||(!!Ne(t.w,e)||Oe[e].indexOf(t.w)>=0))):(c.debug("Tilt, ",e,",not in decendants"),!1)))}(t,i)?c.info("Skipping copy of edge ",t.v,"--\x3e",t.w," rootId: ",i," clusterId:",e):(c.info("Copying as ",t.v,t.w,a,t.name),r.setEdge(t.v,t.w,a,t.name),c.info("newGraph edges ",r.edges(),r.edge(r.edges()[0])))}catch(t){c.error(t)}}))}c.debug("Removing node",a),n.removeNode(a)}))},Le=function t(e,n){c.trace("Searching",e);var r=n.children(e);if(c.trace("Searching children of id ",e,r),r.length<1)return c.trace("This is a valid node",e),e;for(var i=0;i<r.length;i++){var a=t(r[i],n);if(a)return c.trace("Found replacement for",e," => ",a),a}},Pe=function(t){return Me[t]&&Me[t].externalConnections&&Me[t]?Me[t].id:t},Ie=function(t,e){!t||e>10?c.debug("Opting out, no graph "):(c.debug("Opting in, graph "),t.nodes().forEach((function(e){t.children(e).length>0&&(c.warn("Cluster identified",e," Replacement id in edges: ",Le(e,t)),Oe[e]=function t(e,n){for(var r=n.children(e),i=[].concat(r),a=0;a<r.length;a++)De[r[a]]=e,i=i.concat(t(r[a],n));return i}(e,t),Me[e]={id:Le(e,t),clusterData:t.node(e)})})),t.nodes().forEach((function(e){var n=t.children(e),r=t.edges();n.length>0?(c.debug("Cluster identified",e,Oe),r.forEach((function(t){t.v!==e&&t.w!==e&&(Ne(t.v,e)^Ne(t.w,e)&&(c.warn("Edge: ",t," leaves cluster ",e),c.warn("Decendants of XXX ",e,": ",Oe[e]),Me[e].externalConnections=!0))}))):c.debug("Not a cluster ",e,Oe)})),t.edges().forEach((function(e){var n=t.edge(e);c.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(e)),c.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(t.edge(e)));var r=e.v,i=e.w;c.warn("Fix XXX",Me,"ids:",e.v,e.w,"Translateing: ",Me[e.v]," --- ",Me[e.w]),(Me[e.v]||Me[e.w])&&(c.warn("Fixing and trixing - removing XXX",e.v,e.w,e.name),r=Pe(e.v),i=Pe(e.w),t.removeEdge(e.v,e.w,e.name),r!==e.v&&(n.fromCluster=e.v),i!==e.w&&(n.toCluster=e.w),c.warn("Fix Replacing with XXX",r,i,e.name),t.setEdge(r,i,n,e.name))})),c.warn("Adjusted Graph",G.a.json.write(t)),Fe(t,0),c.trace(Me))},Fe=function t(e,n){if(c.warn("extractor - ",n,G.a.json.write(e),e.children("D")),n>10)c.error("Bailing out");else{for(var r=e.nodes(),i=!1,a=0;a<r.length;a++){var o=r[a],s=e.children(o);i=i||s.length>0}if(i){c.debug("Nodes = ",r,n);for(var u=0;u<r.length;u++){var l=r[u];if(c.debug("Extracting node",l,Me,Me[l]&&!Me[l].externalConnections,!e.parent(l),e.node(l),e.children("D")," Depth ",n),Me[l])if(!Me[l].externalConnections&&e.children(l)&&e.children(l).length>0){c.warn("Cluster without external connections, without a parent and with children",l,n);var h=e.graph(),f=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:"TB"===h.rankdir?"LR":"TB",nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));c.warn("Old graph before copy",G.a.json.write(e)),Be(l,e,f,l),e.setNode(l,{clusterNode:!0,id:l,clusterData:Me[l].clusterData,labelText:Me[l].labelText,graph:f}),c.warn("New graph after copy node: (",l,")",G.a.json.write(f)),c.debug("Old graph after copy",G.a.json.write(e))}else c.warn("Cluster ** ",l," **not meeting the criteria !externalConnections:",!Me[l].externalConnections," no parent: ",!e.parent(l)," children ",e.children(l)&&e.children(l).length>0,e.children("D"),n),c.debug(Me);else c.debug("Not a cluster",l,n)}r=e.nodes(),c.warn("New list of nodes",r);for(var d=0;d<r.length;d++){var p=r[d],g=e.node(p);c.warn(" Now next level",p,g),g.clusterNode&&t(g.graph,n+1)}}else c.debug("Done, no node has children",e.nodes())}},je=function(t){return function t(e,n){if(0===n.length)return[];var r=Object.assign(n);return n.forEach((function(n){var i=e.children(n),a=t(e,i);r=r.concat(a)})),r}(t,t.children())},Re=n(170);var Ye=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);return r.y<a&&(l=-l),{x:i+u,y:a+l}};var ze=function(t,e,n){return Ye(t,e,e,n)};function Ue(t,e){return t*e>0}var $e=function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,g,y;if(i=e.y-t.y,o=t.x-e.x,c=e.x*t.y-t.x*e.y,f=i*n.x+o*n.y+c,d=i*r.x+o*r.y+c,!(0!==f&&0!==d&&Ue(f,d)||(a=r.y-n.y,s=n.x-r.x,u=r.x*n.y-n.x*r.y,l=a*t.x+s*t.y+u,h=a*e.x+s*e.y+u,0!==l&&0!==h&&Ue(l,h)||0==(p=i*s-a*o))))return g=Math.abs(p/2),{x:(y=o*u-s*c)<0?(y-g)/p:(y+g)/p,y:(y=a*c-i*u)<0?(y-g)/p:(y+g)/p}},We=function(t,e,n){var r=t.x,i=t.y,a=[],o=Number.POSITIVE_INFINITY,s=Number.POSITIVE_INFINITY;"function"==typeof e.forEach?e.forEach((function(t){o=Math.min(o,t.x),s=Math.min(s,t.y)})):(o=Math.min(o,e.x),s=Math.min(s,e.y));for(var c=r-t.width/2-o,u=i-t.height/2-s,l=0;l<e.length;l++){var h=e[l],f=e[l<e.length-1?l+1:0],d=$e(t,n,{x:c+h.x,y:u+h.y},{x:c+f.x,y:u+f.y});d&&a.push(d)}if(!a.length)return t;a.length>1&&a.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1}));return a[0]};var He=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o),{x:i+n,y:a+r}},Ve={node:n.n(Re).a,circle:ze,ellipse:Ye,polygon:We,rect:He},Ge=function(t,e){var n=Ce(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;c.info("Classes = ",e.classes);var o=r.insert("rect",":first-child");return o.attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),Ae(e,o),e.intersect=function(t){return Ve.rect(e,t)},r};function qe(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var Xe=[],Ze={},Je=0,Ke=[],Qe=function(t){var e="",n=t;if(t.indexOf("~")>0){var r=t.split("~");n=r[0],e=r[1]}return{className:n,type:e}},tn=function(t){var e=Qe(t);void 0===Ze[e.className]&&(Ze[e.className]={id:e.className,type:e.type,cssClasses:[],methods:[],members:[],annotations:[],domId:"classid-"+e.className+"-"+Je},Je++)},en=function(t){for(var e=Object.keys(Ze),n=0;n<e.length;n++)if(Ze[e[n]].id===t)return Ze[e[n]].domId},nn=function(t,e){var n=Qe(t).className,r=Ze[n];if("string"==typeof e){var i=e.trim();i.startsWith("<<")&&i.endsWith(">>")?r.annotations.push(i.substring(2,i.length-2)):i.indexOf(")")>0?r.methods.push(i):i&&r.members.push(i)}},rn=function(t,e){t.split(",").forEach((function(t){var n=t;t[0].match(/\d/)&&(n="classid-"+n),void 0!==Ze[n]&&Ze[n].cssClasses.push(e)}))},an=function(t,e,n){var r=_t(),i=t,a=en(i);if("loose"===r.securityLevel&&void 0!==e&&void 0!==Ze[i]){var o=[];if("string"==typeof n){o=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var s=0;s<o.length;s++){var c=o[s].trim();'"'===c.charAt(0)&&'"'===c.charAt(c.length-1)&&(c=c.substr(1,c.length-2)),o[s]=c}}0===o.length&&o.push(a),Ke.push((function(){var t=document.querySelector('[id="'.concat(a,'"]'));null!==t&&t.addEventListener("click",(function(){H.runFunc.apply(H,[e].concat(qe(o)))}),!1)}))}},on={AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},sn=function(t){var e=Object(d.select)(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=Object(d.select)("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),Object(d.select)(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=Object(d.select)(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),Object(d.select)(this).classed("hover",!1)}))};Ke.push(sn);var cn={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().class},addClass:tn,bindFunctions:function(t){Ke.forEach((function(e){e(t)}))},clear:function(){Xe=[],Ze={},(Ke=[]).push(sn)},getClass:function(t){return Ze[t]},getClasses:function(){return Ze},addAnnotation:function(t,e){var n=Qe(t).className;Ze[n].annotations.push(e)},getRelations:function(){return Xe},addRelation:function(t){c.debug("Adding relation: "+JSON.stringify(t)),tn(t.id1),tn(t.id2),t.id1=Qe(t.id1).className,t.id2=Qe(t.id2).className,Xe.push(t)},addMember:nn,addMembers:function(t,e){Array.isArray(e)&&(e.reverse(),e.forEach((function(e){return nn(t,e)})))},cleanupLabel:function(t){return":"===t.substring(0,1)?t.substr(1).trim():t.trim()},lineType:{LINE:0,DOTTED_LINE:1},relationType:on,setClickEvent:function(t,e,n){t.split(",").forEach((function(t){an(t,e,n),Ze[t].haveCallback=!0})),rn(t,"clickable")},setCssClass:rn,setLink:function(t,e,n){var r=_t();t.split(",").forEach((function(t){var i=t;t[0].match(/\d/)&&(i="classid-"+i),void 0!==Ze[i]&&(Ze[i].link=H.formatUrl(e,r),Ze[i].linkTarget="string"==typeof n?n:"_blank")})),rn(t,"clickable")},setTooltip:function(t,e){var n=_t();t.split(",").forEach((function(t){void 0!==e&&(Ze[t].tooltip=x.sanitizeText(e,n))}))},lookUpDomId:en},un=0,ln=function(t){var e=t.match(/(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+)/),n=t.match(/^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/);return e&&!n?hn(e):n?fn(n):dn(t)},hn=function(t){var e="";try{e=(t[1]?t[1].trim():"")+(t[2]?t[2].trim():"")+(t[3]?gn(t[3].trim()):"")+" "+(t[4]?t[4].trim():"")}catch(n){e=t}return{displayText:e,cssStyle:""}},fn=function(t){var e="",n="";try{var r=t[1]?t[1].trim():"",i=t[2]?t[2].trim():"",a=t[3]?gn(t[3].trim()):"",o=t[4]?t[4].trim():"";n=r+i+"("+a+")"+(t[5]?" : "+gn(t[5]).trim():""),e=yn(o)}catch(e){n=t}return{displayText:n,cssStyle:e}},dn=function(t){var e="",n="",r="",i=t.indexOf("("),a=t.indexOf(")");if(i>1&&a>i&&a<=t.length){var o="",s="",c=t.substring(0,1);c.match(/\w/)?s=t.substring(0,i).trim():(c.match(/\+|-|~|#/)&&(o=c),s=t.substring(1,i).trim());var u=t.substring(i+1,a),l=t.substring(a+1,1);n=yn(l),e=o+s+"("+gn(u.trim())+")",a<"".length&&""!==(r=t.substring(a+2).trim())&&(r=" : "+gn(r))}else e=gn(t);return{displayText:e,cssStyle:n}},pn=function(t,e,n,r){var i=ln(e),a=t.append("tspan").attr("x",r.padding).text(i.displayText);""!==i.cssStyle&&a.attr("style",i.cssStyle),n||a.attr("dy",r.textHeight)},gn=function t(e){var n=e;return-1!=e.indexOf("~")?t(n=(n=n.replace("~","<")).replace("~",">")):n},yn=function(t){switch(t){case"*":return"font-style:italic;";case"$":return"text-decoration:underline;";default:return""}},vn=function(t,e,n){c.info("Rendering class "+e);var r,i=e.id,a={id:i,label:e.id,width:0,height:0},o=t.append("g").attr("id",en(i)).attr("class","classGroup");r=e.link?o.append("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget).append("text").attr("y",n.textHeight+n.padding).attr("x",0):o.append("text").attr("y",n.textHeight+n.padding).attr("x",0);var s=!0;e.annotations.forEach((function(t){var e=r.append("tspan").text("«"+t+"»");s||e.attr("dy",n.textHeight),s=!1}));var u=e.id;void 0!==e.type&&""!==e.type&&(u+="<"+e.type+">");var l=r.append("tspan").text(u).attr("class","title");s||l.attr("dy",n.textHeight);var h=r.node().getBBox().height,f=o.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin/2).attr("y2",n.padding+h+n.dividerMargin/2),d=o.append("text").attr("x",n.padding).attr("y",h+n.dividerMargin+n.textHeight).attr("fill","white").attr("class","classText");s=!0,e.members.forEach((function(t){pn(d,t,s,n),s=!1}));var p=d.node().getBBox(),g=o.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin+p.height).attr("y2",n.padding+h+n.dividerMargin+p.height),y=o.append("text").attr("x",n.padding).attr("y",h+2*n.dividerMargin+p.height+n.textHeight).attr("fill","white").attr("class","classText");s=!0,e.methods.forEach((function(t){pn(y,t,s,n),s=!1}));var v=o.node().getBBox(),m=" ";e.cssClasses.length>0&&(m+=e.cssClasses.join(" "));var b=o.insert("rect",":first-child").attr("x",0).attr("y",0).attr("width",v.width+2*n.padding).attr("height",v.height+n.padding+.5*n.dividerMargin).attr("class",m).node().getBBox().width;return r.node().childNodes.forEach((function(t){t.setAttribute("x",(b-t.getBBox().width)/2)})),e.tooltip&&r.insert("title").text(e.tooltip),f.attr("x2",b),g.attr("x2",b),a.width=b,a.height=v.height+n.padding+.5*n.dividerMargin,a},mn=function(t,e,n,r){var i=function(t){switch(t){case on.AGGREGATION:return"aggregation";case on.EXTENSION:return"extension";case on.COMPOSITION:return"composition";case on.DEPENDENCY:return"dependency"}};e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var a,o,s=e.points,u=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis),l=t.append("path").attr("d",u(s)).attr("id","edge"+un).attr("class","relation"),h="";r.arrowMarkerAbsolute&&(h=(h=(h=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),1==n.relation.lineType&&l.attr("class","relation dashed-line"),"none"!==n.relation.type1&&l.attr("marker-start","url("+h+"#"+i(n.relation.type1)+"Start)"),"none"!==n.relation.type2&&l.attr("marker-end","url("+h+"#"+i(n.relation.type2)+"End)");var f,p,g,y,v=e.points.length,m=H.calcLabelPosition(e.points);if(a=m.x,o=m.y,v%2!=0&&v>1){var b=H.calcCardinalityPosition("none"!==n.relation.type1,e.points,e.points[0]),x=H.calcCardinalityPosition("none"!==n.relation.type2,e.points,e.points[v-1]);c.debug("cardinality_1_point "+JSON.stringify(b)),c.debug("cardinality_2_point "+JSON.stringify(x)),f=b.x,p=b.y,g=x.x,y=x.y}if(void 0!==n.title){var _=t.append("g").attr("class","classLabel"),k=_.append("text").attr("class","label").attr("x",a).attr("y",o).attr("fill","red").attr("text-anchor","middle").text(n.title);window.label=k;var w=k.node().getBBox();_.insert("rect",":first-child").attr("class","box").attr("x",w.x-r.padding/2).attr("y",w.y-r.padding/2).attr("width",w.width+r.padding).attr("height",w.height+r.padding)}(c.info("Rendering relation "+JSON.stringify(n)),void 0!==n.relationTitle1&&"none"!==n.relationTitle1)&&t.append("g").attr("class","cardinality").append("text").attr("class","type1").attr("x",f).attr("y",p).attr("fill","black").attr("font-size","6").text(n.relationTitle1);void 0!==n.relationTitle2&&"none"!==n.relationTitle2&&t.append("g").attr("class","cardinality").append("text").attr("class","type2").attr("x",g).attr("y",y).attr("fill","black").attr("font-size","6").text(n.relationTitle2);un++},bn=function(t,e,n){var r=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),i=70,a=10;"LR"===n&&(i=10,a=70);var o=r.append("rect").style("stroke","black").style("fill","black").attr("x",-1*i/2).attr("y",-1*a/2).attr("width",i).attr("height",a).attr("class","fork-join");return Ae(e,o),e.height=e.height+e.padding/2,e.width=e.width+e.padding/2,e.intersect=function(t){return Ve.rect(e,t)},r},xn={question:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding+(i.height+e.padding),o=[{x:a/2,y:0},{x:a,y:-a/2},{x:a/2,y:-a},{x:0,y:-a/2}];c.info("Question main (Circle)");var s=Se(r,a,a,o);return Ae(e,s),e.intersect=function(t){return c.warn("Intersect called"),Ve.polygon(e,o,t)},r},rect:function(t,e){var n=Ce(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;c.trace("Classes = ",e.classes);var o=r.insert("rect",":first-child");return o.attr("class","basic label-container").attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),Ae(e,o),e.intersect=function(t){return Ve.rect(e,t)},r},rectWithTitle:function(t,e){var n;n=e.classes?"node "+e.classes:"node default";var r=t.insert("g").attr("class",n).attr("id",e.domId||e.id),i=r.insert("rect",":first-child"),a=r.insert("line"),o=r.insert("g").attr("class","label"),s=e.labelText.flat();c.info("Label text",s[0]);var u,l=o.node().appendChild(Te(s[0],e.labelStyle,!0,!0));if(_t().flowchart.htmlLabels){var h=l.children[0],f=Object(d.select)(l);u=h.getBoundingClientRect(),f.attr("width",u.width),f.attr("height",u.height)}c.info("Text 2",s);var p=s.slice(1,s.length),g=l.getBBox(),y=o.node().appendChild(Te(p.join("<br/>"),e.labelStyle,!0,!0));if(_t().flowchart.htmlLabels){var v=y.children[0],m=Object(d.select)(y);u=v.getBoundingClientRect(),m.attr("width",u.width),m.attr("height",u.height)}var b=e.padding/2;return Object(d.select)(y).attr("transform","translate( "+(u.width>g.width?0:(g.width-u.width)/2)+", "+(g.height+b+5)+")"),Object(d.select)(l).attr("transform","translate( "+(u.width<g.width?0:-(g.width-u.width)/2)+", 0)"),u=o.node().getBBox(),o.attr("transform","translate("+-u.width/2+", "+(-u.height/2-b+3)+")"),i.attr("class","outer title-state").attr("x",-u.width/2-b).attr("y",-u.height/2-b).attr("width",u.width+e.padding).attr("height",u.height+e.padding),a.attr("class","divider").attr("x1",-u.width/2-b).attr("x2",u.width/2+b).attr("y1",-u.height/2-b+g.height+b).attr("y2",-u.height/2-b+g.height+b),Ae(e,i),e.intersect=function(t){return Ve.rect(e,t)},r},circle:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding,o=r.insert("circle",":first-child");return o.attr("rx",e.rx).attr("ry",e.ry).attr("r",i.width/2+a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),c.info("Circle main"),Ae(e,o),e.intersect=function(t){return c.info("Circle intersect",e,i.width/2+a,t),Ve.circle(e,i.width/2+a,t)},r},stadium:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=i.width+a/4+e.padding,s=r.insert("rect",":first-child").attr("rx",a/2).attr("ry",a/2).attr("x",-o/2).attr("y",-a/2).attr("width",o).attr("height",a);return Ae(e,s),e.intersect=function(t){return Ve.rect(e,t)},r},hexagon:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=a/4,s=i.width+2*o+e.padding,c=Se(r,s,a,[{x:o,y:0},{x:s-o,y:0},{x:s,y:-a/2},{x:s-o,y:-a},{x:o,y:-a},{x:0,y:-a/2}]);return Ae(e,c),e.intersect=function(t){return Ve.polygon(e,t)},r},rect_left_inv_arrow:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:-o/2,y:0},{x:a,y:0},{x:a,y:-o},{x:-o/2,y:-o},{x:0,y:-o/2}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},lean_right:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:-2*o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},lean_left:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:2*o/6,y:0},{x:a+o/6,y:0},{x:a-2*o/6,y:-o},{x:-o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},trapezoid:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:-2*o/6,y:0},{x:a+2*o/6,y:0},{x:a-o/6,y:-o},{x:o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},inv_trapezoid:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:-2*o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},rect_right_inv_arrow:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:0,y:0},{x:a+o/2,y:0},{x:a,y:-o/2},{x:a+o/2,y:-o},{x:0,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},cylinder:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=a/2,s=o/(2.5+a/50),c=i.height+s+e.padding,u="M 0,"+s+" a "+o+","+s+" 0,0,0 "+a+" 0 a "+o+","+s+" 0,0,0 "+-a+" 0 l 0,"+c+" a "+o+","+s+" 0,0,0 "+a+" 0 l 0,"+-c,l=r.attr("label-offset-y",s).insert("path",":first-child").attr("d",u).attr("transform","translate("+-a/2+","+-(c/2+s)+")");return Ae(e,l),e.intersect=function(t){var n=Ve.rect(e,t),r=n.x-e.x;if(0!=o&&(Math.abs(r)<e.width/2||Math.abs(r)==e.width/2&&Math.abs(n.y-e.y)>e.height/2-s)){var i=s*s*(1-r*r/(o*o));0!=i&&(i=Math.sqrt(i)),i=s-i,t.y-e.y>0&&(i=-i),n.y+=i}return n},r},start:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child");return r.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),Ae(e,r),e.intersect=function(t){return Ve.circle(e,7,t)},n},end:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child"),i=n.insert("circle",":first-child");return i.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),r.attr("class","state-end").attr("r",5).attr("width",10).attr("height",10),Ae(e,i),e.intersect=function(t){return Ve.circle(e,7,t)},n},note:Ge,subroutine:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:0,y:0},{x:a,y:0},{x:a,y:-o},{x:0,y:-o},{x:0,y:0},{x:-8,y:0},{x:a+8,y:0},{x:a+8,y:-o},{x:-8,y:-o},{x:-8,y:0}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},fork:bn,join:bn,class_box:function(t,e){var n,r=e.padding/2;n=e.classes?"node "+e.classes:"node default";var i=t.insert("g").attr("class",n).attr("id",e.domId||e.id),a=i.insert("rect",":first-child"),o=i.insert("line"),s=i.insert("line"),c=0,u=4,l=i.insert("g").attr("class","label"),h=0,f=e.classData.annotations&&e.classData.annotations[0],p=e.classData.annotations[0]?"«"+e.classData.annotations[0]+"»":"",g=l.node().appendChild(Te(p,e.labelStyle,!0,!0)),y=g.getBBox();if(_t().flowchart.htmlLabels){var v=g.children[0],m=Object(d.select)(g);y=v.getBoundingClientRect(),m.attr("width",y.width),m.attr("height",y.height)}e.classData.annotations[0]&&(u+=y.height+4,c+=y.width);var b=e.classData.id;void 0!==e.classData.type&&""!==e.classData.type&&(b+="<"+e.classData.type+">");var x=l.node().appendChild(Te(b,e.labelStyle,!0,!0));Object(d.select)(x).attr("class","classTitle");var _=x.getBBox();if(_t().flowchart.htmlLabels){var k=x.children[0],w=Object(d.select)(x);_=k.getBoundingClientRect(),w.attr("width",_.width),w.attr("height",_.height)}u+=_.height+4,_.width>c&&(c=_.width);var E=[];e.classData.members.forEach((function(t){var n=ln(t).displayText,r=l.node().appendChild(Te(n,e.labelStyle,!0,!0)),i=r.getBBox();if(_t().flowchart.htmlLabels){var a=r.children[0],o=Object(d.select)(r);i=a.getBoundingClientRect(),o.attr("width",i.width),o.attr("height",i.height)}i.width>c&&(c=i.width),u+=i.height+4,E.push(r)})),u+=8;var T=[];if(e.classData.methods.forEach((function(t){var n=ln(t).displayText,r=l.node().appendChild(Te(n,e.labelStyle,!0,!0)),i=r.getBBox();if(_t().flowchart.htmlLabels){var a=r.children[0],o=Object(d.select)(r);i=a.getBoundingClientRect(),o.attr("width",i.width),o.attr("height",i.height)}i.width>c&&(c=i.width),u+=i.height+4,T.push(r)})),u+=8,f){var C=(c-y.width)/2;Object(d.select)(g).attr("transform","translate( "+(-1*c/2+C)+", "+-1*u/2+")"),h=y.height+4}var A=(c-_.width)/2;return Object(d.select)(x).attr("transform","translate( "+(-1*c/2+A)+", "+(-1*u/2+h)+")"),h+=_.height+4,o.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,E.forEach((function(t){Object(d.select)(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h+4)+")"),h+=_.height+4})),h+=8,s.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,T.forEach((function(t){Object(d.select)(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h)+")"),h+=_.height+4})),a.attr("class","outer title-state").attr("x",-c/2-r).attr("y",-u/2-r).attr("width",c+e.padding).attr("height",u+e.padding),Ae(e,a),e.intersect=function(t){return Ve.rect(e,t)},i}},_n={},kn=function(t){var e=_n[t.id];c.trace("Transforming node",t,"translate("+(t.x-t.width/2-5)+", "+(t.y-t.height/2-5)+")");t.clusterNode?e.attr("transform","translate("+(t.x-t.width/2-8)+", "+(t.y-t.height/2-8)+")"):e.attr("transform","translate("+t.x+", "+t.y+")")},wn={rect:function(t,e){c.trace("Creating subgraph rect for ",e.id,e);var n=t.insert("g").attr("class","cluster"+(e.class?" "+e.class:"")).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=i.node().appendChild(Te(e.labelText,e.labelStyle,void 0,!0)),o=a.getBBox();if(_t().flowchart.htmlLabels){var s=a.children[0],u=Object(d.select)(a);o=s.getBoundingClientRect(),u.attr("width",o.width),u.attr("height",o.height)}var l=0*e.padding,h=l/2;c.trace("Data ",e,JSON.stringify(e)),r.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-h).attr("y",e.y-e.height/2-h).attr("width",e.width+l).attr("height",e.height+l),i.attr("transform","translate("+(e.x-o.width/2)+", "+(e.y-e.height/2+e.padding/3)+")");var f=r.node().getBBox();return e.width=f.width,e.height=f.height,e.intersect=function(t){return He(e,t)},n},roundedWithTitle:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=n.append("rect"),o=i.node().appendChild(Te(e.labelText,e.labelStyle,void 0,!0)),s=o.getBBox();if(_t().flowchart.htmlLabels){var c=o.children[0],u=Object(d.select)(o);s=c.getBoundingClientRect(),u.attr("width",s.width),u.attr("height",s.height)}s=o.getBBox();var l=0*e.padding,h=l/2;r.attr("class","outer").attr("x",e.x-e.width/2-h).attr("y",e.y-e.height/2-h).attr("width",e.width+l).attr("height",e.height+l),a.attr("class","inner").attr("x",e.x-e.width/2-h).attr("y",e.y-e.height/2-h+s.height-1).attr("width",e.width+l).attr("height",e.height+l-s.height-3),i.attr("transform","translate("+(e.x-s.width/2)+", "+(e.y-e.height/2-e.padding/3+(_t().flowchart.htmlLabels?5:3))+")");var f=r.node().getBBox();return e.width=f.width,e.height=f.height,e.intersect=function(t){return He(e,t)},n},noteGroup:function(t,e){var n=t.insert("g").attr("class","note-cluster").attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2-a).attr("width",e.width+i).attr("height",e.height+i).attr("fill","none");var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.intersect=function(t){return He(e,t)},n},divider:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("class","divider").attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2).attr("width",e.width+i).attr("height",e.height+i);var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.intersect=function(t){return He(e,t)},n}},En={},Tn={},Cn={},An=function(t,e){var n=t.x,r=t.y,i=Math.abs(e.x-n),a=Math.abs(e.y-r),o=t.width/2,s=t.height/2;return i>=o||a>=s},Sn=function(t,e,n){c.warn("intersection calc o:",e," i:",n,t);var r=t.x,i=t.y,a=Math.abs(r-n.x),o=t.width/2,s=n.x<e.x?o-a:o+a,u=t.height/2,l=r-o,h=r+o,f=i-u,d=i+u;if(e.x===l||e.x===h||e.y===f||e.y===d)return c.warn("calc equals on edge"),e;var p=Math.abs(e.y-n.y),g=Math.abs(e.x-n.x);if(Math.abs(i-e.y)*o>Math.abs(r-e.x)*u){var y=n.y<e.y?e.y-u-i:i-u-e.y;s=g*y/p;var v={x:n.x<e.x?n.x+g-s:n.x-s,y:n.y<e.y?n.y+p-y:n.y-y};return c.warn("topp/bott calc, Q ".concat(p,", q ").concat(y,", R ").concat(g,", r ").concat(s),v),v}var m=m=p*(s=n.x<e.x?e.x-o-r:r-o-e.x)/g;return c.warn("sides calc, Q ".concat(p,", q ").concat(m,", R ").concat(g,", r ").concat(s),{x:n.x<e.x?n.x+g-s:n.x+a-o,y:n.y<e.y?n.y+m:n.y-m}),{x:n.x<e.x?n.x+g-s:n.x+a-o,y:n.y<e.y?n.y+m:n.y-m}},Mn=function t(e,n,r,i){c.info("Graph in recursive render: XXX",G.a.json.write(n),i);var a=n.graph().rankdir;c.warn("Dir in recursive render - dir:",a);var o=e.insert("g").attr("class","root");n.nodes()?c.info("Recursive render XXX",n.nodes()):c.info("No nodes found for",n),n.edges().length>0&&c.info("Recursive edges",n.edge(n.edges()[0]));var s=o.insert("g").attr("class","clusters"),u=o.insert("g").attr("class","edgePaths"),l=o.insert("g").attr("class","edgeLabels"),h=o.insert("g").attr("class","nodes");return n.nodes().forEach((function(e){var o=n.node(e);if(void 0!==i){var s=JSON.parse(JSON.stringify(i.clusterData));c.info("Setting data for cluster XXX (",e,") ",s,i),n.setNode(i.id,s),n.parent(e)||(c.warn("Setting parent",e,i.id),n.setParent(e,i.id,s))}if(c.info("(Insert) Node XXX"+e+": "+JSON.stringify(n.node(e))),o&&o.clusterNode){c.info("Cluster identified",e,o,n.node(e));var u=t(h,o.graph,r,n.node(e));Ae(o,u),function(t,e){_n[e.id]=t}(u,o),c.warn("Recursive render complete",u,o)}else n.children(e).length>0?(c.info("Cluster - the non recursive path XXX",e,o.id,o,n),c.info(Le(o.id,n)),Me[o.id]={id:Le(o.id,n),node:o}):(c.info("Node - the non recursive path",e,o.id,o),function(t,e,n){var r,i;e.link?(r=t.insert("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget||"_blank"),i=xn[e.shape](r,e,n)):r=i=xn[e.shape](t,e,n),e.tooltip&&i.attr("title",e.tooltip),e.class&&i.attr("class","node default "+e.class),_n[e.id]=r,e.haveCallback&&_n[e.id].attr("class",_n[e.id].attr("class")+" clickable")}(h,n.node(e),a))})),n.edges().forEach((function(t){var e=n.edge(t.v,t.w,t.name);c.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t)),c.info("Edge "+t.v+" -> "+t.w+": ",t," ",JSON.stringify(n.edge(t))),c.info("Fix",Me,"ids:",t.v,t.w,"Translateing: ",Me[t.v],Me[t.w]),function(t,e){var n=Te(e.label,e.labelStyle),r=t.insert("g").attr("class","edgeLabel"),i=r.insert("g").attr("class","label");i.node().appendChild(n);var a=n.getBBox();if(_t().flowchart.htmlLabels){var o=n.children[0],s=Object(d.select)(n);a=o.getBoundingClientRect(),s.attr("width",a.width),s.attr("height",a.height)}if(i.attr("transform","translate("+-a.width/2+", "+-a.height/2+")"),Tn[e.id]=r,e.width=a.width,e.height=a.height,e.startLabelLeft){var c=Te(e.startLabelLeft,e.labelStyle),u=t.insert("g").attr("class","edgeTerminals"),l=u.insert("g").attr("class","inner");l.node().appendChild(c);var h=c.getBBox();l.attr("transform","translate("+-h.width/2+", "+-h.height/2+")"),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].startLeft=u}if(e.startLabelRight){var f=Te(e.startLabelRight,e.labelStyle),p=t.insert("g").attr("class","edgeTerminals"),g=p.insert("g").attr("class","inner");p.node().appendChild(f),g.node().appendChild(f);var y=f.getBBox();g.attr("transform","translate("+-y.width/2+", "+-y.height/2+")"),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].startRight=p}if(e.endLabelLeft){var v=Te(e.endLabelLeft,e.labelStyle),m=t.insert("g").attr("class","edgeTerminals"),b=m.insert("g").attr("class","inner");b.node().appendChild(v);var x=v.getBBox();b.attr("transform","translate("+-x.width/2+", "+-x.height/2+")"),m.node().appendChild(v),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].endLeft=m}if(e.endLabelRight){var _=Te(e.endLabelRight,e.labelStyle),k=t.insert("g").attr("class","edgeTerminals"),w=k.insert("g").attr("class","inner");w.node().appendChild(_);var E=_.getBBox();w.attr("transform","translate("+-E.width/2+", "+-E.height/2+")"),k.node().appendChild(_),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].endRight=k}}(l,e)})),n.edges().forEach((function(t){c.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t))})),c.info("#############################################"),c.info("### Layout ###"),c.info("#############################################"),c.info(n),ke.a.layout(n),c.info("Graph after layout:",G.a.json.write(n)),je(n).forEach((function(t){var e=n.node(t);c.info("Position "+t+": "+JSON.stringify(n.node(t))),c.info("Position "+t+": ("+e.x,","+e.y,") width: ",e.width," height: ",e.height),e&&e.clusterNode?kn(e):n.children(t).length>0?(!function(t,e){c.trace("Inserting cluster");var n=e.shape||"rect";En[e.id]=wn[n](t,e)}(s,e),Me[e.id].node=e):kn(e)})),n.edges().forEach((function(t){var e=n.edge(t);c.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(e),e);var i=function(t,e,n,r,i,a){var o=n.points,s=!1,u=a.node(e.v),l=a.node(e.w);if(l.intersect&&u.intersect&&((o=o.slice(1,n.points.length-1)).unshift(u.intersect(o[0])),c.info("Last point",o[o.length-1],l,l.intersect(o[o.length-1])),o.push(l.intersect(o[o.length-1]))),n.toCluster){var h;c.trace("edge",n),c.trace("to cluster",r[n.toCluster]),o=[];var f=!1;n.points.forEach((function(t){var e=r[n.toCluster].node;if(An(e,t)||f)f||o.push(t);else{c.trace("inside",n.toCluster,t,h);var i=Sn(e,h,t),a=!1;o.forEach((function(t){a=a||t.x===i.x&&t.y===i.y})),o.find((function(t){return t.x===i.x&&t.y===i.y}))?c.warn("no intersect",i,o):o.push(i),f=!0}h=t})),s=!0}if(n.fromCluster){c.trace("edge",n),c.warn("from cluster",r[n.fromCluster]);for(var p,g=[],y=!1,v=o.length-1;v>=0;v--){var m=o[v],b=r[n.fromCluster].node;if(An(b,m)||y)c.trace("Outside point",m),y||g.unshift(m);else{c.warn("inside",n.fromCluster,m,b);var x=Sn(b,p,m);g.unshift(x),y=!0}p=m}o=g,s=!0}var _,k=o.filter((function(t){return!Number.isNaN(t.y)})),w=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis);switch(n.thickness){case"normal":_="edge-thickness-normal";break;case"thick":_="edge-thickness-thick";break;default:_=""}switch(n.pattern){case"solid":_+=" edge-pattern-solid";break;case"dotted":_+=" edge-pattern-dotted";break;case"dashed":_+=" edge-pattern-dashed"}var E=t.append("path").attr("d",w(k)).attr("id",n.id).attr("class"," "+_+(n.classes?" "+n.classes:"")).attr("style",n.style),T="";switch(_t().state.arrowMarkerAbsolute&&(T=(T=(T=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),c.info("arrowTypeStart",n.arrowTypeStart),c.info("arrowTypeEnd",n.arrowTypeEnd),n.arrowTypeStart){case"arrow_cross":E.attr("marker-start","url("+T+"#"+i+"-crossStart)");break;case"arrow_point":E.attr("marker-start","url("+T+"#"+i+"-pointStart)");break;case"arrow_barb":E.attr("marker-start","url("+T+"#"+i+"-barbStart)");break;case"arrow_circle":E.attr("marker-start","url("+T+"#"+i+"-circleStart)");break;case"aggregation":E.attr("marker-start","url("+T+"#"+i+"-aggregationStart)");break;case"extension":E.attr("marker-start","url("+T+"#"+i+"-extensionStart)");break;case"composition":E.attr("marker-start","url("+T+"#"+i+"-compositionStart)");break;case"dependency":E.attr("marker-start","url("+T+"#"+i+"-dependencyStart)")}switch(n.arrowTypeEnd){case"arrow_cross":E.attr("marker-end","url("+T+"#"+i+"-crossEnd)");break;case"arrow_point":E.attr("marker-end","url("+T+"#"+i+"-pointEnd)");break;case"arrow_barb":E.attr("marker-end","url("+T+"#"+i+"-barbEnd)");break;case"arrow_circle":E.attr("marker-end","url("+T+"#"+i+"-circleEnd)");break;case"aggregation":E.attr("marker-end","url("+T+"#"+i+"-aggregationEnd)");break;case"extension":E.attr("marker-end","url("+T+"#"+i+"-extensionEnd)");break;case"composition":E.attr("marker-end","url("+T+"#"+i+"-compositionEnd)");break;case"dependency":E.attr("marker-end","url("+T+"#"+i+"-dependencyEnd)")}var C={};return s&&(C.updatedPath=o),C.originalPath=n.points,C}(u,t,e,Me,r,n);!function(t,e){c.info("Moving label",t.id,t.label,Tn[t.id]);var n=e.updatedPath?e.updatedPath:e.originalPath;if(t.label){var r=Tn[t.id],i=t.x,a=t.y;if(n){var o=H.calcLabelPosition(n);c.info("Moving label from (",i,",",a,") to (",o.x,",",o.y,")")}r.attr("transform","translate("+i+", "+a+")")}if(t.startLabelLeft){var s=Cn[t.id].startLeft,u=t.x,l=t.y;if(n){var h=H.calcTerminalLabelPosition(0,"start_left",n);u=h.x,l=h.y}s.attr("transform","translate("+u+", "+l+")")}if(t.startLabelRight){var f=Cn[t.id].startRight,d=t.x,p=t.y;if(n){var g=H.calcTerminalLabelPosition(0,"start_right",n);d=g.x,p=g.y}f.attr("transform","translate("+d+", "+p+")")}if(t.endLabelLeft){var y=Cn[t.id].endLeft,v=t.x,m=t.y;if(n){var b=H.calcTerminalLabelPosition(0,"end_left",n);v=b.x,m=b.y}y.attr("transform","translate("+v+", "+m+")")}if(t.endLabelRight){var x=Cn[t.id].endRight,_=t.x,k=t.y;if(n){var w=H.calcTerminalLabelPosition(0,"end_right",n);_=w.x,k=w.y}x.attr("transform","translate("+_+", "+k+")")}}(e,i)})),o},On=function(t,e,n,r,i){Ee(t,n,r,i),_n={},Tn={},Cn={},En={},Oe={},De={},Me={},c.warn("Graph at first:",G.a.json.write(e)),Ie(e),c.warn("Graph after:",G.a.json.write(e)),Mn(t,e,r)},Dn={},Nn=function(t,e,n){var r=Object(d.select)('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var o,s=B(i.styles),u=void 0!==i.text?i.text:i.id;if(_t().flowchart.htmlLabels){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(o=ee()(r,l).node()).parentNode.removeChild(o)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",s.labelStyle.replace("color:","fill:"));for(var f=u.split(x.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}o=h}var g=0,y="";switch(i.type){case"round":g=5,y="rect";break;case"square":y="rect";break;case"diamond":y="question";break;case"hexagon":y="hexagon";break;case"odd":y="rect_left_inv_arrow";break;case"lean_right":y="lean_right";break;case"lean_left":y="lean_left";break;case"trapezoid":y="trapezoid";break;case"inv_trapezoid":y="inv_trapezoid";break;case"odd_right":y="rect_left_inv_arrow";break;case"circle":y="circle";break;case"ellipse":y="ellipse";break;case"stadium":y="stadium";break;case"subroutine":y="subroutine";break;case"cylinder":y="cylinder";break;case"group":y="rect";break;default:y="rect"}e.setNode(i.id,{labelStyle:s.labelStyle,shape:y,labelText:u,rx:g,ry:g,class:a,style:s.style,id:i.id,link:i.link,linkTarget:i.linkTarget,tooltip:Xt.getTooltip(i.id)||"",domId:Xt.lookUpDomId(i.id),haveCallback:i.haveCallback,width:"group"===i.type?500:void 0,type:i.type,padding:_t().flowchart.padding}),c.info("setNode",{labelStyle:s.labelStyle,shape:y,labelText:u,rx:g,ry:g,class:a,style:s.style,id:i.id,domId:Xt.lookUpDomId(i.id),width:"group"===i.type?500:void 0,type:i.type,padding:_t().flowchart.padding})}))},Bn=function(t,e){var n,r,i=0;if(void 0!==t.defaultStyle){var a=B(t.defaultStyle);n=a.style,r=a.labelStyle}t.forEach((function(a){i++;var o="L-"+a.start+"-"+a.end,s="LS-"+a.start,c="LE-"+a.end,u={style:"",labelStyle:""};switch(u.minlen=a.length||1,"arrow_open"===a.type?u.arrowhead="none":u.arrowhead="normal",u.arrowTypeStart="arrow_open",u.arrowTypeEnd="arrow_open",a.type){case"double_arrow_cross":u.arrowTypeStart="arrow_cross";case"arrow_cross":u.arrowTypeEnd="arrow_cross";break;case"double_arrow_point":u.arrowTypeStart="arrow_point";case"arrow_point":u.arrowTypeEnd="arrow_point";break;case"double_arrow_circle":u.arrowTypeStart="arrow_circle";case"arrow_circle":u.arrowTypeEnd="arrow_circle"}var l="",h="";switch(a.stroke){case"normal":l="fill:none;",void 0!==n&&(l=n),void 0!==r&&(h=r),u.thickness="normal",u.pattern="solid";break;case"dotted":u.thickness="normal",u.pattern="dotted",u.style="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":u.thickness="thick",u.pattern="solid",u.style="stroke-width: 3.5px;fill:none;"}if(void 0!==a.style){var f=B(a.style);l=f.style,h=f.labelStyle}u.style=u.style+=l,u.labelStyle=u.labelStyle+=h,void 0!==a.interpolate?u.curve=D(a.interpolate,d.curveLinear):void 0!==t.defaultInterpolate?u.curve=D(t.defaultInterpolate,d.curveLinear):u.curve=D(Dn.curve,d.curveLinear),void 0===a.text?void 0!==a.style&&(u.arrowheadStyle="fill: #333"):(u.arrowheadStyle="fill: #333",u.labelpos="c"),u.labelType="text",u.label=a.text.replace(x.lineBreakRegex,"\n"),void 0===a.style&&(u.style=u.style||"stroke: #333; stroke-width: 1.5px;fill:none;"),u.labelStyle=u.labelStyle.replace("color:","fill:"),u.id=o,u.classes="flowchart-link "+s+" "+c,e.setEdge(a.start,a.end,u,i)}))},Ln=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)Dn[e[n]]=t[e[n]]},Pn=function(t,e){c.info("Drawing flowchart"),Xt.clear(),Xt.setGen("gen-2");var n=Jt.a.parser;n.yy=Xt,n.parse(t);var r=Xt.getDirection();void 0===r&&(r="TD");var i,a=_t().flowchart,o=a.nodeSpacing||50,s=a.rankSpacing||50,u=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:o,ranksep:s,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=Xt.getSubGraphs();c.info("Subgraphs - ",l);for(var h=l.length-1;h>=0;h--)i=l[h],c.info("Subgraph - ",i),Xt.addVertex(i.id,i.title,"group",void 0,i.classes);var f=Xt.getVertices(),p=Xt.getEdges();c.info(p);var g=0;for(g=l.length-1;g>=0;g--){i=l[g],Object(d.selectAll)("cluster").append("text");for(var y=0;y<i.nodes.length;y++)c.info("Setting up subgraphs",i.nodes[y],i.id),u.setParent(i.nodes[y],i.id)}Nn(f,u,e),Bn(p,u);var v=Object(d.select)('[id="'.concat(e,'"]'));v.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var m=Object(d.select)("#"+e+" g");On(m,u,["point","circle","cross"],"flowchart",e);var b=a.diagramPadding,x=v.node().getBBox(),_=x.width+2*b,k=x.height+2*b;if(c.debug("new ViewBox 0 0 ".concat(_," ").concat(k),"translate(".concat(b-u._label.marginx,", ").concat(b-u._label.marginy,")")),W(v,k,_,a.useMaxWidth),v.attr("viewBox","0 0 ".concat(_," ").concat(k)),v.select("g").attr("transform","translate(".concat(b-u._label.marginx,", ").concat(b-x.y,")")),Xt.indexNodes("subGraph"+g),!a.htmlLabels)for(var w=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),E=0;E<w.length;E++){var T=w[E],C=T.getBBox(),A=document.createElementNS("http://www.w3.org/2000/svg","rect");A.setAttribute("rx",0),A.setAttribute("ry",0),A.setAttribute("width",C.width),A.setAttribute("height",C.height),T.insertBefore(A,T.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=Object(d.select)("#"+e+' [id="'+t+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))},In=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},Fn=function(t,e){var n=0,r=0,i=e.text.split(x.lineBreakRegex),a=[],o=0,s=function(){return e.y};if(void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0)switch(e.valign){case"top":case"start":s=function(){return Math.round(e.y+e.textMargin)};break;case"middle":case"center":s=function(){return Math.round(e.y+(n+r+e.textMargin)/2)};break;case"bottom":case"end":s=function(){return Math.round(e.y+(n+r+2*e.textMargin)-e.textMargin)}}if(void 0!==e.anchor&&void 0!==e.textMargin&&void 0!==e.width)switch(e.anchor){case"left":case"start":e.x=Math.round(e.x+e.textMargin),e.anchor="start",e.dominantBaseline="text-after-edge",e.alignmentBaseline="middle";break;case"middle":case"center":e.x=Math.round(e.x+e.width/2),e.anchor="middle",e.dominantBaseline="middle",e.alignmentBaseline="middle";break;case"right":case"end":e.x=Math.round(e.x+e.width-e.textMargin),e.anchor="end",e.dominantBaseline="text-before-edge",e.alignmentBaseline="middle"}for(var c=0;c<i.length;c++){var u=i[c];void 0!==e.textMargin&&0===e.textMargin&&void 0!==e.fontSize&&(o=c*e.fontSize);var l=t.append("text");if(l.attr("x",e.x),l.attr("y",s()),void 0!==e.anchor&&l.attr("text-anchor",e.anchor).attr("dominant-baseline",e.dominantBaseline).attr("alignment-baseline",e.alignmentBaseline),void 0!==e.fontFamily&&l.style("font-family",e.fontFamily),void 0!==e.fontSize&&l.style("font-size",e.fontSize),void 0!==e.fontWeight&&l.style("font-weight",e.fontWeight),void 0!==e.fill&&l.attr("fill",e.fill),void 0!==e.class&&l.attr("class",e.class),void 0!==e.dy?l.attr("dy",e.dy):0!==o&&l.attr("dy",o),e.tspan){var h=l.append("tspan");h.attr("x",e.x),void 0!==e.fill&&h.attr("fill",e.fill),h.text(u)}else l.text(u);void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0&&(r+=(l._groups||l)[0][0].getBBox().height,n=r),a.push(l)}return a},jn=function(t,e){var n,r,i,a,o,s=t.append("polygon");return s.attr("points",(n=e.x,r=e.y,i=e.width,a=e.height,n+","+r+" "+(n+i)+","+r+" "+(n+i)+","+(r+a-(o=7))+" "+(n+i-1.2*o)+","+(r+a)+" "+n+","+(r+a))),s.attr("class","labelBox"),e.y=e.y+e.height/2,Fn(t,e),s},Rn=-1,Yn=function(){return{x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}},zn=function(){return{x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0}},Un=function(){function t(t,e,n,i,a,o,s){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c){for(var u=c.actorFontSize,l=c.actorFontFamily,h=c.actorFontWeight,f=t.split(x.lineBreakRegex),d=0;d<f.length;d++){var p=d*u-u*(f.length-1)/2,g=e.append("text").attr("x",n+a/2).attr("y",i).style("text-anchor","middle").style("font-size",u).style("font-weight",h).style("font-family",l);g.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),g.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(g,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).append("div").style("display","table").style("height","100%").style("width","100%");h.append("div").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)e.hasOwnProperty(n)&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}(),$n={drawRect:In,drawText:Fn,drawLabel:jn,drawActor:function(t,e,n){var r=e.x+e.width/2,i=t.append("g");0===e.y&&(Rn++,i.append("line").attr("id","actor"+Rn).attr("x1",r).attr("y1",5).attr("x2",r).attr("y2",2e3).attr("class","actor-line").attr("stroke-width","0.5px").attr("stroke","#999"));var a=zn();a.x=e.x,a.y=e.y,a.fill="#eaeaea",a.width=e.width,a.height=e.height,a.class="actor",a.rx=3,a.ry=3,In(i,a),Un(n)(e.description,i,a.x,a.y,a.width,a.height,{class:"actor"},n)},anchorElement:function(t){return t.append("g")},drawActivation:function(t,e,n,r,i){var a=zn(),o=e.anchored;a.x=e.startx,a.y=e.starty,a.class="activation"+i%3,a.width=e.stopx-e.startx,a.height=n-e.starty,In(o,a)},drawLoop:function(t,e,n,r){var i=r.boxMargin,a=r.boxTextMargin,o=r.labelBoxHeight,s=r.labelBoxWidth,c=r.messageFontFamily,u=r.messageFontSize,l=r.messageFontWeight,h=t.append("g"),f=function(t,e,n,r){return h.append("line").attr("x1",t).attr("y1",e).attr("x2",n).attr("y2",r).attr("class","loopLine")};f(e.startx,e.starty,e.stopx,e.starty),f(e.stopx,e.starty,e.stopx,e.stopy),f(e.startx,e.stopy,e.stopx,e.stopy),f(e.startx,e.starty,e.startx,e.stopy),void 0!==e.sections&&e.sections.forEach((function(t){f(e.startx,t.y,e.stopx,t.y).style("stroke-dasharray","3, 3")}));var d=Yn();d.text=n,d.x=e.startx,d.y=e.starty,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.anchor="middle",d.valign="middle",d.tspan=!1,d.width=s||50,d.height=o||20,d.textMargin=a,d.class="labelText",jn(h,d),(d=Yn()).text=e.title,d.x=e.startx+s/2+(e.stopx-e.startx)/2,d.y=e.starty+i+a,d.anchor="middle",d.valign="middle",d.textMargin=a,d.class="loopText",d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=!0;var p=Fn(h,d);return void 0!==e.sectionTitles&&e.sectionTitles.forEach((function(t,n){if(t.message){d.text=t.message,d.x=e.startx+(e.stopx-e.startx)/2,d.y=e.sections[n].y+i+a,d.class="loopText",d.anchor="middle",d.valign="middle",d.tspan=!1,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=e.wrap,p=Fn(h,d);var r=Math.round(p.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));e.sections[n].height+=r-(i+a)}})),e.height=Math.round(e.stopy-e.starty),h},drawBackgroundRect:function(t,e){In(t,{x:e.startx,y:e.starty,width:e.stopx-e.startx,height:e.stopy-e.starty,fill:e.fill,class:"rect"}).lower()},insertArrowHead:function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z")},insertArrowFilledHead:function(t){t.append("defs").append("marker").attr("id","filled-head").attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},insertSequenceNumber:function(t){t.append("defs").append("marker").attr("id","sequencenumber").attr("refX",15).attr("refY",15).attr("markerWidth",60).attr("markerHeight",40).attr("orient","auto").append("circle").attr("cx",15).attr("cy",15).attr("r",6)},insertArrowCrossHead:function(t){var e=t.append("defs").append("marker").attr("id","crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",16).attr("refY",4);e.append("path").attr("fill","black").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 9,2 V 6 L16,4 Z"),e.append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 0,1 L 6,7 M 6,1 L 0,7")},getTextObj:Yn,getNoteRect:zn},Wn=n(2),Hn=n.n(Wn),Vn=void 0,Gn={},qn=[],Xn=[],Zn="",Jn=!1,Kn=!1,Qn=!1,tr=function(t,e,n){var r=Gn[t];r&&e===r.name&&null==n||(null!=n&&null!=n.text||(n={text:e,wrap:null}),Gn[t]={name:e,description:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,prevActor:Vn},Vn&&Gn[Vn]&&(Gn[Vn].nextActor=t),Vn=t)},er=function(t){var e,n=0;for(e=0;e<qn.length;e++)qn[e].type===ir.ACTIVE_START&&qn[e].from.actor===t&&n++,qn[e].type===ir.ACTIVE_END&&qn[e].from.actor===t&&n--;return n},nr=function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{text:void 0,wrap:void 0},r=arguments.length>3?arguments[3]:void 0;if(r===ir.ACTIVE_END){var i=er(t.actor);if(i<1){var a=new Error("Trying to inactivate an inactive participant ("+t.actor+")");throw a.hash={text:"->>-",token:"->>-",line:"1",loc:{first_line:1,last_line:1,first_column:1,last_column:1},expected:["'ACTIVE_PARTICIPANT'"]},a}}return qn.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,type:r}),!0},rr=function(){return Qn},ir={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25},ar=function(t,e,n){var r={actor:t,placement:e,message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap},i=[].concat(t,t);Xn.push(r),qn.push({from:i[0],to:i[1],message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,type:ir.NOTE,placement:e})},or=function(t){Zn=t.text,Jn=void 0===t.wrap&&rr()||!!t.wrap},sr={addActor:tr,addMessage:function(t,e,n,r){qn.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,answer:r})},addSignal:nr,autoWrap:rr,setWrap:function(t){Qn=t},enableSequenceNumbers:function(){Kn=!0},showSequenceNumbers:function(){return Kn},getMessages:function(){return qn},getActors:function(){return Gn},getActor:function(t){return Gn[t]},getActorKeys:function(){return Object.keys(Gn)},getTitle:function(){return Zn},parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().sequence},getTitleWrapped:function(){return Jn},clear:function(){Gn={},qn=[]},parseMessage:function(t){var e=t.trim(),n={text:e.replace(/^[:]?(?:no)?wrap:/,"").trim(),wrap:null!==e.match(/^[:]?wrap:/)||null===e.match(/^[:]?nowrap:/)&&void 0};return c.debug("parseMessage:",n),n},LINETYPE:ir,ARROWTYPE:{FILLED:0,OPEN:1},PLACEMENT:{LEFTOF:0,RIGHTOF:1,OVER:2},addNote:ar,setTitle:or,apply:function t(e){if(e instanceof Array)e.forEach((function(e){t(e)}));else switch(e.type){case"addActor":tr(e.actor,e.actor,e.description);break;case"activeStart":case"activeEnd":nr(e.actor,void 0,void 0,e.signalType);break;case"addNote":ar(e.actor,e.placement,e.text);break;case"addMessage":nr(e.from,e.to,e.msg,e.signalType);break;case"loopStart":nr(void 0,void 0,e.loopText,e.signalType);break;case"loopEnd":nr(void 0,void 0,void 0,e.signalType);break;case"rectStart":nr(void 0,void 0,e.color,e.signalType);break;case"rectEnd":nr(void 0,void 0,void 0,e.signalType);break;case"optStart":nr(void 0,void 0,e.optText,e.signalType);break;case"optEnd":nr(void 0,void 0,void 0,e.signalType);break;case"altStart":case"else":nr(void 0,void 0,e.altText,e.signalType);break;case"altEnd":nr(void 0,void 0,void 0,e.signalType);break;case"setTitle":or(e.text);break;case"parStart":case"and":nr(void 0,void 0,e.parText,e.signalType);break;case"parEnd":nr(void 0,void 0,void 0,e.signalType)}}};Wn.parser.yy=sr;var cr={},ur={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],activations:[],models:{getHeight:function(){return Math.max.apply(null,0===this.actors.length?[0]:this.actors.map((function(t){return t.height||0})))+(0===this.loops.length?0:this.loops.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.messages.length?0:this.messages.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.notes.length?0:this.notes.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))},clear:function(){this.actors=[],this.loops=[],this.messages=[],this.notes=[]},addActor:function(t){this.actors.push(t)},addLoop:function(t){this.loops.push(t)},addMessage:function(t){this.messages.push(t)},addNote:function(t){this.notes.push(t)},lastActor:function(){return this.actors[this.actors.length-1]},lastLoop:function(){return this.loops[this.loops.length-1]},lastMessage:function(){return this.messages[this.messages.length-1]},lastNote:function(){return this.notes[this.notes.length-1]},actors:[],loops:[],messages:[],notes:[]},init:function(){this.sequenceItems=[],this.activations=[],this.models.clear(),this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0,pr(Wn.parser.yy.getConfig())},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i=this,a=0;function o(o){return function(s){a++;var c=i.sequenceItems.length-a+1;i.updateVal(s,"starty",e-c*cr.boxMargin,Math.min),i.updateVal(s,"stopy",r+c*cr.boxMargin,Math.max),i.updateVal(ur.data,"startx",t-c*cr.boxMargin,Math.min),i.updateVal(ur.data,"stopx",n+c*cr.boxMargin,Math.max),"activation"!==o&&(i.updateVal(s,"startx",t-c*cr.boxMargin,Math.min),i.updateVal(s,"stopx",n+c*cr.boxMargin,Math.max),i.updateVal(ur.data,"starty",e-c*cr.boxMargin,Math.min),i.updateVal(ur.data,"stopy",r+c*cr.boxMargin,Math.max))}}this.sequenceItems.forEach(o()),this.activations.forEach(o("activation"))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(ur.data,"startx",i,Math.min),this.updateVal(ur.data,"starty",o,Math.min),this.updateVal(ur.data,"stopx",a,Math.max),this.updateVal(ur.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},newActivation:function(t,e,n){var r=n[t.from.actor],i=gr(t.from.actor).length||0,a=r.x+r.width/2+(i-1)*cr.activationWidth/2;this.activations.push({startx:a,starty:this.verticalPos+2,stopx:a+cr.activationWidth,stopy:void 0,actor:t.from.actor,anchored:$n.anchorElement(e)})},endActivation:function(t){var e=this.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);return this.activations.splice(e,1)[0]},createLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;return{startx:void 0,starty:this.verticalPos,stopx:void 0,stopy:void 0,title:t.message,wrap:t.wrap,width:t.width,height:0,fill:e}},newLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;this.sequenceItems.push(this.createLoop(t,e))},endLoop:function(){return this.sequenceItems.pop()},addSectionToLoop:function(t){var e=this.sequenceItems.pop();e.sections=e.sections||[],e.sectionTitles=e.sectionTitles||[],e.sections.push({y:ur.getVerticalPos(),height:0}),e.sectionTitles.push(t),this.sequenceItems.push(e)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return{bounds:this.data,models:this.models}}},lr=function(t){return{fontFamily:t.messageFontFamily,fontSize:t.messageFontSize,fontWeight:t.messageFontWeight}},hr=function(t){return{fontFamily:t.noteFontFamily,fontSize:t.noteFontSize,fontWeight:t.noteFontWeight}},fr=function(t){return{fontFamily:t.actorFontFamily,fontSize:t.actorFontSize,fontWeight:t.actorFontWeight}},dr=function(t,e,n,r){for(var i=0,a=0,o=0;o<n.length;o++){var s=e[n[o]];s.width=s.width||cr.width,s.height=Math.max(s.height||cr.height,cr.height),s.margin=s.margin||cr.actorMargin,s.x=i+a,s.y=r,$n.drawActor(t,s,cr),ur.insert(s.x,r,s.x+s.width,s.height),i+=s.width,a+=s.margin,ur.models.addActor(s)}ur.bumpVerticalPos(cr.height)},pr=function(t){F(cr,t),t.fontFamily&&(cr.actorFontFamily=cr.noteFontFamily=cr.messageFontFamily=t.fontFamily),t.fontSize&&(cr.actorFontSize=cr.noteFontSize=cr.messageFontSize=t.fontSize),t.fontWeight&&(cr.actorFontWeight=cr.noteFontWeight=cr.messageFontWeight=t.fontWeight)},gr=function(t){return ur.activations.filter((function(e){return e.actor===t}))},yr=function(t,e){var n=e[t],r=gr(t);return[r.reduce((function(t,e){return Math.min(t,e.startx)}),n.x+n.width/2),r.reduce((function(t,e){return Math.max(t,e.stopx)}),n.x+n.width/2)]};function vr(t,e,n,r,i){ur.bumpVerticalPos(n);var a=r;if(e.id&&e.message&&t[e.id]){var o=t[e.id].width,s=lr(cr);e.message=H.wrapLabel("[".concat(e.message,"]"),o-2*cr.wrapPadding,s),e.width=o,e.wrap=!0;var u=H.calculateTextDimensions(e.message,s),l=Math.max(u.height,cr.labelBoxHeight);a=r+l,c.debug("".concat(l," - ").concat(e.message))}i(e),ur.bumpVerticalPos(a)}var mr=function(t,e){var n={};return e.forEach((function(e){if(t[e.to]&&t[e.from]){var r=t[e.to];if(e.placement===Wn.parser.yy.PLACEMENT.LEFTOF&&!r.prevActor)return;if(e.placement===Wn.parser.yy.PLACEMENT.RIGHTOF&&!r.nextActor)return;var i=void 0!==e.placement,a=!i,o=i?hr(cr):lr(cr),s=e.wrap?H.wrapLabel(e.message,cr.width-2*cr.wrapPadding,o):e.message,c=H.calculateTextDimensions(s,o).width+2*cr.wrapPadding;a&&e.from===r.nextActor?n[e.to]=Math.max(n[e.to]||0,c):a&&e.from===r.prevActor?n[e.from]=Math.max(n[e.from]||0,c):a&&e.from===e.to?(n[e.from]=Math.max(n[e.from]||0,c/2),n[e.to]=Math.max(n[e.to]||0,c/2)):e.placement===Wn.parser.yy.PLACEMENT.RIGHTOF?n[e.from]=Math.max(n[e.from]||0,c):e.placement===Wn.parser.yy.PLACEMENT.LEFTOF?n[r.prevActor]=Math.max(n[r.prevActor]||0,c):e.placement===Wn.parser.yy.PLACEMENT.OVER&&(r.prevActor&&(n[r.prevActor]=Math.max(n[r.prevActor]||0,c/2)),r.nextActor&&(n[e.from]=Math.max(n[e.from]||0,c/2)))}})),c.debug("maxMessageWidthPerActor:",n),n},br=function(t,e){var n=0;for(var r in Object.keys(t).forEach((function(e){var r=t[e];r.wrap&&(r.description=H.wrapLabel(r.description,cr.width-2*cr.wrapPadding,fr(cr)));var i=H.calculateTextDimensions(r.description,fr(cr));r.width=r.wrap?cr.width:Math.max(cr.width,i.width+2*cr.wrapPadding),r.height=r.wrap?Math.max(i.height,cr.height):cr.height,n=Math.max(n,r.height)})),e){var i=t[r];if(i){var a=t[i.nextActor];if(a){var o=e[r]+cr.actorMargin-i.width/2-a.width/2;i.margin=Math.max(o,cr.actorMargin)}}}return Math.max(n,cr.height)},xr=function(t,e){var n,r,i,a={},o=[];return t.forEach((function(t){switch(t.id=H.random({length:10}),t.type){case Wn.parser.yy.LINETYPE.LOOP_START:case Wn.parser.yy.LINETYPE.ALT_START:case Wn.parser.yy.LINETYPE.OPT_START:case Wn.parser.yy.LINETYPE.PAR_START:o.push({id:t.id,msg:t.message,from:Number.MAX_SAFE_INTEGER,to:Number.MIN_SAFE_INTEGER,width:0});break;case Wn.parser.yy.LINETYPE.ALT_ELSE:case Wn.parser.yy.LINETYPE.PAR_AND:t.message&&(n=o.pop(),a[n.id]=n,a[t.id]=n,o.push(n));break;case Wn.parser.yy.LINETYPE.LOOP_END:case Wn.parser.yy.LINETYPE.ALT_END:case Wn.parser.yy.LINETYPE.OPT_END:case Wn.parser.yy.LINETYPE.PAR_END:n=o.pop(),a[n.id]=n;break;case Wn.parser.yy.LINETYPE.ACTIVE_START:var s=e[t.from?t.from.actor:t.to.actor],u=gr(t.from?t.from.actor:t.to.actor).length,l=s.x+s.width/2+(u-1)*cr.activationWidth/2,h={startx:l,stopx:l+cr.activationWidth,actor:t.from.actor,enabled:!0};ur.activations.push(h);break;case Wn.parser.yy.LINETYPE.ACTIVE_END:var f=ur.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);delete ur.activations.splice(f,1)[0]}void 0!==t.placement?(r=function(t,e){var n=e[t.from].x,r=e[t.to].x,i=t.wrap&&t.message,a=H.calculateTextDimensions(i?H.wrapLabel(t.message,cr.width,hr(cr)):t.message,hr(cr)),o={width:i?cr.width:Math.max(cr.width,a.width+2*cr.noteMargin),height:0,startx:e[t.from].x,stopx:0,starty:0,stopy:0,message:t.message};return t.placement===Wn.parser.yy.PLACEMENT.RIGHTOF?(o.width=i?Math.max(cr.width,a.width):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*cr.noteMargin),o.startx=n+(e[t.from].width+cr.actorMargin)/2):t.placement===Wn.parser.yy.PLACEMENT.LEFTOF?(o.width=i?Math.max(cr.width,a.width+2*cr.noteMargin):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*cr.noteMargin),o.startx=n-o.width+(e[t.from].width-cr.actorMargin)/2):t.to===t.from?(a=H.calculateTextDimensions(i?H.wrapLabel(t.message,Math.max(cr.width,e[t.from].width),hr(cr)):t.message,hr(cr)),o.width=i?Math.max(cr.width,e[t.from].width):Math.max(e[t.from].width,cr.width,a.width+2*cr.noteMargin),o.startx=n+(e[t.from].width-o.width)/2):(o.width=Math.abs(n+e[t.from].width/2-(r+e[t.to].width/2))+cr.actorMargin,o.startx=n<r?n+e[t.from].width/2-cr.actorMargin/2:r+e[t.to].width/2-cr.actorMargin/2),i&&(o.message=H.wrapLabel(t.message,o.width-2*cr.wrapPadding,hr(cr))),c.debug("NM:[".concat(o.startx,",").concat(o.stopx,",").concat(o.starty,",").concat(o.stopy,":").concat(o.width,",").concat(o.height,"=").concat(t.message,"]")),o}(t,e),t.noteModel=r,o.forEach((function(t){(n=t).from=Math.min(n.from,r.startx),n.to=Math.max(n.to,r.startx+r.width),n.width=Math.max(n.width,Math.abs(n.from-n.to))-cr.labelBoxWidth}))):(i=function(t,e){var n=!1;if([Wn.parser.yy.LINETYPE.SOLID_OPEN,Wn.parser.yy.LINETYPE.DOTTED_OPEN,Wn.parser.yy.LINETYPE.SOLID,Wn.parser.yy.LINETYPE.DOTTED,Wn.parser.yy.LINETYPE.SOLID_CROSS,Wn.parser.yy.LINETYPE.DOTTED_CROSS,Wn.parser.yy.LINETYPE.SOLID_POINT,Wn.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&(n=!0),!n)return{};var r=yr(t.from,e),i=yr(t.to,e),a=r[0]<=i[0]?1:0,o=r[0]<i[0]?0:1,s=r.concat(i),c=Math.abs(i[o]-r[a]);t.wrap&&t.message&&(t.message=H.wrapLabel(t.message,Math.max(c+2*cr.wrapPadding,cr.width),lr(cr)));var u=H.calculateTextDimensions(t.message,lr(cr));return{width:Math.max(t.wrap?0:u.width+2*cr.wrapPadding,c+2*cr.wrapPadding,cr.width),height:0,startx:r[a],stopx:i[o],starty:0,stopy:0,message:t.message,type:t.type,wrap:t.wrap,fromBounds:Math.min.apply(null,s),toBounds:Math.max.apply(null,s)}}(t,e),t.msgModel=i,i.startx&&i.stopx&&o.length>0&&o.forEach((function(r){if(n=r,i.startx===i.stopx){var a=e[t.from],o=e[t.to];n.from=Math.min(a.x-i.width/2,a.x-a.width/2,n.from),n.to=Math.max(o.x+i.width/2,o.x+a.width/2,n.to),n.width=Math.max(n.width,Math.abs(n.to-n.from))-cr.labelBoxWidth}else n.from=Math.min(i.startx,n.from),n.to=Math.max(i.stopx,n.to),n.width=Math.max(n.width,i.width)-cr.labelBoxWidth})))})),ur.activations=[],c.debug("Loop type widths:",a),a},_r={bounds:ur,drawActors:dr,setConf:pr,draw:function(t,e){cr=_t().sequence,Wn.parser.yy.clear(),Wn.parser.yy.setWrap(cr.wrap),Wn.parser.parse(t+"\n"),ur.init(),c.debug("C:".concat(JSON.stringify(cr,null,2)));var n=Object(d.select)('[id="'.concat(e,'"]')),r=Wn.parser.yy.getActors(),i=Wn.parser.yy.getActorKeys(),a=Wn.parser.yy.getMessages(),o=Wn.parser.yy.getTitle(),s=mr(r,a);cr.height=br(r,s),dr(n,r,i,0);var u=xr(a,r,s);$n.insertArrowHead(n),$n.insertArrowCrossHead(n),$n.insertArrowFilledHead(n),$n.insertSequenceNumber(n);var l=1;a.forEach((function(t){var e,i,a;switch(t.type){case Wn.parser.yy.LINETYPE.NOTE:i=t.noteModel,function(t,e){ur.bumpVerticalPos(cr.boxMargin),e.height=cr.boxMargin,e.starty=ur.getVerticalPos();var n=$n.getNoteRect();n.x=e.startx,n.y=e.starty,n.width=e.width||cr.width,n.class="note";var r=t.append("g"),i=$n.drawRect(r,n),a=$n.getTextObj();a.x=e.startx,a.y=e.starty,a.width=n.width,a.dy="1em",a.text=e.message,a.class="noteText",a.fontFamily=cr.noteFontFamily,a.fontSize=cr.noteFontSize,a.fontWeight=cr.noteFontWeight,a.anchor=cr.noteAlign,a.textMargin=cr.noteMargin,a.valign=cr.noteAlign;var o=Fn(r,a),s=Math.round(o.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));i.attr("height",s+2*cr.noteMargin),e.height+=s+2*cr.noteMargin,ur.bumpVerticalPos(s+2*cr.noteMargin),e.stopy=e.starty+s+2*cr.noteMargin,e.stopx=e.startx+n.width,ur.insert(e.startx,e.starty,e.stopx,e.stopy),ur.models.addNote(e)}(n,i);break;case Wn.parser.yy.LINETYPE.ACTIVE_START:ur.newActivation(t,n,r);break;case Wn.parser.yy.LINETYPE.ACTIVE_END:!function(t,e){var r=ur.endActivation(t);r.starty+18>e&&(r.starty=e-6,e+=12),$n.drawActivation(n,r,e,cr,gr(t.from.actor).length),ur.insert(r.startx,e-10,r.stopx,e)}(t,ur.getVerticalPos());break;case Wn.parser.yy.LINETYPE.LOOP_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.LOOP_END:e=ur.endLoop(),$n.drawLoop(n,e,"loop",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;case Wn.parser.yy.LINETYPE.RECT_START:vr(u,t,cr.boxMargin,cr.boxMargin,(function(t){return ur.newLoop(void 0,t.message)}));break;case Wn.parser.yy.LINETYPE.RECT_END:e=ur.endLoop(),$n.drawBackgroundRect(n,e),ur.models.addLoop(e),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos());break;case Wn.parser.yy.LINETYPE.OPT_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.OPT_END:e=ur.endLoop(),$n.drawLoop(n,e,"opt",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;case Wn.parser.yy.LINETYPE.ALT_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.ALT_ELSE:vr(u,t,cr.boxMargin+cr.boxTextMargin,cr.boxMargin,(function(t){return ur.addSectionToLoop(t)}));break;case Wn.parser.yy.LINETYPE.ALT_END:e=ur.endLoop(),$n.drawLoop(n,e,"alt",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;case Wn.parser.yy.LINETYPE.PAR_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.PAR_AND:vr(u,t,cr.boxMargin+cr.boxTextMargin,cr.boxMargin,(function(t){return ur.addSectionToLoop(t)}));break;case Wn.parser.yy.LINETYPE.PAR_END:e=ur.endLoop(),$n.drawLoop(n,e,"par",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;default:try{(a=t.msgModel).starty=ur.getVerticalPos(),a.sequenceIndex=l,function(t,e){ur.bumpVerticalPos(10);var n=e.startx,r=e.stopx,i=e.starty,a=e.message,o=e.type,s=e.sequenceIndex,c=x.splitBreaks(a).length,u=H.calculateTextDimensions(a,lr(cr)),l=u.height/c;e.height+=l,ur.bumpVerticalPos(l);var h=$n.getTextObj();h.x=n,h.y=i+10,h.width=r-n,h.class="messageText",h.dy="1em",h.text=a,h.fontFamily=cr.messageFontFamily,h.fontSize=cr.messageFontSize,h.fontWeight=cr.messageFontWeight,h.anchor=cr.messageAlign,h.valign=cr.messageAlign,h.textMargin=cr.wrapPadding,h.tspan=!1,Fn(t,h);var f,d,p=u.height-10,g=u.width;if(n===r){d=ur.getVerticalPos()+p,cr.rightAngles?f=t.append("path").attr("d","M ".concat(n,",").concat(d," H ").concat(n+Math.max(cr.width/2,g/2)," V ").concat(d+25," H ").concat(n)):(p+=cr.boxMargin,d=ur.getVerticalPos()+p,f=t.append("path").attr("d","M "+n+","+d+" C "+(n+60)+","+(d-10)+" "+(n+60)+","+(d+30)+" "+n+","+(d+20))),p+=30;var y=Math.max(g/2,cr.width/2);ur.insert(n-y,ur.getVerticalPos()-10+p,r+y,ur.getVerticalPos()+30+p)}else p+=cr.boxMargin,d=ur.getVerticalPos()+p,(f=t.append("line")).attr("x1",n),f.attr("y1",d),f.attr("x2",r),f.attr("y2",d),ur.insert(n,d-10,r,d);o===Wn.parser.yy.LINETYPE.DOTTED||o===Wn.parser.yy.LINETYPE.DOTTED_CROSS||o===Wn.parser.yy.LINETYPE.DOTTED_POINT||o===Wn.parser.yy.LINETYPE.DOTTED_OPEN?(f.style("stroke-dasharray","3, 3"),f.attr("class","messageLine1")):f.attr("class","messageLine0");var v="";cr.arrowMarkerAbsolute&&(v=(v=(v=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),f.attr("stroke-width",2),f.attr("stroke","none"),f.style("fill","none"),o!==Wn.parser.yy.LINETYPE.SOLID&&o!==Wn.parser.yy.LINETYPE.DOTTED||f.attr("marker-end","url("+v+"#arrowhead)"),o!==Wn.parser.yy.LINETYPE.SOLID_POINT&&o!==Wn.parser.yy.LINETYPE.DOTTED_POINT||f.attr("marker-end","url("+v+"#filled-head)"),o!==Wn.parser.yy.LINETYPE.SOLID_CROSS&&o!==Wn.parser.yy.LINETYPE.DOTTED_CROSS||f.attr("marker-end","url("+v+"#crosshead)"),(sr.showSequenceNumbers()||cr.showSequenceNumbers)&&(f.attr("marker-start","url("+v+"#sequencenumber)"),t.append("text").attr("x",n).attr("y",d+4).attr("font-family","sans-serif").attr("font-size","12px").attr("text-anchor","middle").attr("textLength","16px").attr("class","sequenceNumber").text(s)),ur.bumpVerticalPos(p),e.height+=p,e.stopy=e.starty+e.height,ur.insert(e.fromBounds,e.starty,e.toBounds,e.stopy)}(n,a),ur.models.addMessage(a)}catch(t){c.error("error while drawing message",t)}}[Wn.parser.yy.LINETYPE.SOLID_OPEN,Wn.parser.yy.LINETYPE.DOTTED_OPEN,Wn.parser.yy.LINETYPE.SOLID,Wn.parser.yy.LINETYPE.DOTTED,Wn.parser.yy.LINETYPE.SOLID_CROSS,Wn.parser.yy.LINETYPE.DOTTED_CROSS,Wn.parser.yy.LINETYPE.SOLID_POINT,Wn.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&l++})),cr.mirrorActors&&(ur.bumpVerticalPos(2*cr.boxMargin),dr(n,r,i,ur.getVerticalPos()));var h=ur.getBounds().bounds;c.debug("For line height fix Querying: #"+e+" .actor-line"),Object(d.selectAll)("#"+e+" .actor-line").attr("y2",h.stopy);var f=h.stopy-h.starty+2*cr.diagramMarginY;cr.mirrorActors&&(f=f-cr.boxMargin+cr.bottomMarginAdj);var p=h.stopx-h.startx+2*cr.diagramMarginX;o&&n.append("text").text(o).attr("x",(h.stopx-h.startx)/2-2*cr.diagramMarginX).attr("y",-25),W(n,f,p,cr.useMaxWidth);var g=o?40:0;n.attr("viewBox",h.startx-cr.diagramMarginX+" -"+(cr.diagramMarginY+g)+" "+p+" "+(f+g)),c.debug("models:",ur.models)}},kr=n(27),wr=n.n(kr);function Er(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var Tr,Cr,Ar="",Sr="",Mr="",Or=[],Dr="",Nr=[],Br=[],Lr="",Pr=["active","done","crit","milestone"],Ir=[],Fr=!1,jr=0,Rr=function(t,e,n){return t.isoWeekday()>=6&&n.indexOf("weekends")>=0||(n.indexOf(t.format("dddd").toLowerCase())>=0||n.indexOf(t.format(e.trim()))>=0)},Yr=function(t,e,n){if(n.length&&!t.manualEndTime){var r=o()(t.startTime,e,!0);r.add(1,"d");var i=o()(t.endTime,e,!0),a=zr(r,i,e,n);t.endTime=i.toDate(),t.renderEndTime=a}},zr=function(t,e,n,r){for(var i=!1,a=null;t<=e;)i||(a=e.toDate()),(i=Rr(t,n,r))&&e.add(1,"d"),t.add(1,"d");return a},Ur=function(t,e,n){n=n.trim();var r=/^after\s+([\d\w- ]+)/.exec(n.trim());if(null!==r){var i=null;if(r[1].split(" ").forEach((function(t){var e=Xr(t);void 0!==e&&(i?e.endTime>i.endTime&&(i=e):i=e)})),i)return i.endTime;var a=new Date;return a.setHours(0,0,0,0),a}var s=o()(n,e.trim(),!0);return s.isValid()?s.toDate():(c.debug("Invalid date:"+n),c.debug("With date format:"+e.trim()),new Date)},$r=function(t,e){if(null!==t)switch(t[2]){case"s":e.add(t[1],"seconds");break;case"m":e.add(t[1],"minutes");break;case"h":e.add(t[1],"hours");break;case"d":e.add(t[1],"days");break;case"w":e.add(t[1],"weeks")}return e.toDate()},Wr=function(t,e,n,r){r=r||!1,n=n.trim();var i=o()(n,e.trim(),!0);return i.isValid()?(r&&i.add(1,"d"),i.toDate()):$r(/^([\d]+)([wdhms])/.exec(n.trim()),o()(t))},Hr=0,Vr=function(t){return void 0===t?"task"+(Hr+=1):t},Gr=[],qr={},Xr=function(t){var e=qr[t];return Gr[e]},Zr=function(){for(var t=function(t){var e=Gr[t],n="";switch(Gr[t].raw.startTime.type){case"prevTaskEnd":var r=Xr(e.prevTaskId);e.startTime=r.endTime;break;case"getStartDate":(n=Ur(0,Ar,Gr[t].raw.startTime.startData))&&(Gr[t].startTime=n)}return Gr[t].startTime&&(Gr[t].endTime=Wr(Gr[t].startTime,Ar,Gr[t].raw.endTime.data,Fr),Gr[t].endTime&&(Gr[t].processed=!0,Gr[t].manualEndTime=o()(Gr[t].raw.endTime.data,"YYYY-MM-DD",!0).isValid(),Yr(Gr[t],Ar,Or))),Gr[t].processed},e=!0,n=0;n<Gr.length;n++)t(n),e=e&&Gr[n].processed;return e},Jr=function(t,e){t.split(",").forEach((function(t){var n=Xr(t);void 0!==n&&n.classes.push(e)}))},Kr=function(t,e){Ir.push((function(){var n=document.querySelector('[id="'.concat(t,'"]'));null!==n&&n.addEventListener("click",(function(){e()}))})),Ir.push((function(){var n=document.querySelector('[id="'.concat(t,'-text"]'));null!==n&&n.addEventListener("click",(function(){e()}))}))},Qr={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().gantt},clear:function(){Nr=[],Br=[],Lr="",Ir=[],Dr="",Hr=0,Tr=void 0,Cr=void 0,Gr=[],Ar="",Sr="",Mr="",Or=[],Fr=!1,jr=0},setDateFormat:function(t){Ar=t},getDateFormat:function(){return Ar},enableInclusiveEndDates:function(){Fr=!0},endDatesAreInclusive:function(){return Fr},setAxisFormat:function(t){Sr=t},getAxisFormat:function(){return Sr},setTodayMarker:function(t){Mr=t},getTodayMarker:function(){return Mr},setTitle:function(t){Dr=t},getTitle:function(){return Dr},addSection:function(t){Lr=t,Nr.push(t)},getSections:function(){return Nr},getTasks:function(){for(var t=Zr(),e=0;!t&&e<10;)t=Zr(),e++;return Br=Gr},addTask:function(t,e){var n={section:Lr,type:Lr,processed:!1,manualEndTime:!1,renderEndTime:null,raw:{data:e},task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};ti(n,r,Pr);for(var i=0;i<n.length;i++)n[i]=n[i].trim();switch(n.length){case 1:r.id=Vr(),r.startTime={type:"prevTaskEnd",id:t},r.endTime={data:n[0]};break;case 2:r.id=Vr(),r.startTime={type:"getStartDate",startData:n[0]},r.endTime={data:n[1]};break;case 3:r.id=Vr(n[0]),r.startTime={type:"getStartDate",startData:n[1]},r.endTime={data:n[2]}}return r}(Cr,e);n.raw.startTime=r.startTime,n.raw.endTime=r.endTime,n.id=r.id,n.prevTaskId=Cr,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,n.order=jr,jr++;var i=Gr.push(n);Cr=n.id,qr[n.id]=i-1},findTaskById:Xr,addTaskOrg:function(t,e){var n={section:Lr,type:Lr,description:t,task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};ti(n,r,Pr);for(var i=0;i<n.length;i++)n[i]=n[i].trim();var a="";switch(n.length){case 1:r.id=Vr(),r.startTime=t.endTime,a=n[0];break;case 2:r.id=Vr(),r.startTime=Ur(0,Ar,n[0]),a=n[1];break;case 3:r.id=Vr(n[0]),r.startTime=Ur(0,Ar,n[1]),a=n[2]}return a&&(r.endTime=Wr(r.startTime,Ar,a,Fr),r.manualEndTime=o()(a,"YYYY-MM-DD",!0).isValid(),Yr(r,Ar,Or)),r}(Tr,e);n.startTime=r.startTime,n.endTime=r.endTime,n.id=r.id,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,Tr=n,Br.push(n)},setExcludes:function(t){Or=t.toLowerCase().split(/[\s,]+/)},getExcludes:function(){return Or},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){if("loose"===_t().securityLevel&&void 0!==e){var r=[];if("string"==typeof n){r=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var i=0;i<r.length;i++){var a=r[i].trim();'"'===a.charAt(0)&&'"'===a.charAt(a.length-1)&&(a=a.substr(1,a.length-2)),r[i]=a}}0===r.length&&r.push(t),void 0!==Xr(t)&&Kr(t,(function(){H.runFunc.apply(H,[e].concat(Er(r)))}))}}(t,e,n)})),Jr(t,"clickable")},setLink:function(t,e){var n=e;"loose"!==_t().securityLevel&&(n=Object(g.sanitizeUrl)(e)),t.split(",").forEach((function(t){void 0!==Xr(t)&&Kr(t,(function(){window.open(n,"_self")}))})),Jr(t,"clickable")},bindFunctions:function(t){Ir.forEach((function(e){e(t)}))},durationToDate:$r};function ti(t,e,n){for(var r=!0;r;)r=!1,n.forEach((function(n){var i=new RegExp("^\\s*"+n+"\\s*$");t[0].match(i)&&(e[n]=!0,t.shift(1),r=!0)}))}kr.parser.yy=Qr;var ei,ni={titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,rightPadding:75,leftPadding:75,gridLineStartPadding:35,fontSize:11,fontFamily:'"Open-Sans", "sans-serif"'},ri=function(t){Object.keys(t).forEach((function(e){ni[e]=t[e]}))},ii=function(t,e){kr.parser.yy.clear(),kr.parser.parse(t);var n=document.getElementById(e);void 0===(ei=n.parentElement.offsetWidth)&&(ei=1200),void 0!==ni.useWidth&&(ei=ni.useWidth);var r=kr.parser.yy.getTasks(),i=r.length*(ni.barHeight+ni.barGap)+2*ni.topPadding;n.setAttribute("viewBox","0 0 "+ei+" "+i);for(var a=Object(d.select)('[id="'.concat(e,'"]')),o=Object(d.scaleTime)().domain([Object(d.min)(r,(function(t){return t.startTime})),Object(d.max)(r,(function(t){return t.endTime}))]).rangeRound([0,ei-ni.leftPadding-ni.rightPadding]),s=[],c=0;c<r.length;c++)s.push(r[c].type);var u=s;function l(t){for(var e=t.length,n={};e;)n[t[--e]]=(n[t[e]]||0)+1;return n}s=function(t){for(var e={},n=[],r=0,i=t.length;r<i;++r)e.hasOwnProperty(t[r])||(e[t[r]]=!0,n.push(t[r]));return n}(s),r.sort((function(t,e){var n=t.startTime,r=e.startTime,i=0;return n>r?i=1:n<r&&(i=-1),i})),function(t,e,n){var r=ni.barHeight,i=r+ni.barGap,c=ni.topPadding,h=ni.leftPadding;Object(d.scaleLinear)().domain([0,s.length]).range(["#00B9FA","#F95002"]).interpolate(d.interpolateHcl);(function(t,e,n,r){var i=Object(d.axisBottom)(o).tickSize(-r+e+ni.gridLineStartPadding).tickFormat(Object(d.timeFormat)(kr.parser.yy.getAxisFormat()||ni.axisFormat||"%Y-%m-%d"));a.append("g").attr("class","grid").attr("transform","translate("+t+", "+(r-50)+")").call(i).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10).attr("dy","1em")})(h,c,0,n),function(t,e,n,r,i,c,u){a.append("g").selectAll("rect").data(t).enter().append("rect").attr("x",0).attr("y",(function(t,r){return t.order*e+n-2})).attr("width",(function(){return u-ni.rightPadding/2})).attr("height",e).attr("class",(function(t){for(var e=0;e<s.length;e++)if(t.type===s[e])return"section section"+e%ni.numberSectionStyles;return"section section0"}));var l=a.append("g").selectAll("rect").data(t).enter();l.append("rect").attr("id",(function(t){return t.id})).attr("rx",3).attr("ry",3).attr("x",(function(t){return t.milestone?o(t.startTime)+r+.5*(o(t.endTime)-o(t.startTime))-.5*i:o(t.startTime)+r})).attr("y",(function(t,r){return t.order*e+n})).attr("width",(function(t){return t.milestone?i:o(t.renderEndTime||t.endTime)-o(t.startTime)})).attr("height",i).attr("transform-origin",(function(t,a){return(o(t.startTime)+r+.5*(o(t.endTime)-o(t.startTime))).toString()+"px "+(a*e+n+.5*i).toString()+"px"})).attr("class",(function(t){var e="";t.classes.length>0&&(e=t.classes.join(" "));for(var n=0,r=0;r<s.length;r++)t.type===s[r]&&(n=r%ni.numberSectionStyles);var i="";return t.active?t.crit?i+=" activeCrit":i=" active":t.done?i=t.crit?" doneCrit":" done":t.crit&&(i+=" crit"),0===i.length&&(i=" task"),t.milestone&&(i=" milestone "+i),i+=n,"task"+(i+=" "+e)})),l.append("text").attr("id",(function(t){return t.id+"-text"})).text((function(t){return t.task})).attr("font-size",ni.fontSize).attr("x",(function(t){var e=o(t.startTime),n=o(t.renderEndTime||t.endTime);t.milestone&&(e+=.5*(o(t.endTime)-o(t.startTime))-.5*i),t.milestone&&(n=e+i);var a=this.getBBox().width;return a>n-e?n+a+1.5*ni.leftPadding>u?e+r-5:n+r+5:(n-e)/2+e+r})).attr("y",(function(t,r){return t.order*e+ni.barHeight/2+(ni.fontSize/2-2)+n})).attr("text-height",i).attr("class",(function(t){var e=o(t.startTime),n=o(t.endTime);t.milestone&&(n=e+i);var r=this.getBBox().width,a="";t.classes.length>0&&(a=t.classes.join(" "));for(var c=0,l=0;l<s.length;l++)t.type===s[l]&&(c=l%ni.numberSectionStyles);var h="";return t.active&&(h=t.crit?"activeCritText"+c:"activeText"+c),t.done?h=t.crit?h+" doneCritText"+c:h+" doneText"+c:t.crit&&(h=h+" critText"+c),t.milestone&&(h+=" milestoneText"),r>n-e?n+r+1.5*ni.leftPadding>u?a+" taskTextOutsideLeft taskTextOutside"+c+" "+h:a+" taskTextOutsideRight taskTextOutside"+c+" "+h+" width-"+r:a+" taskText taskText"+c+" "+h+" width-"+r}))}(t,i,c,h,r,0,e),function(t,e){for(var n=[],r=0,i=0;i<s.length;i++)n[i]=[s[i],(o=s[i],c=u,l(c)[o]||0)];var o,c;a.append("g").selectAll("text").data(n).enter().append((function(t){var e=t[0].split(x.lineBreakRegex),n=-(e.length-1)/2,r=document.createElementNS("http://www.w3.org/2000/svg","text");r.setAttribute("dy",n+"em");for(var i=0;i<e.length;i++){var a=document.createElementNS("http://www.w3.org/2000/svg","tspan");a.setAttribute("alignment-baseline","central"),a.setAttribute("x","10"),i>0&&a.setAttribute("dy","1em"),a.textContent=e[i],r.appendChild(a)}return r})).attr("x",10).attr("y",(function(i,a){if(!(a>0))return i[1]*t/2+e;for(var o=0;o<a;o++)return r+=n[a-1][1],i[1]*t/2+r*t+e})).attr("class",(function(t){for(var e=0;e<s.length;e++)if(t[0]===s[e])return"sectionTitle sectionTitle"+e%ni.numberSectionStyles;return"sectionTitle"}))}(i,c),function(t,e,n,r){var i=Qr.getTodayMarker();if("off"===i)return;var s=a.append("g").attr("class","today"),c=new Date,u=s.append("line");u.attr("x1",o(c)+t).attr("x2",o(c)+t).attr("y1",ni.titleTopMargin).attr("y2",r-ni.titleTopMargin).attr("class","today"),""!==i&&u.attr("style",i.replace(/,/g,";"))}(h,0,0,n)}(r,ei,i),W(a,i,ei,ni.useMaxWidth),a.append("text").text(kr.parser.yy.getTitle()).attr("x",ei/2).attr("y",ni.titleTopMargin).attr("class","titleText")},ai=n(13),oi=n.n(ai);ai.parser.yy=cn;var si={},ci={dividerMargin:10,padding:5,textHeight:10},ui=function(t){for(var e=Object.keys(si),n=0;n<e.length;n++)if(si[e[n]].label===t)return e[n]},li=function(t){Object.keys(t).forEach((function(e){ci[e]=t[e]}))},hi=function(t,e){si={},ai.parser.yy.clear(),ai.parser.parse(t),c.info("Rendering diagram "+t);var n,r=Object(d.select)("[id='".concat(e,"']"));r.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),(n=r).append("defs").append("marker").attr("id","extensionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),n.append("defs").append("marker").attr("id","extensionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z"),n.append("defs").append("marker").attr("id","compositionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","compositionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z");var i=new G.a.Graph({multigraph:!0});i.setGraph({isMultiGraph:!0}),i.setDefaultEdgeLabel((function(){return{}}));for(var a=cn.getClasses(),o=Object.keys(a),s=0;s<o.length;s++){var u=a[o[s]],l=vn(r,u,ci);si[l.id]=l,i.setNode(l.id,l),c.info("Org height: "+l.height)}cn.getRelations().forEach((function(t){c.info("tjoho"+ui(t.id1)+ui(t.id2)+JSON.stringify(t)),i.setEdge(ui(t.id1),ui(t.id2),{relation:t},t.title||"DEFAULT")})),ke.a.layout(i),i.nodes().forEach((function(t){void 0!==t&&void 0!==i.node(t)&&(c.debug("Node "+t+": "+JSON.stringify(i.node(t))),Object(d.select)("#"+en(t)).attr("transform","translate("+(i.node(t).x-i.node(t).width/2)+","+(i.node(t).y-i.node(t).height/2)+" )"))})),i.edges().forEach((function(t){void 0!==t&&void 0!==i.edge(t)&&(c.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(i.edge(t))),mn(r,i.edge(t),i.edge(t).relation,ci))}));var h=r.node().getBBox(),f=h.width+40,p=h.height+40;W(r,p,f,ci.useMaxWidth);var g="".concat(h.x-20," ").concat(h.y-20," ").concat(f," ").concat(p);c.debug("viewBox ".concat(g)),r.attr("viewBox",g)};ai.parser.yy=cn;var fi={dividerMargin:10,padding:5,textHeight:10},di=function(t){Object.keys(t).forEach((function(e){fi[e]=t[e]}))},pi=function(t,e){c.info("Drawing class"),cn.clear(),ai.parser.parse(t);var n=_t().flowchart;c.info("config:",n);var r=n.nodeSpacing||50,i=n.rankSpacing||50,a=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:"TD",nodesep:r,ranksep:i,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),o=cn.getClasses(),s=cn.getRelations();c.info(s),function(t,e){var n=Object.keys(t);c.info("keys:",n),c.info(t),n.forEach((function(n){var r=t[n],i="";r.cssClasses.length>0&&(i=i+" "+r.cssClasses.join(" "));var a={labelStyle:""},o=void 0!==r.text?r.text:r.id,s="";switch(r.type){case"class":s="class_box";break;default:s="class_box"}e.setNode(r.id,{labelStyle:a.labelStyle,shape:s,labelText:o,classData:r,rx:0,ry:0,class:i,style:a.style,id:r.id,domId:r.domId,haveCallback:r.haveCallback,link:r.link,width:"group"===r.type?500:void 0,type:r.type,padding:_t().flowchart.padding}),c.info("setNode",{labelStyle:a.labelStyle,shape:s,labelText:o,rx:0,ry:0,class:i,style:a.style,id:r.id,width:"group"===r.type?500:void 0,type:r.type,padding:_t().flowchart.padding})}))}(o,a),function(t,e){var n=0;t.forEach((function(r){n++;var i={classes:"relation"};i.pattern=1==r.relation.lineType?"dashed":"solid",i.id="id"+n,"arrow_open"===r.type?i.arrowhead="none":i.arrowhead="normal",c.info(i,r),i.startLabelRight="none"===r.relationTitle1?"":r.relationTitle1,i.endLabelLeft="none"===r.relationTitle2?"":r.relationTitle2,i.arrowTypeStart=gi(r.relation.type1),i.arrowTypeEnd=gi(r.relation.type2);var a="",o="";if(void 0!==r.style){var s=B(r.style);a=s.style,o=s.labelStyle}else a="fill:none";i.style=a,i.labelStyle=o,void 0!==r.interpolate?i.curve=D(r.interpolate,d.curveLinear):void 0!==t.defaultInterpolate?i.curve=D(t.defaultInterpolate,d.curveLinear):i.curve=D(fi.curve,d.curveLinear),r.text=r.title,void 0===r.text?void 0!==r.style&&(i.arrowheadStyle="fill: #333"):(i.arrowheadStyle="fill: #333",i.labelpos="c",_t().flowchart.htmlLabels,i.labelType="text",i.label=r.text.replace(x.lineBreakRegex,"\n"),void 0===r.style&&(i.style=i.style||"stroke: #333; stroke-width: 1.5px;fill:none"),i.labelStyle=i.labelStyle.replace("color:","fill:")),e.setEdge(r.id1,r.id2,i,n)}))}(s,a);var u=Object(d.select)('[id="'.concat(e,'"]'));u.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var l=Object(d.select)("#"+e+" g");On(l,a,["aggregation","extension","composition","dependency"],"classDiagram",e);var h=u.node().getBBox(),f=h.width+16,p=h.height+16;if(c.debug("new ViewBox 0 0 ".concat(f," ").concat(p),"translate(".concat(8-a._label.marginx,", ").concat(8-a._label.marginy,")")),W(u,p,f,n.useMaxWidth),u.attr("viewBox","0 0 ".concat(f," ").concat(p)),u.select("g").attr("transform","translate(".concat(8-a._label.marginx,", ").concat(8-h.y,")")),!n.htmlLabels)for(var g=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),y=0;y<g.length;y++){var v=g[y],m=v.getBBox(),b=document.createElementNS("http://www.w3.org/2000/svg","rect");b.setAttribute("rx",0),b.setAttribute("ry",0),b.setAttribute("width",m.width),b.setAttribute("height",m.height),b.setAttribute("style","fill:#e8e8e8;"),v.insertBefore(b,v.firstChild)}};function gi(t){var e;switch(t){case 0:e="aggregation";break;case 1:e="extension";break;case 2:e="composition";break;case 3:e="dependency";break;default:e="none"}return e}function yi(t){return(yi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var vi,mi=function(t){return JSON.parse(JSON.stringify(t))},bi=[],xi={root:{relations:[],states:{},documents:{}}},_i=xi.root,ki=0,wi=function(t,e,n,r,i){void 0===_i.states[t]?_i.states[t]={id:t,descriptions:[],type:e,doc:n,note:i}:(_i.states[t].doc||(_i.states[t].doc=n),_i.states[t].type||(_i.states[t].type=e)),r&&(c.info("Adding state ",t,r),"string"==typeof r&&Ci(t,r.trim()),"object"===yi(r)&&r.forEach((function(e){return Ci(t,e.trim())}))),i&&(_i.states[t].note=i)},Ei=function(){_i=(xi={root:{relations:[],states:{},documents:{}}}).root,_i=xi.root,ki=0,0,Si=[]},Ti=function(t,e,n){var r=t,i=e,a="default",o="default";"[*]"===t&&(r="start"+ ++ki,a="start"),"[*]"===e&&(i="end"+ki,o="end"),wi(r,a),wi(i,o),_i.relations.push({id1:r,id2:i,title:n})},Ci=function(t,e){var n=_i.states[t],r=e;":"===r[0]&&(r=r.substr(1).trim()),n.descriptions.push(r)},Ai=0,Si=[],Mi={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().state},addState:wi,clear:Ei,getState:function(t){return _i.states[t]},getStates:function(){return _i.states},getRelations:function(){return _i.relations},getClasses:function(){return Si},getDirection:function(){return"TB"},addRelation:Ti,getDividerId:function(){return"divider-id-"+ ++Ai},cleanupLabel:function(t){return":"===t.substring(0,1)?t.substr(2).trim():t.trim()},lineType:{LINE:0,DOTTED_LINE:1},relationType:{AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},logDocuments:function(){c.info("Documents = ",xi)},getRootDoc:function(){return bi},setRootDoc:function(t){c.info("Setting root doc",t),bi=t},getRootDocV2:function(){return function t(e,n,r){if("relation"===n.stmt)t(e,n.state1,!0),t(e,n.state2,!1);else if("state"===n.stmt&&"[*]"===n.id&&(n.id=r?e.id+"_start":e.id+"_end",n.start=r),n.doc){var i=[],a=0,o=[];for(a=0;a<n.doc.length;a++)if("divider"===n.doc[a].type){var s=mi(n.doc[a]);s.doc=mi(o),i.push(s),o=[]}else o.push(n.doc[a]);if(i.length>0&&o.length>0){var c={stmt:"state",id:P(),type:"divider",doc:mi(o)};i.push(mi(c)),n.doc=i}n.doc.forEach((function(e){return t(n,e,!0)}))}}({id:"root"},{id:"root",doc:bi},!0),{id:"root",doc:bi}},extract:function(t){var e;e=t.doc?t.doc:t,c.info(e),Ei(),c.info("Extract",e),e.forEach((function(t){"state"===t.stmt&&wi(t.id,t.type,t.doc,t.description,t.note),"relation"===t.stmt&&Ti(t.state1.id,t.state2.id,t.description)}))},trimColon:function(t){return t&&":"===t[0]?t.substr(1).trim():t.trim()}},Oi=n(22),Di=n.n(Oi),Ni={},Bi=function(t,e){Ni[t]=e},Li=function(t,e){var n=t.append("text").attr("x",2*_t().state.padding).attr("y",_t().state.textHeight+1.3*_t().state.padding).attr("font-size",_t().state.fontSize).attr("class","state-title").text(e.descriptions[0]).node().getBBox(),r=n.height,i=t.append("text").attr("x",_t().state.padding).attr("y",r+.4*_t().state.padding+_t().state.dividerMargin+_t().state.textHeight).attr("class","state-description"),a=!0,o=!0;e.descriptions.forEach((function(t){a||(!function(t,e,n){var r=t.append("tspan").attr("x",2*_t().state.padding).text(e);n||r.attr("dy",_t().state.textHeight)}(i,t,o),o=!1),a=!1}));var s=t.append("line").attr("x1",_t().state.padding).attr("y1",_t().state.padding+r+_t().state.dividerMargin/2).attr("y2",_t().state.padding+r+_t().state.dividerMargin/2).attr("class","descr-divider"),c=i.node().getBBox(),u=Math.max(c.width,n.width);return s.attr("x2",u+3*_t().state.padding),t.insert("rect",":first-child").attr("x",_t().state.padding).attr("y",_t().state.padding).attr("width",u+2*_t().state.padding).attr("height",c.height+r+2*_t().state.padding).attr("rx",_t().state.radius),t},Pi=function(t,e,n){var r,i=_t().state.padding,a=2*_t().state.padding,o=t.node().getBBox(),s=o.width,c=o.x,u=t.append("text").attr("x",0).attr("y",_t().state.titleShift).attr("font-size",_t().state.fontSize).attr("class","state-title").text(e.id),l=u.node().getBBox().width+a,h=Math.max(l,s);h===s&&(h+=a);var f=t.node().getBBox();e.doc,r=c-i,l>s&&(r=(s-h)/2+i),Math.abs(c-f.x)<i&&l>s&&(r=c-(l-s)/2);var d=1-_t().state.textHeight;return t.insert("rect",":first-child").attr("x",r).attr("y",d).attr("class",n?"alt-composit":"composit").attr("width",h).attr("height",f.height+_t().state.textHeight+_t().state.titleShift+1).attr("rx","0"),u.attr("x",r+i),l<=s&&u.attr("x",c+(h-a)/2-l/2+i),t.insert("rect",":first-child").attr("x",r).attr("y",_t().state.titleShift-_t().state.textHeight-_t().state.padding).attr("width",h).attr("height",3*_t().state.textHeight).attr("rx",_t().state.radius),t.insert("rect",":first-child").attr("x",r).attr("y",_t().state.titleShift-_t().state.textHeight-_t().state.padding).attr("width",h).attr("height",f.height+3+2*_t().state.textHeight).attr("rx",_t().state.radius),t},Ii=function(t,e){e.attr("class","state-note");var n=e.append("rect").attr("x",0).attr("y",_t().state.padding),r=function(t,e,n,r){var i=0,a=r.append("text");a.style("text-anchor","start"),a.attr("class","noteText");var o=t.replace(/\r\n/g,"<br/>"),s=(o=o.replace(/\n/g,"<br/>")).split(x.lineBreakRegex),c=1.25*_t().state.noteMargin,u=!0,l=!1,h=void 0;try{for(var f,d=s[Symbol.iterator]();!(u=(f=d.next()).done);u=!0){var p=f.value.trim();if(p.length>0){var g=a.append("tspan");if(g.text(p),0===c)c+=g.node().getBBox().height;i+=c,g.attr("x",e+_t().state.noteMargin),g.attr("y",n+i+1.25*_t().state.noteMargin)}}}catch(t){l=!0,h=t}finally{try{u||null==d.return||d.return()}finally{if(l)throw h}}return{textWidth:a.node().getBBox().width,textHeight:i}}(t,0,0,e.append("g")),i=r.textWidth,a=r.textHeight;return n.attr("height",a+2*_t().state.noteMargin),n.attr("width",i+2*_t().state.noteMargin),n},Fi=function(t,e){var n=e.id,r={id:n,label:e.id,width:0,height:0},i=t.append("g").attr("id",n).attr("class","stateGroup");"start"===e.type&&function(t){t.append("circle").attr("class","start-state").attr("r",_t().state.sizeUnit).attr("cx",_t().state.padding+_t().state.sizeUnit).attr("cy",_t().state.padding+_t().state.sizeUnit)}(i),"end"===e.type&&function(t){t.append("circle").attr("class","end-state-outer").attr("r",_t().state.sizeUnit+_t().state.miniPadding).attr("cx",_t().state.padding+_t().state.sizeUnit+_t().state.miniPadding).attr("cy",_t().state.padding+_t().state.sizeUnit+_t().state.miniPadding),t.append("circle").attr("class","end-state-inner").attr("r",_t().state.sizeUnit).attr("cx",_t().state.padding+_t().state.sizeUnit+2).attr("cy",_t().state.padding+_t().state.sizeUnit+2)}(i),"fork"!==e.type&&"join"!==e.type||function(t,e){var n=_t().state.forkWidth,r=_t().state.forkHeight;if(e.parentId){var i=n;n=r,r=i}t.append("rect").style("stroke","black").style("fill","black").attr("width",n).attr("height",r).attr("x",_t().state.padding).attr("y",_t().state.padding)}(i,e),"note"===e.type&&Ii(e.note.text,i),"divider"===e.type&&function(t){t.append("line").style("stroke","grey").style("stroke-dasharray","3").attr("x1",_t().state.textHeight).attr("class","divider").attr("x2",2*_t().state.textHeight).attr("y1",0).attr("y2",0)}(i),"default"===e.type&&0===e.descriptions.length&&function(t,e){var n=t.append("text").attr("x",2*_t().state.padding).attr("y",_t().state.textHeight+2*_t().state.padding).attr("font-size",_t().state.fontSize).attr("class","state-title").text(e.id),r=n.node().getBBox();t.insert("rect",":first-child").attr("x",_t().state.padding).attr("y",_t().state.padding).attr("width",r.width+2*_t().state.padding).attr("height",r.height+2*_t().state.padding).attr("rx",_t().state.radius)}(i,e),"default"===e.type&&e.descriptions.length>0&&Li(i,e);var a=i.node().getBBox();return r.width=a.width+2*_t().state.padding,r.height=a.height+2*_t().state.padding,Bi(n,r),r},ji=0;Oi.parser.yy=Mi;var Ri={},Yi=function t(e,n,r,i){var a,o=new G.a.Graph({compound:!0,multigraph:!0}),s=!0;for(a=0;a<e.length;a++)if("relation"===e[a].stmt){s=!1;break}r?o.setGraph({rankdir:"LR",multigraph:!0,compound:!0,ranker:"tight-tree",ranksep:s?1:vi.edgeLengthFactor,nodeSep:s?1:50,isMultiGraph:!0}):o.setGraph({rankdir:"TB",multigraph:!0,compound:!0,ranksep:s?1:vi.edgeLengthFactor,nodeSep:s?1:50,ranker:"tight-tree",isMultiGraph:!0}),o.setDefaultEdgeLabel((function(){return{}})),Mi.extract(e);for(var u=Mi.getStates(),l=Mi.getRelations(),h=Object.keys(u),f=0;f<h.length;f++){var p=u[h[f]];r&&(p.parentId=r);var g=void 0;if(p.doc){var y=n.append("g").attr("id",p.id).attr("class","stateGroup");g=t(p.doc,y,p.id,!i);var v=(y=Pi(y,p,i)).node().getBBox();g.width=v.width,g.height=v.height+vi.padding/2,Ri[p.id]={y:vi.compositTitleSize}}else g=Fi(n,p);if(p.note){var m={descriptions:[],id:p.id+"-note",note:p.note,type:"note"},b=Fi(n,m);"left of"===p.note.position?(o.setNode(g.id+"-note",b),o.setNode(g.id,g)):(o.setNode(g.id,g),o.setNode(g.id+"-note",b)),o.setParent(g.id,g.id+"-group"),o.setParent(g.id+"-note",g.id+"-group")}else o.setNode(g.id,g)}c.debug("Count=",o.nodeCount(),o);var _=0;l.forEach((function(t){var e;_++,c.debug("Setting edge",t),o.setEdge(t.id1,t.id2,{relation:t,width:(e=t.title,e?e.length*vi.fontSizeFactor:1),height:vi.labelHeight*x.getRows(t.title).length,labelpos:"c"},"id"+_)})),ke.a.layout(o),c.debug("Graph after layout",o.nodes());var k=n.node();o.nodes().forEach((function(t){void 0!==t&&void 0!==o.node(t)?(c.warn("Node "+t+": "+JSON.stringify(o.node(t))),Object(d.select)("#"+k.id+" #"+t).attr("transform","translate("+(o.node(t).x-o.node(t).width/2)+","+(o.node(t).y+(Ri[t]?Ri[t].y:0)-o.node(t).height/2)+" )"),Object(d.select)("#"+k.id+" #"+t).attr("data-x-shift",o.node(t).x-o.node(t).width/2),document.querySelectorAll("#"+k.id+" #"+t+" .divider").forEach((function(t){var e=t.parentElement,n=0,r=0;e&&(e.parentElement&&(n=e.parentElement.getBBox().width),r=parseInt(e.getAttribute("data-x-shift"),10),Number.isNaN(r)&&(r=0)),t.setAttribute("x1",0-r+8),t.setAttribute("x2",n-r-8)}))):c.debug("No Node "+t+": "+JSON.stringify(o.node(t)))}));var w=k.getBBox();o.edges().forEach((function(t){void 0!==t&&void 0!==o.edge(t)&&(c.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(o.edge(t))),function(t,e,n){e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var r=e.points,i=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis),a=t.append("path").attr("d",i(r)).attr("id","edge"+ji).attr("class","transition"),o="";if(_t().state.arrowMarkerAbsolute&&(o=(o=(o=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),a.attr("marker-end","url("+o+"#"+function(t){switch(t){case Mi.relationType.AGGREGATION:return"aggregation";case Mi.relationType.EXTENSION:return"extension";case Mi.relationType.COMPOSITION:return"composition";case Mi.relationType.DEPENDENCY:return"dependency"}}(Mi.relationType.DEPENDENCY)+"End)"),void 0!==n.title){for(var s=t.append("g").attr("class","stateLabel"),u=H.calcLabelPosition(e.points),l=u.x,h=u.y,f=x.getRows(n.title),p=0,g=[],y=0,v=0,m=0;m<=f.length;m++){var b=s.append("text").attr("text-anchor","middle").text(f[m]).attr("x",l).attr("y",h+p),_=b.node().getBBox();if(y=Math.max(y,_.width),v=Math.min(v,_.x),c.info(_.x,l,h+p),0===p){var k=b.node().getBBox();p=k.height,c.info("Title height",p,h)}g.push(b)}var w=p*f.length;if(f.length>1){var E=(f.length-1)*p*.5;g.forEach((function(t,e){return t.attr("y",h+e*p-E)})),w=p*f.length}var T=s.node().getBBox();s.insert("rect",":first-child").attr("class","box").attr("x",l-y/2-_t().state.padding/2).attr("y",h-w/2-_t().state.padding/2-3.5).attr("width",y+_t().state.padding).attr("height",w+_t().state.padding),c.info(T)}ji++}(n,o.edge(t),o.edge(t).relation))})),w=k.getBBox();var E={id:r||"root",label:r||"root",width:0,height:0};return E.width=w.width+2*vi.padding,E.height=w.height+2*vi.padding,c.debug("Doc rendered",E,o),E},zi=function(){},Ui=function(t,e){vi=_t().state,Oi.parser.yy.clear(),Oi.parser.parse(t),c.debug("Rendering diagram "+t);var n=Object(d.select)("[id='".concat(e,"']"));n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z"),new G.a.Graph({multigraph:!0,compound:!0,rankdir:"RL"}).setDefaultEdgeLabel((function(){return{}}));var r=Mi.getRootDoc();Yi(r,n,void 0,!1);var i=vi.padding,a=n.node().getBBox(),o=a.width+2*i,s=a.height+2*i;W(n,s,1.75*o,vi.useMaxWidth),n.attr("viewBox","".concat(a.x-vi.padding," ").concat(a.y-vi.padding," ")+o+" "+s)},$i={},Wi={},Hi=function(t,e,n,r){if("root"!==n.id){var i="rect";!0===n.start&&(i="start"),!1===n.start&&(i="end"),"default"!==n.type&&(i=n.type),Wi[n.id]||(Wi[n.id]={id:n.id,shape:i,description:n.id,classes:"statediagram-state"}),n.description&&(Array.isArray(Wi[n.id].description)?(Wi[n.id].shape="rectWithTitle",Wi[n.id].description.push(n.description)):Wi[n.id].description.length>0?(Wi[n.id].shape="rectWithTitle",Wi[n.id].description===n.id?Wi[n.id].description=[n.description]:Wi[n.id].description=[Wi[n.id].description,n.description]):(Wi[n.id].shape="rect",Wi[n.id].description=n.description)),!Wi[n.id].type&&n.doc&&(c.info("Setting cluser for ",n.id),Wi[n.id].type="group",Wi[n.id].shape="divider"===n.type?"divider":"roundedWithTitle",Wi[n.id].classes=Wi[n.id].classes+" "+(r?"statediagram-cluster statediagram-cluster-alt":"statediagram-cluster"));var a={labelStyle:"",shape:Wi[n.id].shape,labelText:Wi[n.id].description,classes:Wi[n.id].classes,style:"",id:n.id,domId:"state-"+n.id+"-"+Vi,type:Wi[n.id].type,padding:15};if(n.note){var o={labelStyle:"",shape:"note",labelText:n.note.text,classes:"statediagram-note",style:"",id:n.id+"----note",domId:"state-"+n.id+"----note-"+Vi,type:Wi[n.id].type,padding:15},s={labelStyle:"",shape:"noteGroup",labelText:n.note.text,classes:Wi[n.id].classes,style:"",id:n.id+"----parent",domId:"state-"+n.id+"----parent-"+Vi,type:"group",padding:0};Vi++,t.setNode(n.id+"----parent",s),t.setNode(o.id,o),t.setNode(n.id,a),t.setParent(n.id,n.id+"----parent"),t.setParent(o.id,n.id+"----parent");var u=n.id,l=o.id;"left of"===n.note.position&&(u=o.id,l=n.id),t.setEdge(u,l,{arrowhead:"none",arrowType:"",style:"fill:none",labelStyle:"",classes:"transition note-edge",arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal"})}else t.setNode(n.id,a)}e&&"root"!==e.id&&(c.info("Setting node ",n.id," to be child of its parent ",e.id),t.setParent(n.id,e.id)),n.doc&&(c.info("Adding nodes children "),Gi(t,n,n.doc,!r))},Vi=0,Gi=function(t,e,n,r){Vi=0,c.trace("items",n),n.forEach((function(n){if("state"===n.stmt||"default"===n.stmt)Hi(t,e,n,r);else if("relation"===n.stmt){Hi(t,e,n.state1,r),Hi(t,e,n.state2,r);var i={id:"edge"+Vi,arrowhead:"normal",arrowTypeEnd:"arrow_barb",style:"fill:none",labelStyle:"",label:n.description,arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal",classes:"transition"},a=n.state1.id,o=n.state2.id;t.setEdge(a,o,i,Vi),Vi++}}))},qi=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)$i[e[n]]=t[e[n]]},Xi=function(t,e){c.info("Drawing state diagram (v2)",e),Mi.clear(),Wi={};var n=Di.a.parser;n.yy=Mi,n.parse(t);var r=Mi.getDirection();void 0===r&&(r="LR");var i=_t().state,a=i.nodeSpacing||50,o=i.rankSpacing||50,s=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:"TB",nodesep:a,ranksep:o,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));c.info(Mi.getRootDocV2()),Mi.extract(Mi.getRootDocV2()),c.info(Mi.getRootDocV2()),Hi(s,void 0,Mi.getRootDocV2(),!0);var u=Object(d.select)('[id="'.concat(e,'"]')),l=Object(d.select)("#"+e+" g");On(l,s,["barb"],"statediagram",e);var h=u.node().getBBox(),f=h.width+16,p=h.height+16;u.attr("class","statediagram");var g=u.node().getBBox();W(u,p,1.75*f,i.useMaxWidth);var y="".concat(g.x-8," ").concat(g.y-8," ").concat(f," ").concat(p);if(c.debug("viewBox ".concat(y)),u.attr("viewBox",y),!i.htmlLabels)for(var v=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),m=0;m<v.length;m++){var b=v[m],x=b.getBBox(),_=document.createElementNS("http://www.w3.org/2000/svg","rect");_.setAttribute("rx",0),_.setAttribute("ry",0),_.setAttribute("width",x.width),_.setAttribute("height",x.height),b.insertBefore(_,b.firstChild)}},Zi={},Ji=null,Ki={master:Ji},Qi="master",ta="LR",ea=0;function na(){return I({length:7})}function ra(t,e){for(c.debug("Entering isfastforwardable:",t.id,e.id);t.seq<=e.seq&&t!==e&&null!=e.parent;){if(Array.isArray(e.parent))return c.debug("In merge commit:",e.parent),ra(t,Zi[e.parent[0]])||ra(t,Zi[e.parent[1]]);e=Zi[e.parent]}return c.debug(t.id,e.id),t.id===e.id}var ia={};function aa(t,e,n){var r=t.indexOf(e);-1===r?t.push(n):t.splice(r,1,n)}function oa(t){var e=t.reduce((function(t,e){return t.seq>e.seq?t:e}),t[0]),n="";t.forEach((function(t){n+=t===e?"\t*":"\t|"}));var r,i,a,o=[n,e.id,e.seq];for(var s in Ki)Ki[s]===e.id&&o.push(s);if(c.debug(o.join(" ")),Array.isArray(e.parent)){var u=Zi[e.parent[0]];aa(t,e,u),t.push(Zi[e.parent[1]])}else{if(null==e.parent)return;var l=Zi[e.parent];aa(t,e,l)}r=t,i=function(t){return t.id},a=Object.create(null),oa(t=r.reduce((function(t,e){var n=i(e);return a[n]||(a[n]=!0,t.push(e)),t}),[]))}var sa,ca=function(){var t=Object.keys(Zi).map((function(t){return Zi[t]}));return t.forEach((function(t){c.debug(t.id)})),t.sort((function(t,e){return e.seq-t.seq})),t},ua={setDirection:function(t){ta=t},setOptions:function(t){c.debug("options str",t),t=(t=t&&t.trim())||"{}";try{ia=JSON.parse(t)}catch(t){c.error("error while parsing gitGraph options",t.message)}},getOptions:function(){return ia},commit:function(t){var e={id:na(),message:t,seq:ea++,parent:null==Ji?null:Ji.id};Ji=e,Zi[e.id]=e,Ki[Qi]=e.id,c.debug("in pushCommit "+e.id)},branch:function(t){Ki[t]=null!=Ji?Ji.id:null,c.debug("in createBranch")},merge:function(t){var e=Zi[Ki[Qi]],n=Zi[Ki[t]];if(function(t,e){return t.seq>e.seq&&ra(e,t)}(e,n))c.debug("Already merged");else{if(ra(e,n))Ki[Qi]=Ki[t],Ji=Zi[Ki[Qi]];else{var r={id:na(),message:"merged branch "+t+" into "+Qi,seq:ea++,parent:[null==Ji?null:Ji.id,Ki[t]]};Ji=r,Zi[r.id]=r,Ki[Qi]=r.id}c.debug(Ki),c.debug("in mergeBranch")}},checkout:function(t){c.debug("in checkout");var e=Ki[Qi=t];Ji=Zi[e]},reset:function(t){c.debug("in reset",t);var e=t.split(":")[0],n=parseInt(t.split(":")[1]),r="HEAD"===e?Ji:Zi[Ki[e]];for(c.debug(r,n);n>0;)if(n--,!(r=Zi[r.parent])){var i="Critical error - unique parent commit not found during reset";throw c.error(i),i}Ji=r,Ki[Qi]=r.id},prettyPrint:function(){c.debug(Zi),oa([ca()[0]])},clear:function(){Zi={},Ki={master:Ji=null},Qi="master",ea=0},getBranchesAsObjArray:function(){var t=[];for(var e in Ki)t.push({name:e,commit:Zi[Ki[e]]});return t},getBranches:function(){return Ki},getCommits:function(){return Zi},getCommitsArray:ca,getCurrentBranch:function(){return Qi},getDirection:function(){return ta},getHead:function(){return Ji}},la=n(71),ha=n.n(la),fa={},da={nodeSpacing:150,nodeFillColor:"yellow",nodeStrokeWidth:2,nodeStrokeColor:"grey",lineStrokeWidth:4,branchOffset:50,lineColor:"grey",leftMargin:50,branchColors:["#442f74","#983351","#609732","#AA9A39"],nodeRadius:10,nodeLabel:{width:75,height:100,x:-25,y:0}},pa={};function ga(t,e,n,r){var i=D(r,d.curveBasis),a=da.branchColors[n%da.branchColors.length],o=Object(d.line)().x((function(t){return Math.round(t.x)})).y((function(t){return Math.round(t.y)})).curve(i);t.append("svg:path").attr("d",o(e)).style("stroke",a).style("stroke-width",da.lineStrokeWidth).style("fill","none")}function ya(t,e){e=e||t.node().getBBox();var n=t.node().getCTM();return{left:n.e+e.x*n.a,top:n.f+e.y*n.d,width:e.width,height:e.height}}function va(t,e,n,r,i){c.debug("svgDrawLineForCommits: ",e,n);var a=ya(t.select("#node-"+e+" circle")),o=ya(t.select("#node-"+n+" circle"));switch(r){case"LR":if(a.left-o.left>da.nodeSpacing){var s={x:a.left-da.nodeSpacing,y:o.top+o.height/2};ga(t,[s,{x:o.left+o.width,y:o.top+o.height/2}],i,"linear"),ga(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:s.y},s],i)}else ga(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:o.top+o.height/2},{x:o.left+o.width,y:o.top+o.height/2}],i);break;case"BT":if(o.top-a.top>da.nodeSpacing){var u={x:o.left+o.width/2,y:a.top+a.height+da.nodeSpacing};ga(t,[u,{x:o.left+o.width/2,y:o.top}],i,"linear"),ga(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+a.height+da.nodeSpacing/2},{x:o.left+o.width/2,y:u.y-da.nodeSpacing/2},u],i)}else ga(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+da.nodeSpacing/2},{x:o.left+o.width/2,y:o.top-da.nodeSpacing/2},{x:o.left+o.width/2,y:o.top}],i)}}function ma(t,e){return t.select(e).node().cloneNode(!0)}function ba(t,e,n,r){var i,a=Object.keys(fa).length;if("string"==typeof e)do{if(i=fa[e],c.debug("in renderCommitHistory",i.id,i.seq),t.select("#node-"+e).size()>0)return;t.append((function(){return ma(t,"#def-commit")})).attr("class","commit").attr("id",(function(){return"node-"+i.id})).attr("transform",(function(){switch(r){case"LR":return"translate("+(i.seq*da.nodeSpacing+da.leftMargin)+", "+sa*da.branchOffset+")";case"BT":return"translate("+(sa*da.branchOffset+da.leftMargin)+", "+(a-i.seq)*da.nodeSpacing+")"}})).attr("fill",da.nodeFillColor).attr("stroke",da.nodeStrokeColor).attr("stroke-width",da.nodeStrokeWidth);var o=void 0;for(var s in n)if(n[s].commit===i){o=n[s];break}o&&(c.debug("found branch ",o.name),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","branch-label").text(o.name+", ")),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-id").text(i.id),""!==i.message&&"BT"===r&&t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-msg").text(", "+i.message),e=i.parent}while(e&&fa[e]);Array.isArray(e)&&(c.debug("found merge commmit",e),ba(t,e[0],n,r),sa++,ba(t,e[1],n,r),sa--)}function xa(t,e,n,r){for(r=r||0;e.seq>0&&!e.lineDrawn;)"string"==typeof e.parent?(va(t,e.id,e.parent,n,r),e.lineDrawn=!0,e=fa[e.parent]):Array.isArray(e.parent)&&(va(t,e.id,e.parent[0],n,r),va(t,e.id,e.parent[1],n,r+1),xa(t,fa[e.parent[1]],n,r+1),e.lineDrawn=!0,e=fa[e.parent[0]])}var _a,ka=function(t){pa=t},wa=function(t,e,n){try{var r=ha.a.parser;r.yy=ua,r.yy.clear(),c.debug("in gitgraph renderer",t+"\n","id:",e,n),r.parse(t+"\n"),da=Object.assign(da,pa,ua.getOptions()),c.debug("effective options",da);var i=ua.getDirection();fa=ua.getCommits();var a=ua.getBranchesAsObjArray();"BT"===i&&(da.nodeLabel.x=a.length*da.branchOffset,da.nodeLabel.width="100%",da.nodeLabel.y=-2*da.nodeRadius);var o=Object(d.select)('[id="'.concat(e,'"]'));for(var s in function(t){t.append("defs").append("g").attr("id","def-commit").append("circle").attr("r",da.nodeRadius).attr("cx",0).attr("cy",0),t.select("#def-commit").append("foreignObject").attr("width",da.nodeLabel.width).attr("height",da.nodeLabel.height).attr("x",da.nodeLabel.x).attr("y",da.nodeLabel.y).attr("class","node-label").attr("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility").append("p").html("")}(o),sa=1,a){var u=a[s];ba(o,u.commit.id,a,i),xa(o,u.commit,i),sa++}o.attr("height",(function(){return"BT"===i?Object.keys(fa).length*da.nodeSpacing:(a.length+1)*da.branchOffset}))}catch(t){c.error("Error while rendering gitgraph"),c.error(t.message)}},Ea="",Ta=!1,Ca={setMessage:function(t){c.debug("Setting message to: "+t),Ea=t},getMessage:function(){return Ea},setInfo:function(t){Ta=t},getInfo:function(){return Ta}},Aa=n(72),Sa=n.n(Aa),Ma={},Oa=function(t){Object.keys(t).forEach((function(e){Ma[e]=t[e]}))},Da=function(t,e,n){try{var r=Sa.a.parser;r.yy=Ca,c.debug("Renering info diagram\n"+t),r.parse(t),c.debug("Parsed info diagram");var i=Object(d.select)("#"+e);i.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size","32px").style("text-anchor","middle").text("v "+n),i.attr("height",100),i.attr("width",400)}catch(t){c.error("Error while rendering info diagram"),c.error(t.message)}},Na={},Ba=function(t){Object.keys(t).forEach((function(e){Na[e]=t[e]}))},La=function(t,e){try{c.debug("Renering svg for syntax error\n");var n=Object(d.select)("#"+t),r=n.append("g");r.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"),r.append("path").attr("class","error-icon").attr("d","m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"),r.append("path").attr("class","error-icon").attr("d","m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"),r.append("path").attr("class","error-icon").attr("d","m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"),r.append("path").attr("class","error-icon").attr("d","m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"),r.append("path").attr("class","error-icon").attr("d","m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"),r.append("text").attr("class","error-text").attr("x",1240).attr("y",250).attr("font-size","150px").style("text-anchor","middle").text("Syntax error in graph"),r.append("text").attr("class","error-text").attr("x",1050).attr("y",400).attr("font-size","100px").style("text-anchor","middle").text("mermaid version "+e),n.attr("height",100),n.attr("width",400),n.attr("viewBox","768 0 512 512")}catch(t){c.error("Error while rendering info diagram"),c.error(t.message)}},Pa={},Ia="",Fa={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().pie},addSection:function(t,e){void 0===Pa[t]&&(Pa[t]=e,c.debug("Added new section :",t))},getSections:function(){return Pa},cleanupValue:function(t){return":"===t.substring(0,1)?(t=t.substring(1).trim(),Number(t.trim())):Number(t.trim())},clear:function(){Pa={},Ia=""},setTitle:function(t){Ia=t},getTitle:function(){return Ia}},ja=n(73),Ra=n.n(ja),Ya={},za=function(t){Object.keys(t).forEach((function(e){Ya[e]=t[e]}))},Ua=function(t,e){try{var n=Ra.a.parser;n.yy=Fa,c.debug("Rendering info diagram\n"+t),n.yy.clear(),n.parse(t),c.debug("Parsed info diagram");var r=document.getElementById(e);void 0===(_a=r.parentElement.offsetWidth)&&(_a=1200),void 0!==Ya.useWidth&&(_a=Ya.useWidth);var i=Object(d.select)("#"+e);W(i,450,_a,Ya.useMaxWidth),r.setAttribute("viewBox","0 0 "+_a+" 450");var a=Math.min(_a,450)/2-40,o=i.append("g").attr("transform","translate("+_a/2+",225)"),s=Fa.getSections(),u=0;Object.keys(s).forEach((function(t){u+=s[t]}));var l=Object(d.scaleOrdinal)().domain(s).range(d.schemeSet2),h=Object(d.pie)().value((function(t){return t.value}))(Object(d.entries)(s)),f=Object(d.arc)().innerRadius(0).outerRadius(a);o.selectAll("mySlices").data(h).enter().append("path").attr("d",f).attr("fill",(function(t){return l(t.data.key)})).attr("stroke","black").style("stroke-width","2px").style("opacity",.7),o.selectAll("mySlices").data(h.filter((function(t){return 0!==t.data.value}))).enter().append("text").text((function(t){return(t.data.value/u*100).toFixed(0)+"%"})).attr("transform",(function(t){return"translate("+f.centroid(t)+")"})).style("text-anchor","middle").attr("class","slice").style("font-size",17),o.append("text").text(n.yy.getTitle()).attr("x",0).attr("y",-200).attr("class","pieTitleText");var p=o.selectAll(".legend").data(l.domain()).enter().append("g").attr("class","legend").attr("transform",(function(t,e){return"translate(216,"+(22*e-22*l.domain().length/2)+")"}));p.append("rect").attr("width",18).attr("height",18).style("fill",l).style("stroke",l),p.append("text").attr("x",22).attr("y",14).text((function(t){return t}))}catch(t){c.error("Error while rendering info diagram"),c.error(t)}},$a={},Wa=[],Ha="",Va=function(t){return void 0===$a[t]&&($a[t]={attributes:[]},c.info("Added new entity :",t)),$a[t]},Ga={Cardinality:{ZERO_OR_ONE:"ZERO_OR_ONE",ZERO_OR_MORE:"ZERO_OR_MORE",ONE_OR_MORE:"ONE_OR_MORE",ONLY_ONE:"ONLY_ONE"},Identification:{NON_IDENTIFYING:"NON_IDENTIFYING",IDENTIFYING:"IDENTIFYING"},parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().er},addEntity:Va,addAttributes:function(t,e){var n,r=Va(t);for(n=e.length-1;n>=0;n--)r.attributes.push(e[n]),c.debug("Added attribute ",e[n].attributeName)},getEntities:function(){return $a},addRelationship:function(t,e,n,r){var i={entityA:t,roleA:e,entityB:n,relSpec:r};Wa.push(i),c.debug("Added new relationship :",i)},getRelationships:function(){return Wa},clear:function(){$a={},Wa=[],Ha=""},setTitle:function(t){Ha=t},getTitle:function(){return Ha}},qa=n(74),Xa=n.n(qa),Za={ONLY_ONE_START:"ONLY_ONE_START",ONLY_ONE_END:"ONLY_ONE_END",ZERO_OR_ONE_START:"ZERO_OR_ONE_START",ZERO_OR_ONE_END:"ZERO_OR_ONE_END",ONE_OR_MORE_START:"ONE_OR_MORE_START",ONE_OR_MORE_END:"ONE_OR_MORE_END",ZERO_OR_MORE_START:"ZERO_OR_MORE_START",ZERO_OR_MORE_END:"ZERO_OR_MORE_END"},Ja=Za,Ka=function(t,e){var n;t.append("defs").append("marker").attr("id",Za.ONLY_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18 M15,0 L15,18"),t.append("defs").append("marker").attr("id",Za.ONLY_ONE_END).attr("refX",18).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,0 L3,18 M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",21).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_ONE_END).attr("refX",30).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,0 L21,18"),t.append("defs").append("marker").attr("id",Za.ONE_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27"),t.append("defs").append("marker").attr("id",Za.ONE_OR_MORE_END).attr("refX",27).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",48).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q18,0 36,18 Q18,36 0,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_MORE_END).attr("refX",39).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,18 Q39,0 57,18 Q39,36 21,18")},Qa={},to=function(t,e,n){var r;return Object.keys(e).forEach((function(i){var a=t.append("g").attr("id",i);r=void 0===r?i:r;var o="entity-"+i,s=a.append("text").attr("class","er entityLabel").attr("id",o).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("style","font-family: "+_t().fontFamily+"; font-size: "+Qa.fontSize+"px").text(i),c=function(t,e,n){var r=Qa.entityPadding/3,i=Qa.entityPadding/3,a=.85*Qa.fontSize,o=e.node().getBBox(),s=[],c=0,u=0,l=o.height+2*r,h=1;n.forEach((function(n){var i="".concat(e.node().id,"-attr-").concat(h),o=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-type")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+_t().fontFamily+"; font-size: "+a+"px").text(n.attributeType),f=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-name")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+_t().fontFamily+"; font-size: "+a+"px").text(n.attributeName);s.push({tn:o,nn:f});var d=o.node().getBBox(),p=f.node().getBBox();c=Math.max(c,d.width),u=Math.max(u,p.width),l+=Math.max(d.height,p.height)+2*r,h+=1}));var f={width:Math.max(Qa.minEntityWidth,Math.max(o.width+2*Qa.entityPadding,c+u+4*i)),height:n.length>0?l:Math.max(Qa.minEntityHeight,o.height+2*Qa.entityPadding)},d=Math.max(0,f.width-(c+u)-4*i);if(n.length>0){e.attr("transform","translate("+f.width/2+","+(r+o.height/2)+")");var p=o.height+2*r,g="attributeBoxOdd";s.forEach((function(e){var n=p+r+Math.max(e.tn.node().getBBox().height,e.nn.node().getBBox().height)/2;e.tn.attr("transform","translate("+i+","+n+")");var a=t.insert("rect","#"+e.tn.node().id).attr("class","er ".concat(g)).attr("fill",Qa.fill).attr("fill-opacity","100%").attr("stroke",Qa.stroke).attr("x",0).attr("y",p).attr("width",c+2*i+d/2).attr("height",e.tn.node().getBBox().height+2*r);e.nn.attr("transform","translate("+(parseFloat(a.attr("width"))+i)+","+n+")"),t.insert("rect","#"+e.nn.node().id).attr("class","er ".concat(g)).attr("fill",Qa.fill).attr("fill-opacity","100%").attr("stroke",Qa.stroke).attr("x","".concat(a.attr("x")+a.attr("width"))).attr("y",p).attr("width",u+2*i+d/2).attr("height",e.nn.node().getBBox().height+2*r),p+=Math.max(e.tn.node().getBBox().height,e.nn.node().getBBox().height)+2*r,g="attributeBoxOdd"==g?"attributeBoxEven":"attributeBoxOdd"}))}else f.height=Math.max(Qa.minEntityHeight,l),e.attr("transform","translate("+f.width/2+","+f.height/2+")");return f}(a,s,e[i].attributes),u=c.width,l=c.height,h=a.insert("rect","#"+o).attr("class","er entityBox").attr("fill",Qa.fill).attr("fill-opacity","100%").attr("stroke",Qa.stroke).attr("x",0).attr("y",0).attr("width",u).attr("height",l).node().getBBox();n.setNode(i,{width:h.width,height:h.height,shape:"rect",id:i})})),r},eo=function(t){return(t.entityA+t.roleA+t.entityB).replace(/\s/g,"")},no=0,ro=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)Qa[e[n]]=t[e[n]]},io=function(t,e){c.info("Drawing ER diagram"),Ga.clear();var n=Xa.a.parser;n.yy=Ga;try{n.parse(t)}catch(t){c.debug("Parsing failed")}var r,i=Object(d.select)("[id='".concat(e,"']"));Ka(i,Qa),r=new G.a.Graph({multigraph:!0,directed:!0,compound:!1}).setGraph({rankdir:Qa.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel((function(){return{}}));var a,o,s=to(i,Ga.getEntities(),r),u=function(t,e){return t.forEach((function(t){e.setEdge(t.entityA,t.entityB,{relationship:t},eo(t))})),t}(Ga.getRelationships(),r);ke.a.layout(r),a=i,(o=r).nodes().forEach((function(t){void 0!==t&&void 0!==o.node(t)&&a.select("#"+t).attr("transform","translate("+(o.node(t).x-o.node(t).width/2)+","+(o.node(t).y-o.node(t).height/2)+" )")})),u.forEach((function(t){!function(t,e,n,r){no++;var i=n.edge(e.entityA,e.entityB,eo(e)),a=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis),o=t.insert("path","#"+r).attr("class","er relationshipLine").attr("d",a(i.points)).attr("stroke",Qa.stroke).attr("fill","none");e.relSpec.relType===Ga.Identification.NON_IDENTIFYING&&o.attr("stroke-dasharray","8,8");var s="";switch(Qa.arrowMarkerAbsolute&&(s=(s=(s=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),e.relSpec.cardA){case Ga.Cardinality.ZERO_OR_ONE:o.attr("marker-end","url("+s+"#"+Ja.ZERO_OR_ONE_END+")");break;case Ga.Cardinality.ZERO_OR_MORE:o.attr("marker-end","url("+s+"#"+Ja.ZERO_OR_MORE_END+")");break;case Ga.Cardinality.ONE_OR_MORE:o.attr("marker-end","url("+s+"#"+Ja.ONE_OR_MORE_END+")");break;case Ga.Cardinality.ONLY_ONE:o.attr("marker-end","url("+s+"#"+Ja.ONLY_ONE_END+")")}switch(e.relSpec.cardB){case Ga.Cardinality.ZERO_OR_ONE:o.attr("marker-start","url("+s+"#"+Ja.ZERO_OR_ONE_START+")");break;case Ga.Cardinality.ZERO_OR_MORE:o.attr("marker-start","url("+s+"#"+Ja.ZERO_OR_MORE_START+")");break;case Ga.Cardinality.ONE_OR_MORE:o.attr("marker-start","url("+s+"#"+Ja.ONE_OR_MORE_START+")");break;case Ga.Cardinality.ONLY_ONE:o.attr("marker-start","url("+s+"#"+Ja.ONLY_ONE_START+")")}var c=o.node().getTotalLength(),u=o.node().getPointAtLength(.5*c),l="rel"+no,h=t.append("text").attr("class","er relationshipLabel").attr("id",l).attr("x",u.x).attr("y",u.y).attr("text-anchor","middle").attr("dominant-baseline","middle").attr("style","font-family: "+_t().fontFamily+"; font-size: "+Qa.fontSize+"px").text(e.roleA).node().getBBox();t.insert("rect","#"+l).attr("class","er relationshipLabelBox").attr("x",u.x-h.width/2).attr("y",u.y-h.height/2).attr("width",h.width).attr("height",h.height).attr("fill","white").attr("fill-opacity","85%")}(i,t,r,s)}));var l=Qa.diagramPadding,h=i.node().getBBox(),f=h.width+2*l,p=h.height+2*l;W(i,p,f,Qa.useMaxWidth),i.attr("viewBox","".concat(h.x-l," ").concat(h.y-l," ").concat(f," ").concat(p))},ao=n(28),oo=n.n(ao);function so(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var co="",uo="",lo=[],ho=[],fo=[],po=function(){for(var t=!0,e=0;e<fo.length;e++)fo[e].processed,t=t&&fo[e].processed;return t},go={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().journey},clear:function(){lo.length=0,ho.length=0,uo="",co="",fo.length=0},setTitle:function(t){co=t},getTitle:function(){return co},addSection:function(t){uo=t,lo.push(t)},getSections:function(){return lo},getTasks:function(){for(var t=po(),e=0;!t&&e<100;)t=po(),e++;return ho.push.apply(ho,fo),ho},addTask:function(t,e){var n=e.substr(1).split(":"),r=0,i=[];1===n.length?(r=Number(n[0]),i=[]):(r=Number(n[0]),i=n[1].split(","));var a=i.map((function(t){return t.trim()})),o={section:uo,type:uo,people:a,task:t,score:r};fo.push(o)},addTaskOrg:function(t){var e={section:uo,type:uo,description:t,task:t,classes:[]};ho.push(e)},getActors:function(){return t=[],ho.forEach((function(e){e.people&&t.push.apply(t,so(e.people))})),so(new Set(t)).sort();var t}},yo=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},vo=function(t,e){var n=t.append("circle");return n.attr("cx",e.cx),n.attr("cy",e.cy),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("r",e.r),void 0!==n.class&&n.attr("class",n.class),void 0!==e.title&&n.append("title").text(e.title),n},mo=function(t,e){var n=e.text.replace(/<br\s*\/?>/gi," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.attr("class","legend"),r.style("text-anchor",e.anchor),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.text(n),r},bo=-1,xo=function(){return{x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0}},_o=function(){function t(t,e,n,i,a,o,s,c){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("font-color",c).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c,u){for(var l=c.taskFontSize,h=c.taskFontFamily,f=t.split(/<br\s*\/?>/gi),d=0;d<f.length;d++){var p=d*l-l*(f.length-1)/2,g=e.append("text").attr("x",n+a/2).attr("y",i).attr("fill",u).style("text-anchor","middle").style("font-size",l).style("font-family",h);g.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),g.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(g,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).attr("position","fixed").append("div").style("display","table").style("height","100%").style("width","100%");h.append("div").attr("class","label").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)n in e&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}(),ko=vo,wo=function(t,e,n){var r=t.append("g"),i=xo();i.x=e.x,i.y=e.y,i.fill=e.fill,i.width=n.width,i.height=n.height,i.class="journey-section section-type-"+e.num,i.rx=3,i.ry=3,yo(r,i),_o(n)(e.text,r,i.x,i.y,i.width,i.height,{class:"journey-section section-type-"+e.num},n,e.colour)},Eo=mo,To=function(t,e,n){var r=e.x+n.width/2,i=t.append("g");bo++;var a,o,s;i.append("line").attr("id","task"+bo).attr("x1",r).attr("y1",e.y).attr("x2",r).attr("y2",450).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),a=i,o={cx:r,cy:300+30*(5-e.score),score:e.score},a.append("circle").attr("cx",o.cx).attr("cy",o.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),(s=a.append("g")).append("circle").attr("cx",o.cx-5).attr("cy",o.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),s.append("circle").attr("cx",o.cx+5).attr("cy",o.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),o.score>3?function(t){var e=Object(d.arc)().startAngle(Math.PI/2).endAngle(Math.PI/2*3).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+o.cx+","+(o.cy+2)+")")}(s):o.score<3?function(t){var e=Object(d.arc)().startAngle(3*Math.PI/2).endAngle(Math.PI/2*5).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+o.cx+","+(o.cy+7)+")")}(s):function(t){t.append("line").attr("class","mouth").attr("stroke",2).attr("x1",o.cx-5).attr("y1",o.cy+7).attr("x2",o.cx+5).attr("y2",o.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}(s);var c=xo();c.x=e.x,c.y=e.y,c.fill=e.fill,c.width=n.width,c.height=n.height,c.class="task task-type-"+e.num,c.rx=3,c.ry=3,yo(i,c);var u=e.x+14;e.people.forEach((function(t){var n=e.actors[t],r={cx:u,cy:e.y,r:7,fill:n,stroke:"#000",title:t};vo(i,r),u+=10})),_o(n)(e.task,i,c.x,c.y,c.width,c.height,{class:"task"},n,e.colour)},Co=function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",5).attr("refY",2).attr("markerWidth",6).attr("markerHeight",4).attr("orient","auto").append("path").attr("d","M 0,0 V 4 L6,2 Z")};ao.parser.yy=go;var Ao={leftMargin:150,diagramMarginX:50,diagramMarginY:20,taskMargin:50,width:150,height:50,taskFontSize:14,taskFontFamily:'"Open-Sans", "sans-serif"',boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"]},So={};var Mo=Ao.leftMargin,Oo={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],init:function(){this.sequenceItems=[],this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i,a=this,o=0;this.sequenceItems.forEach((function(s){o++;var c=a.sequenceItems.length-o+1;a.updateVal(s,"starty",e-c*Ao.boxMargin,Math.min),a.updateVal(s,"stopy",r+c*Ao.boxMargin,Math.max),a.updateVal(Oo.data,"startx",t-c*Ao.boxMargin,Math.min),a.updateVal(Oo.data,"stopx",n+c*Ao.boxMargin,Math.max),"activation"!==i&&(a.updateVal(s,"startx",t-c*Ao.boxMargin,Math.min),a.updateVal(s,"stopx",n+c*Ao.boxMargin,Math.max),a.updateVal(Oo.data,"starty",e-c*Ao.boxMargin,Math.min),a.updateVal(Oo.data,"stopy",r+c*Ao.boxMargin,Math.max))}))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(Oo.data,"startx",i,Math.min),this.updateVal(Oo.data,"starty",o,Math.min),this.updateVal(Oo.data,"stopx",a,Math.max),this.updateVal(Oo.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return this.data}},Do=Ao.sectionFills,No=Ao.sectionColours,Bo=function(t,e,n){for(var r="",i=n+(2*Ao.height+Ao.diagramMarginY),a=0,o="#CCC",s="black",c=0,u=0;u<e.length;u++){var l=e[u];if(r!==l.section){o=Do[a%Do.length],c=a%Do.length,s=No[a%No.length];var h={x:u*Ao.taskMargin+u*Ao.width+Mo,y:50,text:l.section,fill:o,num:c,colour:s};wo(t,h,Ao),r=l.section,a++}var f=l.people.reduce((function(t,e){return So[e]&&(t[e]=So[e]),t}),{});l.x=u*Ao.taskMargin+u*Ao.width+Mo,l.y=i,l.width=Ao.diagramMarginX,l.height=Ao.diagramMarginY,l.colour=s,l.fill=o,l.num=c,l.actors=f,To(t,l,Ao),Oo.insert(l.x,l.y,l.x+l.width+Ao.taskMargin,450)}},Lo=function(t){Object.keys(t).forEach((function(e){Ao[e]=t[e]}))},Po=function(t,e){ao.parser.yy.clear(),ao.parser.parse(t+"\n"),Oo.init();var n=Object(d.select)("#"+e);n.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),Co(n);var r=ao.parser.yy.getTasks(),i=ao.parser.yy.getTitle(),a=ao.parser.yy.getActors();for(var o in So)delete So[o];var s=0;a.forEach((function(t){So[t]=Ao.actorColours[s%Ao.actorColours.length],s++})),function(t){var e=60;Object.keys(So).forEach((function(n){var r=So[n];ko(t,{cx:20,cy:e,r:7,fill:r,stroke:"#000"});var i={x:40,y:e+7,fill:"#666",text:n,textMargin:5|Ao.boxTextMargin};Eo(t,i),e+=20}))}(n),Oo.insert(0,0,Mo,50*Object.keys(So).length),Bo(n,r,0);var c=Oo.getBounds();i&&n.append("text").text(i).attr("x",Mo).attr("font-size","4ex").attr("font-weight","bold").attr("y",25);var u=c.stopy-c.starty+2*Ao.diagramMarginY,l=Mo+c.stopx+2*Ao.diagramMarginX;W(n,u,l,Ao.useMaxWidth),n.append("line").attr("x1",Mo).attr("y1",4*Ao.height).attr("x2",l-Mo-4).attr("y2",4*Ao.height).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#arrowhead)");var h=i?70:0;n.attr("viewBox","".concat(c.startx," -25 ").concat(l," ").concat(u+h)),n.attr("preserveAspectRatio","xMinYMin meet")},Io=function(t){return"g.classGroup text {\n fill: ".concat(t.nodeBorder,";\n fill: ").concat(t.classText,";\n stroke: none;\n font-family: ").concat(t.fontFamily,";\n font-size: 10px;\n\n .title {\n font-weight: bolder;\n }\n\n}\n\n.classTitle {\n font-weight: bolder;\n}\n.node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n\n.divider {\n stroke: ").concat(t.nodeBorder,";\n stroke: 1;\n}\n\ng.clickable {\n cursor: pointer;\n}\n\ng.classGroup rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n}\n\ng.classGroup line {\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1;\n}\n\n.classLabel .box {\n stroke: none;\n stroke-width: 0;\n fill: ").concat(t.mainBkg,";\n opacity: 0.5;\n}\n\n.classLabel .label {\n fill: ").concat(t.nodeBorder,";\n font-size: 10px;\n}\n\n.relation {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n fill: none;\n}\n\n.dashed-line{\n stroke-dasharray: 3;\n}\n\n#compositionStart, .composition {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#compositionEnd, .composition {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#extensionStart, .extension {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#extensionEnd, .extension {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#aggregationStart, .aggregation {\n fill: ").concat(t.mainBkg," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#aggregationEnd, .aggregation {\n fill: ").concat(t.mainBkg," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n.edgeTerminals {\n font-size: 11px;\n}\n\n")},Fo=function(t){return".label {\n font-family: ".concat(t.fontFamily,";\n color: ").concat(t.nodeTextColor||t.textColor,";\n }\n\n .label text {\n fill: ").concat(t.nodeTextColor||t.textColor,";\n }\n\n .node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n .node .label {\n text-align: center;\n }\n .node.clickable {\n cursor: pointer;\n }\n\n .arrowheadPath {\n fill: ").concat(t.arrowheadColor,";\n }\n\n .edgePath .path {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1.5px;\n }\n\n .flowchart-link {\n stroke: ").concat(t.lineColor,";\n fill: none;\n }\n\n .edgeLabel {\n background-color: ").concat(t.edgeLabelBackground,";\n rect {\n opacity: 0.5;\n background-color: ").concat(t.edgeLabelBackground,";\n fill: ").concat(t.edgeLabelBackground,";\n }\n text-align: center;\n }\n\n .cluster rect {\n fill: ").concat(t.clusterBkg,";\n stroke: ").concat(t.clusterBorder,";\n stroke-width: 1px;\n }\n\n .cluster text {\n fill: ").concat(t.titleColor,";\n }\n\n div.mermaidTooltip {\n position: absolute;\n text-align: center;\n max-width: 200px;\n padding: 2px;\n font-family: ").concat(t.fontFamily,";\n font-size: 12px;\n background: ").concat(t.tertiaryColor,";\n border: 1px solid ").concat(t.border2,";\n border-radius: 2px;\n pointer-events: none;\n z-index: 100;\n }\n")},jo=function(t){return"g.stateGroup text {\n fill: ".concat(t.nodeBorder,";\n stroke: none;\n font-size: 10px;\n}\ng.stateGroup text {\n fill: ").concat(t.textColor,";\n stroke: none;\n font-size: 10px;\n\n}\ng.stateGroup .state-title {\n font-weight: bolder;\n fill: ").concat(t.labelColor,";\n}\n\ng.stateGroup rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n}\n\ng.stateGroup line {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n}\n\n.transition {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n fill: none;\n}\n\n.stateGroup .composit {\n fill: ").concat(t.background,";\n border-bottom: 1px\n}\n\n.stateGroup .alt-composit {\n fill: #e0e0e0;\n border-bottom: 1px\n}\n\n.state-note {\n stroke: ").concat(t.noteBorderColor,";\n fill: ").concat(t.noteBkgColor,";\n\n text {\n fill: black;\n stroke: none;\n font-size: 10px;\n }\n}\n\n.stateLabel .box {\n stroke: none;\n stroke-width: 0;\n fill: ").concat(t.mainBkg,";\n opacity: 0.5;\n}\n\n.edgeLabel .label rect {\n fill: ").concat(t.tertiaryColor,";\n opacity: 0.5;\n}\n.edgeLabel .label text {\n fill: ").concat(t.tertiaryTextColor,";\n}\n.label div .edgeLabel {\n color: ").concat(t.tertiaryTextColor,";\n}\n\n.stateLabel text {\n fill: ").concat(t.labelColor,";\n font-size: 10px;\n font-weight: bold;\n}\n\n.node circle.state-start {\n fill: ").concat(t.lineColor,";\n stroke: black;\n}\n.node circle.state-end {\n fill: ").concat(t.primaryBorderColor,";\n stroke: ").concat(t.background,";\n stroke-width: 1.5\n}\n.end-state-inner {\n fill: ").concat(t.background,";\n // stroke: ").concat(t.background,";\n stroke-width: 1.5\n}\n\n.node rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n}\n#statediagram-barbEnd {\n fill: ").concat(t.lineColor,";\n}\n\n.statediagram-cluster rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n}\n\n.cluster-label, .nodeLabel {\n color: ").concat(t.textColor,";\n}\n\n.statediagram-cluster rect.outer {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-state .divider {\n stroke: ").concat(t.nodeBorder,";\n}\n\n.statediagram-state .title-state {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-cluster.statediagram-cluster .inner {\n fill: ").concat(t.background,";\n}\n.statediagram-cluster.statediagram-cluster-alt .inner {\n fill: #e0e0e0;\n}\n\n.statediagram-cluster .inner {\n rx:0;\n ry:0;\n}\n\n.statediagram-state rect.basic {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-state rect.divider {\n stroke-dasharray: 10,10;\n fill: ").concat(t.altBackground?t.altBackground:"#efefef",";\n}\n\n.note-edge {\n stroke-dasharray: 5;\n}\n\n.statediagram-note rect {\n fill: ").concat(t.noteBkgColor,";\n stroke: ").concat(t.noteBorderColor,";\n stroke-width: 1px;\n rx: 0;\n ry: 0;\n}\n.statediagram-note rect {\n fill: ").concat(t.noteBkgColor,";\n stroke: ").concat(t.noteBorderColor,";\n stroke-width: 1px;\n rx: 0;\n ry: 0;\n}\n\n.statediagram-note text {\n fill: ").concat(t.noteTextColor,";\n}\n\n.statediagram-note .nodeLabel {\n color: ").concat(t.noteTextColor,";\n}\n\n#dependencyStart, #dependencyEnd {\n fill: ").concat(t.lineColor,";\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n}\n")},Ro={flowchart:Fo,"flowchart-v2":Fo,sequence:function(t){return".actor {\n stroke: ".concat(t.actorBorder,";\n fill: ").concat(t.actorBkg,";\n }\n\n text.actor > tspan {\n fill: ").concat(t.actorTextColor,";\n stroke: none;\n }\n\n .actor-line {\n stroke: ").concat(t.actorLineColor,";\n }\n\n .messageLine0 {\n stroke-width: 1.5;\n stroke-dasharray: none;\n stroke: ").concat(t.signalColor,";\n }\n\n .messageLine1 {\n stroke-width: 1.5;\n stroke-dasharray: 2, 2;\n stroke: ").concat(t.signalColor,";\n }\n\n #arrowhead path {\n fill: ").concat(t.signalColor,";\n stroke: ").concat(t.signalColor,";\n }\n\n .sequenceNumber {\n fill: ").concat(t.sequenceNumberColor,";\n }\n\n #sequencenumber {\n fill: ").concat(t.signalColor,";\n }\n\n #crosshead path {\n fill: ").concat(t.signalColor,";\n stroke: ").concat(t.signalColor,";\n }\n\n .messageText {\n fill: ").concat(t.signalTextColor,";\n stroke: ").concat(t.signalTextColor,";\n }\n\n .labelBox {\n stroke: ").concat(t.labelBoxBorderColor,";\n fill: ").concat(t.labelBoxBkgColor,";\n }\n\n .labelText, .labelText > tspan {\n fill: ").concat(t.labelTextColor,";\n stroke: none;\n }\n\n .loopText, .loopText > tspan {\n fill: ").concat(t.loopTextColor,";\n stroke: none;\n }\n\n .loopLine {\n stroke-width: 2px;\n stroke-dasharray: 2, 2;\n stroke: ").concat(t.labelBoxBorderColor,";\n fill: ").concat(t.labelBoxBorderColor,";\n }\n\n .note {\n //stroke: #decc93;\n stroke: ").concat(t.noteBorderColor,";\n fill: ").concat(t.noteBkgColor,";\n }\n\n .noteText, .noteText > tspan {\n fill: ").concat(t.noteTextColor,";\n stroke: none;\n }\n\n .activation0 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n\n .activation1 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n\n .activation2 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n")},gantt:function(t){return'\n .mermaid-main-font {\n font-family: "trebuchet ms", verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n\n .section {\n stroke: none;\n opacity: 0.2;\n }\n\n .section0 {\n fill: '.concat(t.sectionBkgColor,";\n }\n\n .section2 {\n fill: ").concat(t.sectionBkgColor2,";\n }\n\n .section1,\n .section3 {\n fill: ").concat(t.altSectionBkgColor,";\n opacity: 0.2;\n }\n\n .sectionTitle0 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle1 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle2 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle3 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle {\n text-anchor: start;\n font-size: 11px;\n text-height: 14px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n\n }\n\n\n /* Grid and axis */\n\n .grid .tick {\n stroke: ").concat(t.gridColor,";\n opacity: 0.8;\n shape-rendering: crispEdges;\n text {\n font-family: ").concat(t.fontFamily,";\n fill: ").concat(t.textColor,";\n }\n }\n\n .grid path {\n stroke-width: 0;\n }\n\n\n /* Today line */\n\n .today {\n fill: none;\n stroke: ").concat(t.todayLineColor,";\n stroke-width: 2px;\n }\n\n\n /* Task styling */\n\n /* Default task */\n\n .task {\n stroke-width: 2;\n }\n\n .taskText {\n text-anchor: middle;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n\n .taskText:not([font-size]) {\n font-size: 11px;\n }\n\n .taskTextOutsideRight {\n fill: ").concat(t.taskTextDarkColor,";\n text-anchor: start;\n font-size: 11px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n\n }\n\n .taskTextOutsideLeft {\n fill: ").concat(t.taskTextDarkColor,";\n text-anchor: end;\n font-size: 11px;\n }\n\n /* Special case clickable */\n .task.clickable {\n cursor: pointer;\n }\n .taskText.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n .taskTextOutsideLeft.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n .taskTextOutsideRight.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n /* Specific task settings for the sections*/\n\n .taskText0,\n .taskText1,\n .taskText2,\n .taskText3 {\n fill: ").concat(t.taskTextColor,";\n }\n\n .task0,\n .task1,\n .task2,\n .task3 {\n fill: ").concat(t.taskBkgColor,";\n stroke: ").concat(t.taskBorderColor,";\n }\n\n .taskTextOutside0,\n .taskTextOutside2\n {\n fill: ").concat(t.taskTextOutsideColor,";\n }\n\n .taskTextOutside1,\n .taskTextOutside3 {\n fill: ").concat(t.taskTextOutsideColor,";\n }\n\n\n /* Active task */\n\n .active0,\n .active1,\n .active2,\n .active3 {\n fill: ").concat(t.activeTaskBkgColor,";\n stroke: ").concat(t.activeTaskBorderColor,";\n }\n\n .activeText0,\n .activeText1,\n .activeText2,\n .activeText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n\n /* Completed task */\n\n .done0,\n .done1,\n .done2,\n .done3 {\n stroke: ").concat(t.doneTaskBorderColor,";\n fill: ").concat(t.doneTaskBkgColor,";\n stroke-width: 2;\n }\n\n .doneText0,\n .doneText1,\n .doneText2,\n .doneText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n\n /* Tasks on the critical line */\n\n .crit0,\n .crit1,\n .crit2,\n .crit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.critBkgColor,";\n stroke-width: 2;\n }\n\n .activeCrit0,\n .activeCrit1,\n .activeCrit2,\n .activeCrit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.activeTaskBkgColor,";\n stroke-width: 2;\n }\n\n .doneCrit0,\n .doneCrit1,\n .doneCrit2,\n .doneCrit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.doneTaskBkgColor,";\n stroke-width: 2;\n cursor: pointer;\n shape-rendering: crispEdges;\n }\n\n .milestone {\n transform: rotate(45deg) scale(0.8,0.8);\n }\n\n .milestoneText {\n font-style: italic;\n }\n .doneCritText0,\n .doneCritText1,\n .doneCritText2,\n .doneCritText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n .activeCritText0,\n .activeCritText1,\n .activeCritText2,\n .activeCritText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n .titleText {\n text-anchor: middle;\n font-size: 18px;\n fill: ").concat(t.textColor," ;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n")},classDiagram:Io,"classDiagram-v2":Io,class:Io,stateDiagram:jo,state:jo,git:function(){return"\n .commit-id,\n .commit-msg,\n .branch-label {\n fill: lightgrey;\n color: lightgrey;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n"},info:function(){return""},pie:function(t){return".pieTitleText {\n text-anchor: middle;\n font-size: 25px;\n fill: ".concat(t.taskTextDarkColor,";\n font-family: ").concat(t.fontFamily,";\n }\n .slice {\n font-family: ").concat(t.fontFamily,";\n fill: ").concat(t.textColor,";\n // fill: white;\n }\n .legend text {\n fill: ").concat(t.taskTextDarkColor,";\n font-family: ").concat(t.fontFamily,";\n font-size: 17px;\n }\n")},er:function(t){return"\n .entityBox {\n fill: ".concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n }\n\n .attributeBoxOdd {\n fill: #ffffff;\n stroke: ").concat(t.nodeBorder,";\n }\n\n .attributeBoxEven {\n fill: #f2f2f2;\n stroke: ").concat(t.nodeBorder,";\n }\n\n .relationshipLabelBox {\n fill: ").concat(t.tertiaryColor,";\n opacity: 0.7;\n background-color: ").concat(t.tertiaryColor,";\n rect {\n opacity: 0.5;\n }\n }\n\n .relationshipLine {\n stroke: ").concat(t.lineColor,";\n }\n")},journey:function(t){return".label {\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n color: ".concat(t.textColor,";\n }\n .mouth {\n stroke: #666;\n }\n\n line {\n stroke: ").concat(t.textColor,"\n }\n\n .legend {\n fill: ").concat(t.textColor,";\n }\n\n .label text {\n fill: #333;\n }\n .label {\n color: ").concat(t.textColor,"\n }\n\n .face {\n fill: #FFF8DC;\n stroke: #999;\n }\n\n .node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n .node .label {\n text-align: center;\n }\n .node.clickable {\n cursor: pointer;\n }\n\n .arrowheadPath {\n fill: ").concat(t.arrowheadColor,";\n }\n\n .edgePath .path {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1.5px;\n }\n\n .flowchart-link {\n stroke: ").concat(t.lineColor,";\n fill: none;\n }\n\n .edgeLabel {\n background-color: ").concat(t.edgeLabelBackground,";\n rect {\n opacity: 0.5;\n }\n text-align: center;\n }\n\n .cluster rect {\n }\n\n .cluster text {\n fill: ").concat(t.titleColor,";\n }\n\n div.mermaidTooltip {\n position: absolute;\n text-align: center;\n max-width: 200px;\n padding: 2px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n font-size: 12px;\n background: ").concat(t.tertiaryColor,";\n border: 1px solid ").concat(t.border2,";\n border-radius: 2px;\n pointer-events: none;\n z-index: 100;\n }\n\n .task-type-0, .section-type-0 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType0):"",";\n }\n .task-type-1, .section-type-1 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType1):"",";\n }\n .task-type-2, .section-type-2 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType2):"",";\n }\n .task-type-3, .section-type-3 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType3):"",";\n }\n .task-type-4, .section-type-4 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType4):"",";\n }\n .task-type-5, .section-type-5 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType5):"",";\n }\n .task-type-6, .section-type-6 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType6):"",";\n }\n .task-type-7, .section-type-7 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType7):"",";\n }\n")}},Yo=function(t,e,n){return" {\n font-family: ".concat(n.fontFamily,";\n font-size: ").concat(n.fontSize,";\n fill: ").concat(n.textColor,"\n }\n\n /* Classes common for multiple diagrams */\n\n .error-icon {\n fill: ").concat(n.errorBkgColor,";\n }\n .error-text {\n fill: ").concat(n.errorTextColor,";\n stroke: ").concat(n.errorTextColor,";\n }\n\n .edge-thickness-normal {\n stroke-width: 2px;\n }\n .edge-thickness-thick {\n stroke-width: 3.5px\n }\n .edge-pattern-solid {\n stroke-dasharray: 0;\n }\n\n .edge-pattern-dashed{\n stroke-dasharray: 3;\n }\n .edge-pattern-dotted {\n stroke-dasharray: 2;\n }\n\n .marker {\n fill: ").concat(n.lineColor,";\n }\n .marker.cross {\n stroke: ").concat(n.lineColor,";\n }\n\n svg {\n font-family: ").concat(n.fontFamily,";\n font-size: ").concat(n.fontSize,";\n }\n\n ").concat(Ro[t](n),"\n\n ").concat(e,"\n\n ").concat(t," { fill: apa;}\n")};function zo(t){return(zo="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var Uo={},$o=function(t,e,n){switch(c.debug("Directive type=".concat(e.type," with args:"),e.args),e.type){case"init":case"initialize":["config"].forEach((function(t){void 0!==e.args[t]&&("flowchart-v2"===n&&(n="flowchart"),e.args[n]=e.args[t],delete e.args[t])})),e.args,wt(e.args);break;case"wrap":case"nowrap":t&&t.setWrap&&t.setWrap("wrap"===e.type);break;default:c.warn("Unhandled directive: source: '%%{".concat(e.type,": ").concat(JSON.stringify(e.args?e.args:{}),"}%%"),e)}};function Wo(t){ka(t.git),me(t.flowchart),Ln(t.flowchart),void 0!==t.sequenceDiagram&&_r.setConf(F(t.sequence,t.sequenceDiagram)),_r.setConf(t.sequence),ri(t.gantt),li(t.class),zi(t.state),qi(t.state),Oa(t.class),za(t.class),ro(t.er),Lo(t.journey),Ba(t.class)}function Ho(){}var Vo=Object.freeze({render:function(t,e,n,r){Et();var i=e,a=H.detectInit(i);a&&wt(a);var o=_t();if(e.length>o.maxTextSize&&(i="graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa"),void 0!==r)r.innerHTML="",Object(d.select)(r).append("div").attr("id","d"+t).attr("style","font-family: "+o.fontFamily).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g");else{var s=document.getElementById(t);s&&s.remove();var u=document.querySelector("#d"+t);u&&u.remove(),Object(d.select)("body").append("div").attr("id","d"+t).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g")}window.txt=i,i=function(t){var e=t;return e=(e=(e=e.replace(/style.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)}))).replace(/classDef.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)}))).replace(/#\w+;/g,(function(t){var e=t.substring(1,t.length-1);return/^\+?\d+$/.test(e)?"fl°°"+e+"¶ß":"fl°"+e+"¶ß"}))}(i);var l=Object(d.select)("#d"+t).node(),h=H.detectType(i),g=l.firstChild,y=g.firstChild,v="";if(void 0!==o.themeCSS&&(v+="\n".concat(o.themeCSS)),void 0!==o.fontFamily&&(v+="\n:root { --mermaid-font-family: ".concat(o.fontFamily,"}")),void 0!==o.altFontFamily&&(v+="\n:root { --mermaid-alt-font-family: ".concat(o.altFontFamily,"}")),"flowchart"===h||"flowchart-v2"===h||"graph"===h){var m=be(i);for(var b in m)v+="\n.".concat(b," > * { ").concat(m[b].styles.join(" !important; ")," !important; }"),m[b].textStyles&&(v+="\n.".concat(b," tspan { ").concat(m[b].textStyles.join(" !important; ")," !important; }"))}var x=(new f.a)("#".concat(t),Yo(h,v,o.themeVariables)),_=document.createElement("style");_.innerHTML=x,g.insertBefore(_,y);try{switch(h){case"git":o.flowchart.arrowMarkerAbsolute=o.arrowMarkerAbsolute,ka(o.git),wa(i,t,!1);break;case"flowchart":o.flowchart.arrowMarkerAbsolute=o.arrowMarkerAbsolute,me(o.flowchart),xe(i,t,!1);break;case"flowchart-v2":o.flowchart.arrowMarkerAbsolute=o.arrowMarkerAbsolute,Ln(o.flowchart),Pn(i,t,!1);break;case"sequence":o.sequence.arrowMarkerAbsolute=o.arrowMarkerAbsolute,o.sequenceDiagram?(_r.setConf(Object.assign(o.sequence,o.sequenceDiagram)),console.error("`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.")):_r.setConf(o.sequence),_r.draw(i,t);break;case"gantt":o.gantt.arrowMarkerAbsolute=o.arrowMarkerAbsolute,ri(o.gantt),ii(i,t);break;case"class":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,li(o.class),hi(i,t);break;case"classDiagram":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,di(o.class),pi(i,t);break;case"state":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,zi(o.state),Ui(i,t);break;case"stateDiagram":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,qi(o.state),Xi(i,t);break;case"info":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,Oa(o.class),Da(i,t,p.version);break;case"pie":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,za(o.pie),Ua(i,t,p.version);break;case"er":ro(o.er),io(i,t,p.version);break;case"journey":Lo(o.journey),Po(i,t,p.version)}}catch(e){throw La(t,p.version),e}Object(d.select)('[id="'.concat(t,'"]')).selectAll("foreignobject > *").attr("xmlns","http://www.w3.org/1999/xhtml");var k=Object(d.select)("#d"+t).node().innerHTML;if(c.debug("cnf.arrowMarkerAbsolute",o.arrowMarkerAbsolute),o.arrowMarkerAbsolute&&"false"!==o.arrowMarkerAbsolute||(k=k.replace(/marker-end="url\(.*?#/g,'marker-end="url(#',"g")),k=(k=function(t){var e=t;return e=(e=(e=e.replace(/fl°°/g,(function(){return"&#"}))).replace(/fl°/g,(function(){return"&"}))).replace(/¶ß/g,(function(){return";"}))}(k)).replace(/<br>/g,"<br/>"),void 0!==n)switch(h){case"flowchart":case"flowchart-v2":n(k,Xt.bindFunctions);break;case"gantt":n(k,Qr.bindFunctions);break;case"class":case"classDiagram":n(k,cn.bindFunctions);break;default:n(k)}else c.debug("CB = undefined!");var w=Object(d.select)("#d"+t).node();return null!==w&&"function"==typeof w.remove&&Object(d.select)("#d"+t).node().remove(),k},parse:function(t){var e=H.detectInit(t);e&&c.debug("reinit ",e);var n,r=H.detectType(t);switch(c.debug("Type "+r),r){case"git":(n=ha.a).parser.yy=ua;break;case"flowchart":case"flowchart-v2":Xt.clear(),(n=Jt.a).parser.yy=Xt;break;case"sequence":(n=Hn.a).parser.yy=sr;break;case"gantt":(n=wr.a).parser.yy=Qr;break;case"class":case"classDiagram":(n=oi.a).parser.yy=cn;break;case"state":case"stateDiagram":(n=Di.a).parser.yy=Mi;break;case"info":c.debug("info info info"),(n=Sa.a).parser.yy=Ca;break;case"pie":c.debug("pie"),(n=Ra.a).parser.yy=Fa;break;case"er":c.debug("er"),(n=Xa.a).parser.yy=Ga;break;case"journey":c.debug("Journey"),(n=oo.a).parser.yy=go}return n.parser.yy.graphType=r,n.parser.yy.parseError=function(t,e){throw{str:t,hash:e}},n.parse(t),n},parseDirective:function(t,e,n,r){try{if(void 0!==e)switch(e=e.trim(),n){case"open_directive":Uo={};break;case"type_directive":Uo.type=e.toLowerCase();break;case"arg_directive":Uo.args=JSON.parse(e);break;case"close_directive":$o(t,Uo,r),Uo=null}}catch(t){c.error("Error while rendering sequenceDiagram directive: ".concat(e," jison context: ").concat(n)),c.error(t.message)}},initialize:function(t){t&&t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),dt=F({},t),t&&t.theme&&ht[t.theme]?t.themeVariables=ht[t.theme].getThemeVariables(t.themeVariables):t&&(t.themeVariables=ht.default.getThemeVariables(t.themeVariables));var e="object"===zo(t)?function(t){return yt=F({},gt),yt=F(yt,t),t.theme&&(yt.themeVariables=ht[t.theme].getThemeVariables(t.themeVariables)),mt=bt(yt,vt),yt}(t):xt();Wo(e),u(e.logLevel)},reinitialize:Ho,getConfig:_t,setConfig:function(t){return F(mt,t),_t()},getSiteConfig:xt,updateSiteConfig:function(t){return yt=F(yt,t),bt(yt,vt),yt},reset:function(){Et()},globalReset:function(){Et(),Wo(_t())},defaultConfig:gt});u(_t().logLevel),Et(_t());var Go=Vo,qo=function(){Xo.startOnLoad?Go.getConfig().startOnLoad&&Xo.init():void 0===Xo.startOnLoad&&(c.debug("In start, no config"),Go.getConfig().startOnLoad&&Xo.init())};"undefined"!=typeof document&& -/*! - * Wait for document loaded before starting the execution - */ -window.addEventListener("load",(function(){qo()}),!1);var Xo={startOnLoad:!0,htmlLabels:!0,mermaidAPI:Go,parse:Go.parse,render:Go.render,init:function(){var t,e,n=this,r=Go.getConfig();arguments.length>=2?( -/*! sequence config was passed as #1 */ -void 0!==arguments[0]&&(Xo.sequenceConfig=arguments[0]),t=arguments[1]):t=arguments[0],"function"==typeof arguments[arguments.length-1]?(e=arguments[arguments.length-1],c.debug("Callback function found")):void 0!==r.mermaid&&("function"==typeof r.mermaid.callback?(e=r.mermaid.callback,c.debug("Callback function found")):c.debug("No Callback function found")),t=void 0===t?document.querySelectorAll(".mermaid"):"string"==typeof t?document.querySelectorAll(t):t instanceof window.Node?[t]:t,c.debug("Start On Load before: "+Xo.startOnLoad),void 0!==Xo.startOnLoad&&(c.debug("Start On Load inner: "+Xo.startOnLoad),Go.updateSiteConfig({startOnLoad:Xo.startOnLoad})),void 0!==Xo.ganttConfig&&Go.updateSiteConfig({gantt:Xo.ganttConfig});for(var a,o=H.initIdGeneratior(r.deterministicIds,r.deterministicIDSeed).next,s=function(r){var s=t[r]; -/*! Check if previously processed */if(s.getAttribute("data-processed"))return"continue";s.setAttribute("data-processed",!0);var u="mermaid-".concat(o());a=i(a=s.innerHTML).trim().replace(/<br\s*\/?>/gi,"<br/>");var l=H.detectInit(a);l&&c.debug("Detected early reinit: ",l);try{Go.render(u,a,(function(t,n){s.innerHTML=t,void 0!==e&&e(u),n&&n(s)}),s)}catch(t){c.warn("Syntax Error rendering"),c.warn(t),n.parseError&&n.parseError(t)}},u=0;u<t.length;u++)s(u)},initialize:function(t){void 0!==t.mermaid&&(void 0!==t.mermaid.startOnLoad&&(Xo.startOnLoad=t.mermaid.startOnLoad),void 0!==t.mermaid.htmlLabels&&(Xo.htmlLabels=t.mermaid.htmlLabels)),Go.initialize(t)},contentLoaded:qo};e.default=Xo}]).default})); -//# sourceMappingURL=mermaid.min.js.map \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml deleted file mode 100644 index fef70aae..00000000 --- a/docs/sitemap.xml +++ /dev/null @@ -1,193 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" - xmlns:xhtml="http://www.w3.org/1999/xhtml"> - <url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/acknowledgements/</loc> - <priority>0</priority> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/</loc> - <priority>0</priority> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/categories/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1controller/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1em/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1fit/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1system/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/pages/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</loc> - </url><url> - <loc>https://stanley-rozell.github.io/lds-ctrl-est/tags/</loc> - </url> -</urlset> diff --git a/docs/svg/calendar.svg b/docs/svg/calendar.svg deleted file mode 100644 index f8481120..00000000 --- a/docs/svg/calendar.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z"/><path fill="none" d="M0 0h24v24H0z"/></svg> \ No newline at end of file diff --git a/docs/svg/edit.svg b/docs/svg/edit.svg deleted file mode 100644 index 5b54e693..00000000 --- a/docs/svg/edit.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/><path d="M0 0h24v24H0z" fill="none"/></svg> \ No newline at end of file diff --git a/docs/svg/menu.svg b/docs/svg/menu.svg deleted file mode 100644 index 770b1923..00000000 --- a/docs/svg/menu.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg> \ No newline at end of file diff --git a/docs/svg/toc.svg b/docs/svg/toc.svg deleted file mode 100644 index 1889904e..00000000 --- a/docs/svg/toc.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z"/><path d="M0 0h24v24H0z" fill="none"/></svg> \ No newline at end of file diff --git a/docs/svg/translate.svg b/docs/svg/translate.svg deleted file mode 100644 index a1bbe166..00000000 --- a/docs/svg/translate.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/></svg> \ No newline at end of file diff --git a/docs/tags/index.html b/docs/tags/index.html deleted file mode 100644 index 756d8684..00000000 --- a/docs/tags/index.html +++ /dev/null @@ -1,269 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <meta charset="UTF-8"> -<meta name="viewport" content="width=device-width, initial-scale=1.0"> -<meta name="description" content=""> -<meta name="theme-color" content="#FFFFFF"><meta property="og:title" content="Tags" /> -<meta property="og:description" content="" /> -<meta property="og:type" content="website" /> -<meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/tags/" /> - -<title>Tags | LDS C&amp;E</title> -<link rel="manifest" href="/lds-ctrl-est/manifest.json"> -<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> -<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> - <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> - <script defer src="/lds-ctrl-est/en.search.min.93b2d0e7fa872893809358a01a9e6e0c0ce7a384100570924726e75a52c21d85.js" integrity="sha256-k7LQ5/qHKJOAk1igGp5uDAzno4QQBXCSRybnWlLCHYU=" crossorigin="anonymous"></script> -<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/tags/index.xml" title="LDS C&E" /> -<!-- -Made with Book Theme -https://github.com/alex-shpak/hugo-book ---> - -</head> -<body dir="ltr"> - <input type="checkbox" class="hidden toggle" id="menu-control" /> - <input type="checkbox" class="hidden toggle" id="toc-control" /> - <main class="container flex"> - <aside class="book-menu"> - <div class="book-menu-content"> - - <nav> -<h2 class="book-brand"> - <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> - </a> -</h2> - - -<div class="book-search"> - <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> - <div class="book-search-spinner hidden"></div> - <ul id="book-search-results"></ul> -</div> - - - - - - - - - - - - <ul> -<li><strong>Library Terminology</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> -<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> -<ul> -<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><strong>Tutorials</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> -<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><strong>API Reference</strong> -<ul> -<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> -<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> -<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> -<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> -<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> -</ul> -</li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> -</ul> -<p><br /></p> -<ul> -<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> -</ul> -<p><br /></p> - - - - - - - -<ul> - - <li> - <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> - Github - </a> - </li> - -</ul> - - - - - - -</nav> - - - - - <script>(function(){var a=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(b){localStorage.setItem("menu.scrollTop",a.scrollTop)}),a.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> - - - - </div> - </aside> - - <div class="book-page"> - <header class="book-header"> - - <div class="flex align-center justify-between"> - <label for="menu-control"> - <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> - </label> - - <strong>Tags</strong> - - <label for="toc-control"> - - <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> - - </label> -</div> - - - - <aside class="hidden clearfix"> - - <nav> - <ul> - - - <li class="book-section-flat"> - <strong>Categories</strong> - <ul> - - </ul> - </li> - - - - <li class="book-section-flat"> - <strong>Tags</strong> - <ul> - - </ul> - </li> - - - </ul> -</nav> - - - </aside> - - - </header> - - - - - - - - - - <footer class="book-footer"> - - <div class="flex flex-wrap justify-between"> - - - - - -</div> - - - - <script>(function(){function a(c){const a=window.getSelection(),b=document.createRange();b.selectNodeContents(c),a.removeAllRanges(),a.addRange(b)}document.querySelectorAll("pre code").forEach(b=>{b.addEventListener("click",function(c){a(b.parentElement),navigator.clipboard&&navigator.clipboard.writeText(b.parentElement.textContent)})})})()</script> - - - - - </footer> - - - - - - <label for="menu-control" class="hidden book-menu-overlay"></label> - </div> - - - <aside class="book-toc"> - <div class="book-toc-content"> - - <nav> - <ul> - - - <li class="book-section-flat"> - <strong>Categories</strong> - <ul> - - </ul> - </li> - - - - <li class="book-section-flat"> - <strong>Tags</strong> - <ul> - - </ul> - </li> - - - </ul> -</nav> - - - </div> - </aside> - - </main> - - -</body> -</html> - - - - - - - - - - - - diff --git a/docs/tags/index.xml b/docs/tags/index.xml deleted file mode 100644 index 32090c56..00000000 --- a/docs/tags/index.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> - <channel> - <title>Tags on LDS C&amp;E</title> - <link>https://stanley-rozell.github.io/lds-ctrl-est/tags/</link> - <description>Recent content in Tags on LDS C&amp;E</description> - <generator>Hugo -- gohugo.io</generator><atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/tags/index.xml" rel="self" type="application/rss+xml" /> - </channel> -</rss> diff --git a/docs/tags/page/1/index.html b/docs/tags/page/1/index.html deleted file mode 100644 index cadd04da..00000000 --- a/docs/tags/page/1/index.html +++ /dev/null @@ -1 +0,0 @@ -<!DOCTYPE html><html><head><title>https://stanley-rozell.github.io/lds-ctrl-est/tags/</title><link rel="canonical" href="https://stanley-rozell.github.io/lds-ctrl-est/tags/"/><meta name="robots" content="noindex"><meta charset="utf-8" /><meta http-equiv="refresh" content="0; url=https://stanley-rozell.github.io/lds-ctrl-est/tags/" /></head></html> \ No newline at end of file diff --git a/misc/docs-hugo/.hugo_build.lock b/misc/docs-hugo/.hugo_build.lock new file mode 100644 index 00000000..e69de29b diff --git a/misc/docs-hugo/content/docs/api/Classes/_index.md b/misc/docs-hugo/content/docs/api/Classes/_index.md index 330aef00..e4e0dab9 100644 --- a/misc/docs-hugo/content/docs/api/Classes/_index.md +++ b/misc/docs-hugo/content/docs/api/Classes/_index.md @@ -1,126 +1,130 @@ ---- -title: Classes - ---- - -# Classes - - - - - - - - - - - - - - - - - -- **[lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - - - -- **[lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** - - - -- **[lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/)** <br>LDS [Fit]() Type. - - - -- **[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** - - - -- **[lds::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/)** <br>[SwitchedController]() Type. - - - -- **[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** <br>Linear Dynamical [System]() Type. - - - -- **[lds::UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)** - - - -- **[lds::UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/)** - - - -- **[lds::UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/)** - - - - - - -- **[lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/)** <br>Gaussian-observation [Controller]() Type. - - - -- **[lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/)** <br>GLDS [Fit]() Type. - - - -- **[lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/)** <br>GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/) Type. - - - -- **[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for GLDS. - - - -- **[lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/)** <br>Gaussian-observation [SwitchedController]() Type. - - - -- **[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)** <br>Gaussian LDS Type. - - - - - - - - - -- **[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)** <br>PLDS [Controller]() Type. - - - -- **[lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/)** <br>PLDS [Fit]() Type. - - - -- **[lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/)** <br>PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/) Type. - - - -- **[lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for PLDS. - - - -- **[lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/)** <br>Poisson-observation [SwitchedController]() Type. - - - -- **[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)** <br>Poisson [System]() type. - - - - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: Classes + +--- + +# Classes + + + + + + + + + + + + + + + + + +- **[lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + + + +- **[lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** + + + +- **[lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/)** <br>LDS [Fit]() Type. + + + +- **[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** + + + +- **[lds::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/)** <br>[SwitchedController]() Type. + + + +- **[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** <br>Linear Dynamical [System]() Type. + + + +- **[lds::UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)** + + + +- **[lds::UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/)** + + + +- **[lds::UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/)** + + + + + + +- **[lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/)** <br>Gaussian-observation [Controller]() Type. + + + +- **[lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/)** <br>GLDS [Fit]() Type. + + + +- **[lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/)** <br>GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/) Type. + + + +- **[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for GLDS. + + + +- **[lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/)** <br>Gaussian-observation [SwitchedController]() Type. + + + +- **[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)** <br>Gaussian LDS Type. + + + + + + + + + +- **[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)** <br>PLDS [Controller]() Type. + + + +- **[lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/)** <br>PLDS [Fit]() Type. + + + +- **[lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/)** <br>PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/) Type. + + + +- **[lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for PLDS. + + + +- **[lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/)** <br>Poisson-observation [SwitchedController]() Type. + + + +- **[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)** <br>Poisson [System]() type. + + + + + + + + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1Fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1Fit.md deleted file mode 100644 index 7dd0ce98..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1Fit.md +++ /dev/null @@ -1,561 +0,0 @@ ---- -title: lds::Fit -summary: LDS Fit Type. - ---- - -# lds::Fit - - - -LDS [Fit]() Type. -<br /> `#include <lds_fit.h>` - -Inherited by [lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/), [lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-fit)**() =default<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/). | -| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-fit)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt)<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/). | -| virtual | **[~Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-~fit)**() =default | -| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-u)**() const<br>gets number of inputs | -| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-x)**() const<br>gets number of states | -| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-y)**() const<br>gets number of outputs | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-dt)**() const<br>gets sample period | -| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-a)**() const<br>gets state matrix | -| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-b)**() const<br>gets input matrix | -| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-g)**() const<br>gets input gain | -| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-m)**() const<br>gets process disturbance | -| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-q)**() const<br>gets process noise covariance | -| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-x0)**() const<br>gets initial state estimate | -| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-p0)**() const<br>gets covariance of initial state estimate | -| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-c)**() const<br>gets output matrix | -| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-d)**() const<br>gets output bias | -| virtual const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-r)**() const =0 | -| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-a)**(const Matrix & A)<br>sets state matrix | -| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-b)**(const Matrix & B)<br>sets input matrix | -| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-g)**(const Vector & g)<br>sets input gain/conversion factor | -| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-m)**(const Vector & m)<br>sets process disturbance | -| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-q)**(const Matrix & Q)<br>sets process noise covariance | -| virtual void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-r)**(const Matrix & R) =0<br>sets output noise covariance (if any) | -| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-x0)**(const Vector & x0)<br>sets initial state estimate | -| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-p0)**(const Matrix & P0)<br>sets initial state estimate covariance | -| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-c)**(const Matrix & C)<br>sets output matrix | -| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-d)**(const Vector & d)<br>sets output bias | -| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-f)**(Matrix & x, const Matrix & u, size_t t)<br>system dynamics function | -| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-f)**(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)<br>system dynamics function | -| virtual View | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-h)**(Matrix & y, const Matrix & x, size_t t) =0<br>output function | - -## Protected Attributes - -| | Name | -| -------------- | -------------- | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-dt-)** <br>sample period | -| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-a-)** <br>state matrix | -| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-b-)** <br>input matrix | -| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-g-)** <br>input gain | -| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-m-)** <br>process noise mean | -| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-q-)** <br>process noise cov | -| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-c-)** <br>output matrix | -| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-d-)** <br>output bias | -| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-r-)** <br>measurement noise | -| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-x0-)** <br>initial state | -| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-p0-)** <br>initial covar | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-y-)** <br>number of outputs | - ---- ---- -## Public Function Details - -### **Fit** - -```cpp -Fit() =default -``` - - - ---- -### **Fit** - -```cpp -Fit( - size_t n_u, - size_t n_x, - size_t n_y, - data_t dt -) -``` - - - -**Parameters**: - - * **n_u** number of inputs - * **n_x** number of states - * **n_y** number of outputs - * **dt** sample period - - ---- -### **~Fit** - -```cpp -virtual ~Fit() =default -``` - - - ---- -### **n_u** - -```cpp -inline size_t n_u() const -``` - - - ---- -### **n_x** - -```cpp -inline size_t n_x() const -``` - - - ---- -### **n_y** - -```cpp -inline size_t n_y() const -``` - - - ---- -### **dt** - -```cpp -inline data_t dt() const -``` - - - ---- -### **A** - -```cpp -inline const Matrix & A() const -``` - - - ---- -### **B** - -```cpp -inline const Matrix & B() const -``` - - - ---- -### **g** - -```cpp -inline const Vector & g() const -``` - - - ---- -### **m** - -```cpp -inline const Vector & m() const -``` - - - ---- -### **Q** - -```cpp -inline const Matrix & Q() const -``` - - - ---- -### **x0** - -```cpp -inline const Vector & x0() const -``` - - - ---- -### **P0** - -```cpp -inline const Matrix & P0() const -``` - - - ---- -### **C** - -```cpp -inline const Matrix & C() const -``` - - - ---- -### **d** - -```cpp -inline const Vector & d() const -``` - - - ---- -### **R** - -```cpp -virtual const Matrix & R() const =0 -``` - - - -**Reimplemented by**: [lds::gaussian::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-r), [lds::poisson::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-r) - - ---- -### **set_A** - -```cpp -inline void set_A( - const Matrix & A -) -``` - - - ---- -### **set_B** - -```cpp -inline void set_B( - const Matrix & B -) -``` - - - ---- -### **set_g** - -```cpp -inline void set_g( - const Vector & g -) -``` - - - ---- -### **set_m** - -```cpp -inline void set_m( - const Vector & m -) -``` - - - ---- -### **set_Q** - -```cpp -inline void set_Q( - const Matrix & Q -) -``` - - - ---- -### **set_R** - -```cpp -virtual void set_R( - const Matrix & R -) =0 -``` - - - -**Reimplemented by**: [lds::gaussian::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-set-r), [lds::poisson::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-set-r) - - ---- -### **set_x0** - -```cpp -inline void set_x0( - const Vector & x0 -) -``` - - - ---- -### **set_P0** - -```cpp -inline void set_P0( - const Matrix & P0 -) -``` - - - ---- -### **set_C** - -```cpp -inline void set_C( - const Matrix & C -) -``` - - - ---- -### **set_d** - -```cpp -inline void set_d( - const Vector & d -) -``` - - - ---- -### **f** - -```cpp -inline View f( - Matrix & x, - const Matrix & u, - size_t t -) -``` - - - -**Parameters**: - - * **x** state estimate (over time) - * **u** input (over time) - * **t** time index - - -**Return**: view of updated state - ---- -### **f** - -```cpp -inline View f( - Matrix & x_pre, - const Matrix & x_post, - const Matrix & u, - size_t t -) -``` - - - -**Parameters**: - - * **x_pre** predicted state est. - * **x_post** posterior state est. - * **u** input (over time) - * **t** time index - - -**Return**: view of predicted state - ---- -### **h** - -```cpp -virtual View h( - Matrix & y, - const Matrix & x, - size_t t -) =0 -``` - - - -**Parameters**: - - * **y** output estimate (over time) - * **x** state estimate (over time) - * **t** time index - - -**Return**: output - -**Reimplemented by**: [lds::poisson::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-h), [lds::gaussian::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-h) - - ---- - - -## Protected Attribute Details - -### **dt_** - -```cpp -data_t dt_ {}; -``` - - - ---- -### **A_** - -```cpp -Matrix A_; -``` - - - ---- -### **B_** - -```cpp -Matrix B_; -``` - - - ---- -### **g_** - -```cpp -Vector g_; -``` - - - ---- -### **m_** - -```cpp -Vector m_; -``` - - - ---- -### **Q_** - -```cpp -Matrix Q_; -``` - - - ---- -### **C_** - -```cpp -Matrix C_; -``` - - - ---- -### **d_** - -```cpp -Vector d_; -``` - - - ---- -### **R_** - -```cpp -Matrix R_; -``` - - - ---- -### **x0_** - -```cpp -Vector x0_; -``` - - - ---- -### **P0_** - -```cpp -Matrix P0_; -``` - - - ---- -### **n_u_** - -```cpp -size_t n_u_ {}; -``` - - - ---- -### **n_x_** - -```cpp -size_t n_x_ {}; -``` - - - ---- -### **n_y_** - -```cpp -size_t n_y_ {}; -``` - - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1SSID.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1SSID.md deleted file mode 100644 index 3da039f5..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1SSID.md +++ /dev/null @@ -1,370 +0,0 @@ ---- -title: lds::SSID - ---- - -# lds::SSID - - - - [More...](#detailed-description) - -Inherited by [lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/), [lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-ssid)**() =default<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) type. | -| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-ssid)**(size_t n_x, size_t n_h, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train, const Vector & d =Vector(1).fill(-kInf))<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) type. | -| std::tuple< [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/), Vector > | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-run)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) ssid_wt)<br>Runs fitting by subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) | -| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-returndata)**()<br>Returns the I/O data to caller. | - -## Protected Functions - -| | Name | -| -------------- | -------------- | -| void | **[CalcD](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-calcd)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) t_silence =0.1, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d) | -| void | **[CreateHankelDataMat](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-createhankeldatamat)**()<br>Creates the block-hankel I/O data matrix. | -| virtual void | **[DecomposeData](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-decomposedata)**() =0<br>Decompose data to lower-triangular matrix (used in Solve) | -| void | **[CalcSVD](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-calcsvd)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) wt)<br>performs the singular value decomposition (SVD) | -| void | **[Solve](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-solve)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) wt_dc)<br>solves for LDS parameters | -| void | **[RecomputeExtObs](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-recomputeextobs)**()<br>recompute extended observability matrix from estimates of A, C | - -## Protected Attributes - -| | Name | -| -------------- | -------------- | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-u-)** <br>input training data | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-z-)** <br>measurement training data | -| Matrix | **[D_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-d-)** <br>block-Hankel I/O data matrix | -| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-fit-)** <br>fit | -| Matrix | **[g_dc_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-g-dc-)** <br>I/O gain @ DC. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-dt-)** <br>sample period | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-y-)** <br>number of outputs | -| size_t | **[n_h_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-h-)** | -| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-trials-)** <br>number of input/output data sequences | -| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-t-)** <br>number of time steps | -| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-t-tot-)** <br>total number of time steps across trials | -| Matrix | **[L_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-l-)** <br>lower triangle decomp of covariance matrix | -| Vector | **[s_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-s-)** <br>singular values | -| Matrix | **[ext_obs_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-ext-obs-t-)** <br>extended observability matrix | - -## Detailed Description - -```cpp -template <typename Fit > -class lds::SSID; -``` - - ---- ---- -## Public Function Details - -### **SSID** - -```cpp -SSID() =default -``` - - - ---- -### **SSID** - -```cpp -SSID( - size_t n_x, - size_t n_h, - data_t dt, - UniformMatrixList< kMatFreeDim2 > && u_train, - UniformMatrixList< kMatFreeDim2 > && z_train, - const Vector & d =Vector(1).fill(-kInf) -) -``` - - - -**Parameters**: - - * **n_x** number of states - * **n_h** size of block-hankel data matrix - * **dt** sample period - * **u_train** input training data - * **z_train** measurement training data - * **d** output bias - - ---- -### **Run** - -```cpp -std::tuple< Fit, Vector > Run( - SSIDWt ssid_wt -) -``` - - - -**Parameters**: - - * **ssid_wt** weight for singular value decomp - - -**Return**: tuple ([Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/), singular values) - ---- -### **ReturnData** - -```cpp -inline std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > > ReturnData() -``` - - - -**Return**: tuple(input data, output data) - ---- - - -## Protected Function Details - -### **CalcD** - -```cpp -void CalcD( - data_t t_silence =0.1, - data_t thresh_silence =0.001 -) -``` - - - -**Parameters**: - - * **t_silence** threshold on period of time that qualifies as "silence" - * **thresh_silence** threshold on input amplitude u that qualifies as "silence" - - ---- -### **CreateHankelDataMat** - -```cpp -void CreateHankelDataMat() -``` - - - -Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC. - - ---- -### **DecomposeData** - -```cpp -virtual void DecomposeData() =0 -``` - - - -**Reimplemented by**: [lds::poisson::FitSSID::DecomposeData](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/#function-decomposedata), [lds::gaussian::FitSSID::DecomposeData](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/#function-decomposedata) - - ---- -### **CalcSVD** - -```cpp -void CalcSVD( - SSIDWt wt -) -``` - - - -**Parameters**: - - * **ssid_wt** weight for SVD - - ---- -### **Solve** - -```cpp -void Solve( - data_t wt_dc -) -``` - - - -**Parameters**: - - * **wt_dc** weight placed on getting correct DC I/O gain - - ---- -### **RecomputeExtObs** - -```cpp -void RecomputeExtObs() -``` - - - ---- - - -## Protected Attribute Details - -### **u_** - -```cpp -UniformMatrixList< kMatFreeDim2 > u_; -``` - - - ---- -### **z_** - -```cpp -UniformMatrixList< kMatFreeDim2 > z_; -``` - - - ---- -### **D_** - -```cpp -Matrix D_; -``` - - - ---- -### **fit_** - -```cpp -Fit fit_; -``` - - - ---- -### **g_dc_** - -```cpp -Matrix g_dc_; -``` - - - ---- -### **dt_** - -```cpp -data_t dt_ {}; -``` - - - ---- -### **n_u_** - -```cpp -size_t n_u_ {}; -``` - - - ---- -### **n_x_** - -```cpp -size_t n_x_ {}; -``` - - - ---- -### **n_y_** - -```cpp -size_t n_y_ {}; -``` - - - ---- -### **n_h_** - -```cpp -size_t n_h_ {}; -``` - - - ---- -### **n_trials_** - -```cpp -size_t n_trials_ {}; -``` - - - ---- -### **n_t_** - -```cpp -std::vector< size_t > n_t_; -``` - - - ---- -### **n_t_tot_** - -```cpp -size_t n_t_tot_ {}; -``` - - - ---- -### **L_** - -```cpp -Matrix L_; -``` - - - ---- -### **s_** - -```cpp -Vector s_; -``` - - - ---- -### **ext_obs_t_** - -```cpp -Matrix ext_obs_t_; -``` - - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1SwitchedController.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1SwitchedController.md deleted file mode 100644 index a66dd9b5..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1SwitchedController.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -title: lds::SwitchedController -summary: SwitchedController Type. - ---- - -# lds::SwitchedController - - - -[SwitchedController]() Type. [More...](#detailed-description) - - -<br /> `#include <lds_sctrl.h>` - -Inherits from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) - -Inherited by [lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/), [lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**() =default<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/). | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > & systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/). | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > && systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/) (moves systems). | -| void | **[Switch](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switch)**(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller. | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc)<br>sets state feedback gains | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc)<br>sets state feedback gains (moving) | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-inty)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc_inty)<br>sets integral feedback gains | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-inty)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc_inty)<br>sets integral feedback gains (moving) | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-u)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc_u)<br>sets input feedback gains | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-u)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc_u)<br>sets input feedback gains (moving) | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-g-design)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) & g)<br>sets input gain used during controller design | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-g-design)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) && g)<br>sets input gain used during controller design (moving) | - -## Protected Attributes - -| | Name | -| -------------- | -------------- | -| std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > | **[systems_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-systems-)** <br>underlying sub-systems which are switched between | -| size_t | **[n_sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-n-sys-)** <br>number of systems | -| size_t | **[idx_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-idx-)** <br>current system/controller index. | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-list-)** | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_inty_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-inty-list-)** | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_u_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-u-list-)** | -| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) | **[g_design_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-g-design-list-)** | - -## Additional inherited members - -**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) by moving the system object. | -| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | -| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | -| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-sys)**() const | -| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc)**() const<br>Get state feedback controller gain. | -| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-inty)**() const<br>Get integral controller gain. | -| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-u)**() const<br>Get input feedback controller gain. | -| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-g-design)**() const<br>Get input gain used in controller design. | -| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ref)**() const<br>Get reference input. | -| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-x-ref)**() const<br>Get reference state. | -| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-y-ref)**() const<br>Get reference output. | -| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control-type)**() const<br>Get controller type. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-lb)**() const<br>Get control lower bound. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ub)**() const<br>Get control upper bound. | -| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys)<br>Set system. | -| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | -| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | -| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-y-ref)**(const Vector & y_ref)<br>Set reference output (y_ref) | -| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | -| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | -| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | -| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-reset)**()<br>reset system and control variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-print)**()<br>prints variables to stdout | - -**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-sys-)** <br>underlying LDS | -| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-)** <br>control signal | -| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-return-)** <br>control signal that is _returned_ to user | -| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-g-design-)** <br>input gain of the system used for controller design | -| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-)** <br>reference input | -| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-prev-)** <br>reference input at previous time step | -| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-x-ref-)** <br>reference state | -| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-y-ref-)** <br>reference output | -| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-cx-ref-)** | -| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-)** <br>state controller gain | -| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-u-)** <br>input controller gain (optional when control updates ) | -| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-inty-)** <br>integral controller gain | -| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-du-ref-)** | -| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-ref-)** | -| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-ref-)** | -| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-)** | -| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | -| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-)** <br>integrated error | -| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | -| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | -| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-control-prev-)** | -| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-lock-control-prev-)** | -| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-lb-)** <br>lower bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ub-)** <br>upper bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-tau-awu-)** <br>antiwindup time constant | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-k-awu-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | -| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-control-type-)** <br>controller type | - - -## Detailed Description - -```cpp -template <typename System > -class lds::SwitchedController; -``` - - ---- ---- -## Public Function Details - -### **SwitchedController** - -```cpp -SwitchedController() =default -``` - - - ---- -### **SwitchedController** - -```cpp -inline SwitchedController( - const std::vector< System > & systems, - data_t u_lb, - data_t u_ub, - size_t control_type =0 -) -``` - - - -**Parameters**: - - * **systems** vector of sub-systems - * **u_lb** lower bound on control (u) - * **u_ub** upper bound on control (u) - * **control_type** [optional] control type bit mask - - ---- -### **SwitchedController** - -```cpp -inline SwitchedController( - std::vector< System > && systems, - data_t u_lb, - data_t u_ub, - size_t control_type =0 -) -``` - - - -**Parameters**: - - * **systems** vector of sub-systems - * **u_lb** lower bound on control (u) - * **u_ub** upper bound on control (u) - * **control_type** [optional] control type bit mask - - ---- -### **Switch** - -```cpp -inline void Switch( - size_t idx, - bool do_force_switch =false -) -``` - - - -**Parameters**: - - * **idx** index - * **do_force_switch** whether to force a system switch even if already there. - - ---- -### **set_Kc** - -```cpp -inline void set_Kc( - const UniformMatrixList<> & Kc -) -``` - - - ---- -### **set_Kc** - -```cpp -inline void set_Kc( - UniformMatrixList<> && Kc -) -``` - - - ---- -### **set_Kc_inty** - -```cpp -inline void set_Kc_inty( - const UniformMatrixList<> & Kc_inty -) -``` - - - ---- -### **set_Kc_inty** - -```cpp -inline void set_Kc_inty( - UniformMatrixList<> && Kc_inty -) -``` - - - ---- -### **set_Kc_u** - -```cpp -inline void set_Kc_u( - const UniformMatrixList<> & Kc_u -) -``` - - - ---- -### **set_Kc_u** - -```cpp -inline void set_Kc_u( - UniformMatrixList<> && Kc_u -) -``` - - - ---- -### **set_g_design** - -```cpp -inline void set_g_design( - const UniformVectorList & g -) -``` - - - ---- -### **set_g_design** - -```cpp -inline void set_g_design( - UniformVectorList && g -) -``` - - - ---- - - -## Protected Attribute Details - -### **systems_** - -```cpp -std::vector< System > systems_; -``` - - - ---- -### **n_sys_** - -```cpp -size_t n_sys_ {}; -``` - - - ---- -### **idx_** - -```cpp -size_t idx_ {}; -``` - - - ---- -### **Kc_list_** - -```cpp -UniformMatrixList Kc_list_; -``` - - - ---- -### **Kc_inty_list_** - -```cpp -UniformMatrixList Kc_inty_list_; -``` - - - ---- -### **Kc_u_list_** - -```cpp -UniformMatrixList Kc_u_list_; -``` - - - ---- -### **g_design_list_** - -```cpp -UniformVectorList g_design_list_; -``` - - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1System.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1System.md deleted file mode 100644 index ef818466..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1System.md +++ /dev/null @@ -1,850 +0,0 @@ ---- -title: lds::System -summary: Linear Dynamical System Type. - ---- - -# lds::System - - - -Linear Dynamical [System]() Type. -<br /> `#include <lds_sys.h>` - -Inherited by [lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/), [lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-system)**() =default<br>Constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/). | -| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-system)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0)<br>constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) | -| virtual | **[~System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-~system)**() | -| void | **[Filter](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-filter)**(const Vector & u_tm1, const Vector & z)<br>Filter data to produce causal state estimates. | -| virtual const Vector & | **[Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-simulate)**(const Vector & u_tm1) =0<br>simulates system (single time step) | -| void | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-f)**(const Vector & u, bool do_add_noise =false)<br>system dynamics function | -| virtual void | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-h)**() =0<br>system output function | -| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-u)**() const<br>Get number of inputs. | -| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-x)**() const<br>Get number of states. | -| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-y)**() const<br>Get number of outputs. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-dt)**() const<br>Get sample period. | -| const Vector & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-x)**() const<br>Get current state. | -| const Matrix & | **[P](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p)**() const<br>Get covariance of state estimate. | -| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-m)**() const<br>Get current process disturbance/bias. | -| const Matrix & | **[P_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p-m)**() const<br>Get covariance of process disturbance estimate. | -| const Vector & | **[cx](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-cx)**() const<br>Get C*x. | -| const Vector & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-y)**() const<br>Get output. | -| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-x0)**() const<br>Get initial state. | -| const Vector & | **[m0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-m0)**() const<br>Get initial disturbance. | -| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-a)**() const<br>Get state matrix. | -| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-b)**() const<br>Get input matrix. | -| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-g)**() const<br>Get input gain/conversion factor. | -| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-c)**() const<br>Get output matrix. | -| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-d)**() const<br>Get output bias. | -| const Matrix & | **[Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-ke)**() const<br>Get estimator gain. | -| const Matrix & | **[Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-ke-m)**() const<br>Get estimator gain for process disturbance (m) | -| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-q)**()<br>Get process noise covariance. | -| const Matrix & | **[Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-q-m)**()<br>Get process noise covariance of disturbance evoluation. | -| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p0)**()<br>Get covariance of initial state. | -| const Matrix & | **[P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p0-m)**()<br>Get covariance of initial process disturbance. | -| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-a)**(const Matrix & A)<br>Set state matrix. | -| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-b)**(const Matrix & B)<br>Set input matrix. | -| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-m)**(const Vector & m, bool do_force_assign =false)<br>Set process disturbance. | -| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-g)**(const Vector & g)<br>Set input gain. | -| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-q)**(const Matrix & Q)<br>Set process noise covariance. | -| void | **[set_Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-q-m)**(const Matrix & Q_m)<br>Set process noise covariance of disturbance evoluation. | -| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-x0)**(const Vector & x0)<br>Set initial state. | -| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-p0)**(const Matrix & P0)<br>Set covariance of initial state. | -| void | **[set_P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-p0-m)**(const Matrix & P0_m)<br>Set covariance of initial process disturbance. | -| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-c)**(const Matrix & C)<br>Set output matrix. | -| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-d)**(const Vector & d)<br>Set output bias. | -| void | **[set_x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-x)**(const Vector & x)<br>Set state of system. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-reset)**()<br>Reset system variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-print)**()<br>Print system variables to stdout. | - -## Protected Functions - -| | Name | -| -------------- | -------------- | -| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-recurseke)**() =0<br>Recursively recalculate estimator gain (Ke) | -| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-initvars)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0) | - -## Public Attributes - -| | Name | -| -------------- | -------------- | -| bool | **[do_adapt_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-do-adapt-m)** <br>whether to adaptively estimate disturbance m | - -## Protected Attributes - -| | Name | -| -------------- | -------------- | -| std::size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-x-)** <br>number of states | -| std::size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-u-)** <br>number of inputs | -| std::size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-y-)** <br>number of outputs | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-dt-)** <br>sample period | -| Vector | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-x-)** <br>state | -| Matrix | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p-)** <br>covariance of state estimate | -| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-m-)** <br>process disturbance | -| Matrix | **[P_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p-m-)** <br>covariance of disturbance estimate | -| Vector | **[cx_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-cx-)** <br>C*x. | -| Vector | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-y-)** <br>output | -| Vector | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-z-)** <br>measurement | -| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-x0-)** <br>initial state | -| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p0-)** <br>covariance of initial state estimate | -| Vector | **[m0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-m0-)** <br>initial process disturbance | -| Matrix | **[P0_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p0-m-)** <br>covariance of initial disturbance est. | -| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-a-)** <br>state matrix | -| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-b-)** <br>input matrix | -| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-g-)** <br>input gain | -| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-q-)** <br>covariance of process noise | -| Matrix | **[Q_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-q-m-)** <br>covariance of disturbance random walk | -| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-c-)** <br>output matrix | -| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-d-)** <br>output bias | -| Matrix | **[Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-ke-)** <br>estimator gain | -| Matrix | **[Ke_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-ke-m-)** <br>estimator gain for process disturbance | - ---- ---- -## Public Function Details - -### **System** - -```cpp -System() =default -``` - - - ---- -### **System** - -```cpp -System( - size_t n_u, - size_t n_x, - size_t n_y, - data_t dt, - data_t p0 =kDefaultP0, - data_t q0 =kDefaultQ0 -) -``` - - - -**Parameters**: - - * **n_u** number of inputs - * **n_x** number of states - * **n_y** number of outputs - * **dt** sample period - * **p0** diagonal elements for state estimate covariance - * **q0** diagonal elements for process noise covariance - - ---- -### **~System** - -```cpp -inline virtual ~System() -``` - - - ---- -### **Filter** - -```cpp -void Filter( - const Vector & u_tm1, - const Vector & z -) -``` - - - -**Parameters**: - - * **u_tm1** input at t-minus-1 - * **z_t** current measurement - - -Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating. - - ---- -### **Simulate** - -```cpp -virtual const Vector & Simulate( - const Vector & u_tm1 -) =0 -``` - - - -**Parameters**: - - * **u_tm1** input at time t-1 - - -**Return**: simulated measurement at time t - -**Reimplemented by**: [lds::poisson::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-simulate), [lds::gaussian::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-simulate) - - ---- -### **f** - -```cpp -inline void f( - const Vector & u, - bool do_add_noise =false -) -``` - - - -**Parameters**: - - * **u** input - * **do_add_noise** whether to add simulated process noise - - ---- -### **h** - -```cpp -virtual void h() =0 -``` - - - -**Reimplemented by**: [lds::poisson::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-h), [lds::gaussian::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-h) - - ---- -### **n_u** - -```cpp -inline size_t n_u() const -``` - - - ---- -### **n_x** - -```cpp -inline size_t n_x() const -``` - - - ---- -### **n_y** - -```cpp -inline size_t n_y() const -``` - - - ---- -### **dt** - -```cpp -inline data_t dt() const -``` - - - ---- -### **x** - -```cpp -inline const Vector & x() const -``` - - - ---- -### **P** - -```cpp -inline const Matrix & P() const -``` - - - ---- -### **m** - -```cpp -inline const Vector & m() const -``` - - - ---- -### **P_m** - -```cpp -inline const Matrix & P_m() const -``` - - - ---- -### **cx** - -```cpp -inline const Vector & cx() const -``` - - - ---- -### **y** - -```cpp -inline const Vector & y() const -``` - - - ---- -### **x0** - -```cpp -inline const Vector & x0() const -``` - - - ---- -### **m0** - -```cpp -inline const Vector & m0() const -``` - - - ---- -### **A** - -```cpp -inline const Matrix & A() const -``` - - - ---- -### **B** - -```cpp -inline const Matrix & B() const -``` - - - ---- -### **g** - -```cpp -inline const Vector & g() const -``` - - - ---- -### **C** - -```cpp -inline const Matrix & C() const -``` - - - ---- -### **d** - -```cpp -inline const Vector & d() const -``` - - - ---- -### **Ke** - -```cpp -inline const Matrix & Ke() const -``` - - - ---- -### **Ke_m** - -```cpp -inline const Matrix & Ke_m() const -``` - - - ---- -### **Q** - -```cpp -inline const Matrix & Q() -``` - - - ---- -### **Q_m** - -```cpp -inline const Matrix & Q_m() -``` - - - ---- -### **P0** - -```cpp -inline const Matrix & P0() -``` - - - ---- -### **P0_m** - -```cpp -inline const Matrix & P0_m() -``` - - - ---- -### **set_A** - -```cpp -inline void set_A( - const Matrix & A -) -``` - - - ---- -### **set_B** - -```cpp -inline void set_B( - const Matrix & B -) -``` - - - ---- -### **set_m** - -```cpp -inline void set_m( - const Vector & m, - bool do_force_assign =false -) -``` - - - ---- -### **set_g** - -```cpp -inline void set_g( - const Vector & g -) -``` - - - ---- -### **set_Q** - -```cpp -inline void set_Q( - const Matrix & Q -) -``` - - - ---- -### **set_Q_m** - -```cpp -inline void set_Q_m( - const Matrix & Q_m -) -``` - - - ---- -### **set_x0** - -```cpp -inline void set_x0( - const Vector & x0 -) -``` - - - ---- -### **set_P0** - -```cpp -inline void set_P0( - const Matrix & P0 -) -``` - - - ---- -### **set_P0_m** - -```cpp -inline void set_P0_m( - const Matrix & P0_m -) -``` - - - ---- -### **set_C** - -```cpp -inline void set_C( - const Matrix & C -) -``` - - - ---- -### **set_d** - -```cpp -inline void set_d( - const Vector & d -) -``` - - - ---- -### **set_x** - -```cpp -inline void set_x( - const Vector & x -) -``` - - - ---- -### **Reset** - -```cpp -void Reset() -``` - - - ---- -### **Print** - -```cpp -void Print() -``` - - - ---- - - -## Protected Function Details - -### **RecurseKe** - -```cpp -virtual void RecurseKe() =0 -``` - - - -**Reimplemented by**: [lds::poisson::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-recurseke), [lds::gaussian::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-recurseke) - - ---- -### **InitVars** - -```cpp -void InitVars( - data_t p0 =kDefaultP0, - data_t q0 =kDefaultQ0 -) -``` - - - ---- - - -## Public Attribute Details - -### **do_adapt_m** - -```cpp -bool do_adapt_m {}; -``` - - - ---- - - -## Protected Attribute Details - -### **n_x_** - -```cpp -std::size_t n_x_ {}; -``` - - - ---- -### **n_u_** - -```cpp -std::size_t n_u_ {}; -``` - - - ---- -### **n_y_** - -```cpp -std::size_t n_y_ {}; -``` - - - ---- -### **dt_** - -```cpp -data_t dt_ {}; -``` - - - ---- -### **x_** - -```cpp -Vector x_; -``` - - - ---- -### **P_** - -```cpp -Matrix P_; -``` - - - ---- -### **m_** - -```cpp -Vector m_; -``` - - - ---- -### **P_m_** - -```cpp -Matrix P_m_; -``` - - - ---- -### **cx_** - -```cpp -Vector cx_; -``` - - - ---- -### **y_** - -```cpp -Vector y_; -``` - - - ---- -### **z_** - -```cpp -Vector z_; -``` - - - ---- -### **x0_** - -```cpp -Vector x0_; -``` - - - ---- -### **P0_** - -```cpp -Matrix P0_; -``` - - - ---- -### **m0_** - -```cpp -Vector m0_; -``` - - - ---- -### **P0_m_** - -```cpp -Matrix P0_m_; -``` - - - ---- -### **A_** - -```cpp -Matrix A_; -``` - - - ---- -### **B_** - -```cpp -Matrix B_; -``` - - - ---- -### **g_** - -```cpp -Vector g_; -``` - - - ---- -### **Q_** - -```cpp -Matrix Q_; -``` - - - ---- -### **Q_m_** - -```cpp -Matrix Q_m_; -``` - - - ---- -### **C_** - -```cpp -Matrix C_; -``` - - - ---- -### **d_** - -```cpp -Vector d_; -``` - - - ---- -### **Ke_** - -```cpp -Matrix Ke_; -``` - - - ---- -### **Ke_m_** - -```cpp -Matrix Ke_m_; -``` - - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformSystemList.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformSystemList.md deleted file mode 100644 index 37b04282..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformSystemList.md +++ /dev/null @@ -1,235 +0,0 @@ ---- -title: lds::UniformSystemList - ---- - -# lds::UniformSystemList - - - - [More...](#detailed-description) - -Inherits from std::vector< System > - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-uniformsystemlist)**() =default<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/). | -| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-uniformsystemlist)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > & systems, std::array< size_t, 3 > dim ={0, 0, 0})<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) by copying existing vector of [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) if dimensions consistent. | -| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-uniformsystemlist)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > && systems, std::array< size_t, 3 > dim ={0, 0, 0})<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) by moving existing vector of [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) if dimensions consistent. | -| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-uniformsystemlist)**(std::initializer_list< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > systems, std::array< size_t, 3 > dim ={0, 0, 0})<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) from initializer_list of [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) if dimensions consistent. | -| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-uniformsystemlist)**(const [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) & that)<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) (copy). | -| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-uniformsystemlist)**([UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) && that)<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) (move). | -| | **[~UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-~uniformsystemlist)**() =default<br>Destroys the object. | -| const std::array< size_t, 3 > & | **[dim](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-dim)**() const<br>gets dimensions of the uniformly sized systems | -| size_t | **[size](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-size)**()<br>size of container | -| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & | **[at](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-at)**(size_t n)<br>gets reference to n^th element | -| void | **[Swap](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-swap)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & that, size_t n)<br>swaps input system with n^th system of list | -| [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-operator=)**(const [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) & that)<br>assigns the contents (copy) | -| [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/#function-operator=)**([UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) && that)<br>assigns the contents (move) | - -## Detailed Description - -```cpp -template <typename System> -class lds::UniformSystemList; -``` - - ---- ---- -## Public Function Details - -### **UniformSystemList** - -```cpp -UniformSystemList() =default -``` - - - ---- -### **UniformSystemList** - -```cpp -explicit UniformSystemList( - const std::vector< System > & systems, - std::array< size_t, 3 > dim ={0, 0, 0} -) -``` - - - -**Parameters**: - - * **systems** input systems - * **dim** dimensions (n_u, n_x, n_y) - - ---- -### **UniformSystemList** - -```cpp -explicit UniformSystemList( - std::vector< System > && systems, - std::array< size_t, 3 > dim ={0, 0, 0} -) -``` - - - -**Parameters**: - - * **systems** input systems - * **dim** dimensions (n_u, n_x, n_y) - - ---- -### **UniformSystemList** - -```cpp -UniformSystemList( - std::initializer_list< System > systems, - std::array< size_t, 3 > dim ={0, 0, 0} -) -``` - - - -**Parameters**: - - * **systems** input systems - * **dim** dimensions (n_u, n_x, n_y) - - ---- -### **UniformSystemList** - -```cpp -UniformSystemList( - const UniformSystemList & that -) -``` - - - -**Parameters**: - - * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) - - ---- -### **UniformSystemList** - -```cpp -UniformSystemList( - UniformSystemList && that -) -``` - - - -**Parameters**: - - * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) - - ---- -### **~UniformSystemList** - -```cpp -~UniformSystemList() =default -``` - - - ---- -### **dim** - -```cpp -inline const std::array< size_t, 3 > & dim() const -``` - - - ---- -### **size** - -```cpp -inline size_t size() -``` - - - ---- -### **at** - -```cpp -inline const System & at( - size_t n -) -``` - - - ---- -### **Swap** - -```cpp -inline void Swap( - System & that, - size_t n -) -``` - - - -**Parameters**: - - * **that** input system - * **n** index where the system is moved - - ---- -### **operator=** - -```cpp -inline UniformSystemList & operator=( - const UniformSystemList & that -) -``` - - - -**Parameters**: - - * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) - - -**Return**: reference to object - ---- -### **operator=** - -```cpp -inline UniformSystemList & operator=( - UniformSystemList && that -) -``` - - - -**Parameters**: - - * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/) - - -**Return**: reference to object - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformVectorList.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformVectorList.md deleted file mode 100644 index a483504b..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformVectorList.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -title: lds::UniformVectorList - ---- - -# lds::UniformVectorList - - - -Inherits from std::vector< Vector > - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-uniformvectorlist)**() =default<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/). | -| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-uniformvectorlist)**(const std::vector< Vector > & vecs, size_t dim =0)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) by copying existing vector of Vector if dimensions consistent. | -| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-uniformvectorlist)**(std::vector< Vector > && vecs, size_t dim =0)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) by moving existing vector of Vector if dimensions consistent. | -| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-uniformvectorlist)**(std::initializer_list< Vector > vecs, size_t dim =0)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) from initializer_list of Vector if dimensions consistent. | -| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-uniformvectorlist)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) & that)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) (copy) | -| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-uniformvectorlist)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) && that)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) (move) | -| | **[~UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-~uniformvectorlist)**() =default<br>Destroys the object. | -| size_t | **[dim](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-dim)**() const<br>gets dimensions of the uniformly sized matrices | -| size_t | **[size](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-size)**()<br>size of container | -| const Vector & | **[at](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-at)**(size_t n)<br>gets reference to n^th element | -| void | **[Swap](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-swap)**(Vector & that, size_t n)<br>swaps input matrix with n^th vector of list | -| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-operator=)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) & that)<br>assigns the contents (copy) | -| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/#function-operator=)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) && that)<br>assigns the contents (move) | - ---- ---- -## Public Function Details - -### **UniformVectorList** - -```cpp -UniformVectorList() =default -``` - - - ---- -### **UniformVectorList** - -```cpp -explicit UniformVectorList( - const std::vector< Vector > & vecs, - size_t dim =0 -) -``` - - - -**Parameters**: - - * **vecs** input vectors - * **dims** dimension - - ---- -### **UniformVectorList** - -```cpp -explicit UniformVectorList( - std::vector< Vector > && vecs, - size_t dim =0 -) -``` - - - -**Parameters**: - - * **vecs** input vectors - * **dim** dimension - - ---- -### **UniformVectorList** - -```cpp -UniformVectorList( - std::initializer_list< Vector > vecs, - size_t dim =0 -) -``` - - - -**Parameters**: - - * **vecs** input vectors - * **dim** dimension - - ---- -### **UniformVectorList** - -```cpp -UniformVectorList( - const UniformVectorList & that -) -``` - - - -**Parameters**: - - * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) - - ---- -### **UniformVectorList** - -```cpp -UniformVectorList( - UniformVectorList && that -) -``` - - - -**Parameters**: - - * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) - - ---- -### **~UniformVectorList** - -```cpp -~UniformVectorList() =default -``` - - - ---- -### **dim** - -```cpp -inline size_t dim() const -``` - - - ---- -### **size** - -```cpp -inline size_t size() -``` - - - ---- -### **at** - -```cpp -inline const Vector & at( - size_t n -) -``` - - - ---- -### **Swap** - -```cpp -inline void Swap( - Vector & that, - size_t n -) -``` - - - -**Parameters**: - - * **that** input vector - * **n** index where the vector is moved - - ---- -### **operator=** - -```cpp -inline UniformVectorList & operator=( - const UniformVectorList & that -) -``` - - - -**Parameters**: - - * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) - - -**Return**: reference to object - ---- -### **operator=** - -```cpp -inline UniformVectorList & operator=( - UniformVectorList && that -) -``` - - - -**Parameters**: - - * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) - - -**Return**: reference to object - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1Controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_controller.md similarity index 54% rename from misc/docs-hugo/content/docs/api/Classes/classlds_1_1Controller.md rename to misc/docs-hugo/content/docs/api/Classes/classlds_1_1_controller.md index 9b78fe9d..5b8c85f5 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1Controller.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_controller.md @@ -1,774 +1,774 @@ ---- -title: lds::Controller - ---- - -# lds::Controller - - - - [More...](#detailed-description) - -Inherited by [lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/), [lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/), [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) by moving the system object. | -| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | -| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | -| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-sys)**() const | -| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc)**() const<br>Get state feedback controller gain. | -| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-inty)**() const<br>Get integral controller gain. | -| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-u)**() const<br>Get input feedback controller gain. | -| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-g-design)**() const<br>Get input gain used in controller design. | -| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ref)**() const<br>Get reference input. | -| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-x-ref)**() const<br>Get reference state. | -| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-y-ref)**() const<br>Get reference output. | -| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control-type)**() const<br>Get controller type. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-lb)**() const<br>Get control lower bound. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ub)**() const<br>Get control upper bound. | -| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys)<br>Set system. | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | -| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | -| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | -| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-y-ref)**(const Vector & y_ref)<br>Set reference output (y_ref) | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | -| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | -| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | -| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | -| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-reset)**()<br>reset system and control variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-print)**()<br>prints variables to stdout | - -## Protected Attributes - -| | Name | -| -------------- | -------------- | -| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-sys-)** <br>underlying LDS | -| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-)** <br>control signal | -| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-return-)** <br>control signal that is _returned_ to user | -| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-g-design-)** <br>input gain of the system used for controller design | -| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-)** <br>reference input | -| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-prev-)** <br>reference input at previous time step | -| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-x-ref-)** <br>reference state | -| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-y-ref-)** <br>reference output | -| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-cx-ref-)** | -| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-)** <br>state controller gain | -| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-u-)** <br>input controller gain (optional when control updates ) | -| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-inty-)** <br>integral controller gain | -| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-du-ref-)** | -| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-ref-)** | -| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-ref-)** | -| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-)** | -| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | -| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-)** <br>integrated error | -| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | -| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | -| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-control-prev-)** | -| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-lock-control-prev-)** | -| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-lb-)** <br>lower bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ub-)** <br>upper bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-tau-awu-)** <br>antiwindup time constant | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-k-awu-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | -| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-control-type-)** <br>controller type | - -## Detailed Description - -```cpp -template <typename System > -class lds::Controller; -``` - - ---- ---- -## Public Function Details - -### **Controller** - -```cpp -Controller() =default -``` - - - ---- -### **Controller** - -```cpp -inline Controller( - const System & sys, - data_t u_lb, - data_t u_ub, - size_t control_type =0 -) -``` - - - -**Parameters**: - - * **sys** [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) (derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)) - * **u_lb** lower bound on control (u) - * **u_ub** upper bound on control (u) - * **control_type** [optional] control type bit mask - - -**Template Parameters**: - - * **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** type derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) - - ---- -### **Controller** - -```cpp -inline Controller( - System && sys, - data_t u_lb, - data_t u_ub, - size_t control_type =0 -) -``` - - - -**Parameters**: - - * **sys** [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) (derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)) - * **u_lb** lower bound on control (u) - * **u_ub** upper bound on control (u) - * **control_type** [optional] control type bit mask - - -**Template Parameters**: - - * **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** type derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) - - ---- -### **Control** - -```cpp -inline const Vector & Control( - const Vector & z, - bool do_control =true, - bool do_lock_control =false, - data_t sigma_soft_start =0, - data_t sigma_u_noise =0, - bool do_reset_at_control_onset =true -) -``` - - - -**Parameters**: - - * **z** measurement - * **do_control** [optional] whether to update control (true) or simply feed through u_ref (false) - * **do_lock_control** [optional] whether to lock control at its current value - * **sigma_soft_start** [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) - * **sigma_u_noise** [optional] standard deviation (sigma) of Gaussian noise added on top of control signal - * **do_reset_at_control_onset** [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) - - -**Return**: updated control signal - -Updates the control signal (single-step). This is the most flexible option, but requires user to have set the controller's y_ref, x_ref, and u_ref variables. - - ---- -### **ControlOutputReference** - -```cpp -inline const Vector & ControlOutputReference( - const Vector & z, - bool do_control =true, - bool do_estimation =true, - bool do_lock_control =false, - data_t sigma_soft_start =0, - data_t sigma_u_noise =0, - bool do_reset_at_control_onset =true -) -``` - - - -**Parameters**: - - * **z** measurement - * **do_control** [optional] whether to update control (true) or simply feed through u_ref (false) - * **do_estimation** [optional] whether to update state estimate (if false, effectively open-loop control) - * **do_lock_control** [optional] whether to lock control at its current value - * **sigma_soft_start** [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) - * **sigma_u_noise** [optional] standard deviation (sigma) of Gaussian noise added on top of control signal - * **do_reset_at_control_onset** [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) - - -**Return**: updated control signal - -Updates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs. - - ---- -### **sys** - -```cpp -inline const System & sys() const -``` - - - ---- -### **Kc** - -```cpp -inline const Matrix & Kc() const -``` - - - ---- -### **Kc_inty** - -```cpp -inline const Matrix & Kc_inty() const -``` - - - ---- -### **Kc_u** - -```cpp -inline const Matrix & Kc_u() const -``` - - - ---- -### **g_design** - -```cpp -inline const Vector & g_design() const -``` - - - ---- -### **u_ref** - -```cpp -inline const Vector & u_ref() const -``` - - - ---- -### **x_ref** - -```cpp -inline const Vector & x_ref() const -``` - - - ---- -### **y_ref** - -```cpp -inline const Vector & y_ref() const -``` - - - ---- -### **control_type** - -```cpp -inline size_t control_type() const -``` - - - ---- -### **tau_awu** - -```cpp -inline data_t tau_awu() const -``` - - - ---- -### **u_lb** - -```cpp -inline data_t u_lb() const -``` - - - ---- -### **u_ub** - -```cpp -inline data_t u_ub() const -``` - - - ---- -### **set_sys** - -```cpp -inline void set_sys( - const System & sys -) -``` - - - ---- -### **set_g_design** - -```cpp -inline void set_g_design( - const Vector & g_design -) -``` - - - ---- -### **set_u_ref** - -```cpp -inline void set_u_ref( - const Vector & u_ref -) -``` - - - ---- -### **set_x_ref** - -```cpp -inline void set_x_ref( - const Vector & x_ref -) -``` - - - ---- -### **set_y_ref** - -```cpp -inline virtual void set_y_ref( - const Vector & y_ref -) -``` - - - -**Reimplemented by**: [lds::poisson::SwitchedController::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/#function-set-y-ref), [lds::gaussian::SwitchedController::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/#function-set-y-ref), [lds::poisson::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/#function-set-y-ref), [lds::gaussian::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/#function-set-y-ref) - - ---- -### **set_Kc** - -```cpp -inline void set_Kc( - const Matrix & Kc -) -``` - - - ---- -### **set_Kc_inty** - -```cpp -inline void set_Kc_inty( - const Matrix & Kc_inty -) -``` - - - ---- -### **set_Kc_u** - -```cpp -inline void set_Kc_u( - const Matrix & Kc_u -) -``` - - - ---- -### **set_tau_awu** - -```cpp -inline void set_tau_awu( - data_t tau -) -``` - - - ---- -### **set_control_type** - -```cpp -inline void set_control_type( - size_t control_type -) -``` - - - -**Parameters**: - - * **control_type** control type bit mask - - -**Template Parameters**: - - * **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** type derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) - - ---- -### **set_u_lb** - -```cpp -inline void set_u_lb( - data_t u_lb -) -``` - - - -**Parameters**: - - * **u_lb** control lower bound - - ---- -### **set_u_ub** - -```cpp -inline void set_u_ub( - data_t u_ub -) -``` - - - -**Parameters**: - - * **u_ub** control upper bound - - ---- -### **Reset** - -```cpp -inline void Reset() -``` - - - ---- -### **Print** - -```cpp -inline void Print() -``` - - - ---- - - -## Protected Attribute Details - -### **sys_** - -```cpp -System sys_; -``` - - - ---- -### **u_** - -```cpp -Vector u_; -``` - - - ---- -### **u_return_** - -```cpp -Vector u_return_; -``` - - - ---- -### **g_design_** - -```cpp -Vector g_design_; -``` - - - ---- -### **u_ref_** - -```cpp -Vector u_ref_; -``` - - - ---- -### **u_ref_prev_** - -```cpp -Vector u_ref_prev_; -``` - - - ---- -### **x_ref_** - -```cpp -Vector x_ref_; -``` - - - ---- -### **y_ref_** - -```cpp -Vector y_ref_; -``` - - - ---- -### **cx_ref_** - -```cpp -Vector cx_ref_; -``` - - - ---- -### **Kc_** - -```cpp -Matrix Kc_; -``` - - - ---- -### **Kc_u_** - -```cpp -Matrix Kc_u_; -``` - - - ---- -### **Kc_inty_** - -```cpp -Matrix Kc_inty_; -``` - - - ---- -### **du_ref_** - -```cpp -Vector du_ref_; -``` - - - ---- -### **dv_ref_** - -```cpp -Vector dv_ref_; -``` - - - ---- -### **v_ref_** - -```cpp -Vector v_ref_; -``` - - - ---- -### **dv_** - -```cpp -Vector dv_; -``` - - - ---- -### **v_** - -```cpp -Vector v_; -``` - - - ---- -### **int_e_** - -```cpp -Vector int_e_; -``` - - - ---- -### **int_e_awu_adjust_** - -```cpp -Vector int_e_awu_adjust_; -``` - - - ---- -### **u_sat_** - -```cpp -Vector u_sat_; -``` - - - ---- -### **do_control_prev_** - -```cpp -bool do_control_prev_ = false; -``` - - - ---- -### **do_lock_control_prev_** - -```cpp -bool do_lock_control_prev_ = false; -``` - - - ---- -### **u_saturated_** - -```cpp -bool u_saturated_ = - false; -``` - - - ---- -### **u_lb_** - -```cpp -data_t u_lb_ {}; -``` - - - ---- -### **u_ub_** - -```cpp -data_t u_ub_ {}; -``` - - - ---- -### **tau_awu_** - -```cpp -data_t tau_awu_ {}; -``` - - - ---- -### **k_awu_** - -```cpp -data_t k_awu_ = 0; -``` - - - ---- -### **t_since_control_onset_** - -```cpp -data_t t_since_control_onset_ = 0; -``` - - - ---- -### **control_type_** - -```cpp -size_t control_type_ {}; -``` - - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time +--- +title: lds::Controller + +--- + +# lds::Controller + + + + [More...](#detailed-description) + +Inherited by [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/), [lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/), [lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) by moving the system object. | +| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | +| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | +| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys)**() const | +| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc)**() const<br>Get state feedback controller gain. | +| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty)**() const<br>Get integral controller gain. | +| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u)**() const<br>Get input feedback controller gain. | +| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design)**() const<br>Get input gain used in controller design. | +| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref)**() const<br>Get reference input. | +| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref)**() const<br>Get reference state. | +| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref)**() const<br>Get reference output. | +| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type)**() const<br>Get controller type. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb)**() const<br>Get control lower bound. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub)**() const<br>Get control upper bound. | +| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys)<br>Set system. | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | +| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | +| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | +| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref)**(const Vector & y_ref)<br>Set reference output (y_ref) | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | +| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | +| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | +| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | +| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset)**()<br>reset system and control variables. | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print)**()<br>prints variables to stdout | + +## Protected Attributes + +| | Name | +| -------------- | -------------- | +| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-)** <br>underlying LDS | +| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-)** <br>control signal | +| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-)** <br>control signal that is _returned_ to user | +| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-)** <br>input gain of the system used for controller design | +| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-)** <br>reference input | +| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-)** <br>reference input at previous time step | +| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-)** <br>reference state | +| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-)** <br>reference output | +| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-)** | +| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-)** <br>state controller gain | +| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-)** <br>input controller gain (optional when control updates \deltaU) | +| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-)** <br>integral controller gain | +| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-)** | +| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-)** | +| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-)** | +| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-)** | +| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | +| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-)** <br>integrated error | +| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | +| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | +| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-)** | +| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-)** | +| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-)** <br>lower bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-)** <br>upper bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-)** <br>antiwindup time constant | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | +| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-)** <br>controller type | + +## Detailed Description + +```cpp +template <typename System > +class lds::Controller; +``` + + +--- +--- +## Public Function Details + +### **Controller** + +```cpp +Controller() =default +``` + + + +--- +### **Controller** + +```cpp +inline Controller( + const System & sys, + data_t u_lb, + data_t u_ub, + size_t control_type =0 +) +``` + + + +**Parameters**: + + * **sys** [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) (derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)) + * **u_lb** lower bound on control (u) + * **u_ub** upper bound on control (u) + * **control_type** [optional] control type bit mask + + +**Template Parameters**: + + * **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** type derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) + + +--- +### **Controller** + +```cpp +inline Controller( + System && sys, + data_t u_lb, + data_t u_ub, + size_t control_type =0 +) +``` + + + +**Parameters**: + + * **sys** [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) (derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)) + * **u_lb** lower bound on control (u) + * **u_ub** upper bound on control (u) + * **control_type** [optional] control type bit mask + + +**Template Parameters**: + + * **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** type derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) + + +--- +### **Control** + +```cpp +inline const Vector & Control( + const Vector & z, + bool do_control =true, + bool do_lock_control =false, + data_t sigma_soft_start =0, + data_t sigma_u_noise =0, + bool do_reset_at_control_onset =true +) +``` + + + +**Parameters**: + + * **z** measurement + * **do_control** [optional] whether to update control (true) or simply feed through u_ref (false) + * **do_lock_control** [optional] whether to lock control at its current value + * **sigma_soft_start** [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) + * **sigma_u_noise** [optional] standard deviation (sigma) of Gaussian noise added on top of control signal + * **do_reset_at_control_onset** [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) + + +**Return**: updated control signal + +Updates the control signal (single-step). This is the most flexible option, but requires user to have set the controller's y_ref, x_ref, and u_ref variables. + + +--- +### **ControlOutputReference** + +```cpp +inline const Vector & ControlOutputReference( + const Vector & z, + bool do_control =true, + bool do_estimation =true, + bool do_lock_control =false, + data_t sigma_soft_start =0, + data_t sigma_u_noise =0, + bool do_reset_at_control_onset =true +) +``` + + + +**Parameters**: + + * **z** measurement + * **do_control** [optional] whether to update control (true) or simply feed through u_ref (false) + * **do_estimation** [optional] whether to update state estimate (if false, effectively open-loop control) + * **do_lock_control** [optional] whether to lock control at its current value + * **sigma_soft_start** [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) + * **sigma_u_noise** [optional] standard deviation (sigma) of Gaussian noise added on top of control signal + * **do_reset_at_control_onset** [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) + + +**Return**: updated control signal + +Updates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs. + + +--- +### **sys** + +```cpp +inline const System & sys() const +``` + + + +--- +### **Kc** + +```cpp +inline const Matrix & Kc() const +``` + + + +--- +### **Kc_inty** + +```cpp +inline const Matrix & Kc_inty() const +``` + + + +--- +### **Kc_u** + +```cpp +inline const Matrix & Kc_u() const +``` + + + +--- +### **g_design** + +```cpp +inline const Vector & g_design() const +``` + + + +--- +### **u_ref** + +```cpp +inline const Vector & u_ref() const +``` + + + +--- +### **x_ref** + +```cpp +inline const Vector & x_ref() const +``` + + + +--- +### **y_ref** + +```cpp +inline const Vector & y_ref() const +``` + + + +--- +### **control_type** + +```cpp +inline size_t control_type() const +``` + + + +--- +### **tau_awu** + +```cpp +inline data_t tau_awu() const +``` + + + +--- +### **u_lb** + +```cpp +inline data_t u_lb() const +``` + + + +--- +### **u_ub** + +```cpp +inline data_t u_ub() const +``` + + + +--- +### **set_sys** + +```cpp +inline void set_sys( + const System & sys +) +``` + + + +--- +### **set_g_design** + +```cpp +inline void set_g_design( + const Vector & g_design +) +``` + + + +--- +### **set_u_ref** + +```cpp +inline void set_u_ref( + const Vector & u_ref +) +``` + + + +--- +### **set_x_ref** + +```cpp +inline void set_x_ref( + const Vector & x_ref +) +``` + + + +--- +### **set_y_ref** + +```cpp +inline virtual void set_y_ref( + const Vector & y_ref +) +``` + + + +**Reimplemented by**: [lds::gaussian::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/#function-set-y-ref), [lds::gaussian::SwitchedController::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/#function-set-y-ref), [lds::poisson::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/#function-set-y-ref), [lds::poisson::SwitchedController::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/#function-set-y-ref) + + +--- +### **set_Kc** + +```cpp +inline void set_Kc( + const Matrix & Kc +) +``` + + + +--- +### **set_Kc_inty** + +```cpp +inline void set_Kc_inty( + const Matrix & Kc_inty +) +``` + + + +--- +### **set_Kc_u** + +```cpp +inline void set_Kc_u( + const Matrix & Kc_u +) +``` + + + +--- +### **set_tau_awu** + +```cpp +inline void set_tau_awu( + data_t tau +) +``` + + + +--- +### **set_control_type** + +```cpp +inline void set_control_type( + size_t control_type +) +``` + + + +**Parameters**: + + * **control_type** control type bit mask + + +**Template Parameters**: + + * **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** type derived from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) + + +--- +### **set_u_lb** + +```cpp +inline void set_u_lb( + data_t u_lb +) +``` + + + +**Parameters**: + + * **u_lb** control lower bound + + +--- +### **set_u_ub** + +```cpp +inline void set_u_ub( + data_t u_ub +) +``` + + + +**Parameters**: + + * **u_ub** control upper bound + + +--- +### **Reset** + +```cpp +inline void Reset() +``` + + + +--- +### **Print** + +```cpp +inline void Print() +``` + + + +--- + + +## Protected Attribute Details + +### **sys_** + +```cpp +System sys_; +``` + + + +--- +### **u_** + +```cpp +Vector u_; +``` + + + +--- +### **u_return_** + +```cpp +Vector u_return_; +``` + + + +--- +### **g_design_** + +```cpp +Vector g_design_; +``` + + + +--- +### **u_ref_** + +```cpp +Vector u_ref_; +``` + + + +--- +### **u_ref_prev_** + +```cpp +Vector u_ref_prev_; +``` + + + +--- +### **x_ref_** + +```cpp +Vector x_ref_; +``` + + + +--- +### **y_ref_** + +```cpp +Vector y_ref_; +``` + + + +--- +### **cx_ref_** + +```cpp +Vector cx_ref_; +``` + + + +--- +### **Kc_** + +```cpp +Matrix Kc_; +``` + + + +--- +### **Kc_u_** + +```cpp +Matrix Kc_u_; +``` + + + +--- +### **Kc_inty_** + +```cpp +Matrix Kc_inty_; +``` + + + +--- +### **du_ref_** + +```cpp +Vector du_ref_; +``` + + + +--- +### **dv_ref_** + +```cpp +Vector dv_ref_; +``` + + + +--- +### **v_ref_** + +```cpp +Vector v_ref_; +``` + + + +--- +### **dv_** + +```cpp +Vector dv_; +``` + + + +--- +### **v_** + +```cpp +Vector v_; +``` + + + +--- +### **int_e_** + +```cpp +Vector int_e_; +``` + + + +--- +### **int_e_awu_adjust_** + +```cpp +Vector int_e_awu_adjust_; +``` + + + +--- +### **u_sat_** + +```cpp +Vector u_sat_; +``` + + + +--- +### **do_control_prev_** + +```cpp +bool do_control_prev_ = false; +``` + + + +--- +### **do_lock_control_prev_** + +```cpp +bool do_lock_control_prev_ = false; +``` + + + +--- +### **u_saturated_** + +```cpp +bool u_saturated_ = + false; +``` + + + +--- +### **u_lb_** + +```cpp +data_t u_lb_ {}; +``` + + + +--- +### **u_ub_** + +```cpp +data_t u_ub_ {}; +``` + + + +--- +### **tau_awu_** + +```cpp +data_t tau_awu_ {}; +``` + + + +--- +### **k_awu_** + +```cpp +data_t k_awu_ = 0; +``` + + + +--- +### **t_since_control_onset_** + +```cpp +data_t t_since_control_onset_ = 0; +``` + + + +--- +### **control_type_** + +```cpp +size_t control_type_ {}; +``` + + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1EM.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_e_m.md similarity index 52% rename from misc/docs-hugo/content/docs/api/Classes/classlds_1_1EM.md rename to misc/docs-hugo/content/docs/api/Classes/classlds_1_1_e_m.md index c48ea2a0..9e4b61d6 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1EM.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_e_m.md @@ -1,593 +1,593 @@ ---- -title: lds::EM - ---- - -# lds::EM - - - - [More...](#detailed-description) - -Inherited by [lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/), [lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**() =default<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**(size_t n_x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**(const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) & fit0, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| virtual | **[~EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-~em)**() =default | -| const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) & | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-run)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M) | -| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-returndata)**()<br>Returns the input/output data to caller. | -| const std::vector< Matrix > & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-x)**() const<br>gets estimated state (over time) | -| const std::vector< Matrix > & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-y)**() const<br>gets estimated output (over time) | -| const Matrix & | **[sum_E_x_t_x_t](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-x-t-x-t)**() const<br>gets state-input covariance | -| const Matrix & | **[sum_E_xu_tm1_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-xu-tm1-xu-tm1)**() const<br>gets state-input covariance (t-minus-1) | -| const Matrix & | **[sum_E_xu_t_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-xu-t-xu-tm1)**() const<br>gets single lag state-input covariance | -| size_t | **[n_t_tot](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-n-t-tot)**()<br>total number of time samples | -| const Vector & | **[theta](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-theta)**() const<br>gets parameters updated in M step | - -## Protected Functions - -| | Name | -| -------------- | -------------- | -| void | **[Expectation](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-expectation)**(bool force_common_initial =false)<br>Expectation step. | -| void | **[Maximization](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximization)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step. | -| void | **[MaximizeDynamics](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizedynamics)**() | -| void | **[MaximizeQ](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizeq)**() | -| void | **[MaximizeInitial](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizeinitial)**() | -| virtual void | **[MaximizeOutput](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizeoutput)**() =0 | -| virtual void | **[MaximizeMeasurement](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizemeasurement)**() =0 | -| void | **[Smooth](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-smooth)**(bool force_common_initial)<br>get smoothed estimates | -| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-recurseke)**(Matrix & Ke, Cube & P_pre, Cube & P_post, size_t t) =0<br>recursively update estimator gain Ke | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-reset)**()<br>reset to initial conditions | -| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-initvars)**()<br>Initializes the variables. | -| Vector | **[UpdateTheta](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-updatetheta)**()<br>updates parameter list, theta | - -## Protected Attributes - -| | Name | -| -------------- | -------------- | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-u-)** <br>input training data | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-z-)** <br>measurement training data | -| std::vector< Matrix > | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-x-)** <br>state estimate | -| std::vector< Cube > | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-p-)** <br>state estimate cov | -| std::vector< Cube > | **[P_t_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-p-t-tm1-)** <br>single-lag state covariance | -| std::vector< Matrix > | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-y-)** <br>output estimate | -| Matrix | **[diag_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-diag-y-)** | -| Matrix | **[sum_E_x_t_x_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-x-t-x-t-)** <br>state covariance (current time) | -| Matrix | **[sum_E_xu_tm1_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-xu-tm1-xu-tm1-)** <br>state-input covariance (t-minus-1) | -| Matrix | **[sum_E_xu_t_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-xu-t-xu-tm1-)** <br>single lag state-input covariance | -| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-fit-)** | -| Vector | **[theta_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-theta-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-dt-)** <br>sample period | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-y-)** <br>number of outputs | -| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-trials-)** <br>number of input/output data sequences | -| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-t-)** <br>number of time steps | -| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-t-tot-)** <br>total number of time steps across trials | - -## Detailed Description - -```cpp -template <typename Fit > -class lds::EM; -``` - - ---- ---- -## Public Function Details - -### **EM** - -```cpp -EM() =default -``` - - - ---- -### **EM** - -```cpp -EM( - size_t n_x, - data_t dt, - UniformMatrixList< kMatFreeDim2 > && u_train, - UniformMatrixList< kMatFreeDim2 > && z_train -) -``` - - - -**Parameters**: - - * **n_x** number of states - * **dt** sample period - * **u_train** input training data - * **z_train** measurement training data - - ---- -### **EM** - -```cpp -EM( - const Fit & fit0, - UniformMatrixList< kMatFreeDim2 > && u_train, - UniformMatrixList< kMatFreeDim2 > && z_train -) -``` - - - -**Parameters**: - - * **fit0** initial fit - * **u_train** input training data - * **z_train** measurement training data - - ---- -### **~EM** - -```cpp -virtual ~EM() =default -``` - - - ---- -### **Run** - -```cpp -const Fit & Run( - bool calc_dynamics =true, - bool calc_Q =true, - bool calc_init =true, - bool calc_output =true, - bool calc_measurement =true, - size_t max_iter =100, - data_t tol =1e-2 -) -``` - - - -**Parameters**: - - * **calc_dynamics** [optional] whether to calculate dynamics (A, B) - * **calc_Q** [optional] whether to calculate process noise covariance - * **calc_init** [optional] whether to calculate initial conditions - * **calc_output** [optional] whether to calculate output function - * **calc_measurement** [optional] whether to calculate parameters for measurement/observation law - * **max_iter** max number of iterations - * **tol** convergence tolerance (max fractional abs change) - - -**Return**: [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) - ---- -### **ReturnData** - -```cpp -inline std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > > ReturnData() -``` - - - -**Return**: tuple(input data, output data) - ---- -### **x** - -```cpp -inline const std::vector< Matrix > & x() const -``` - - - ---- -### **y** - -```cpp -inline const std::vector< Matrix > & y() const -``` - - - ---- -### **sum_E_x_t_x_t** - -```cpp -inline const Matrix & sum_E_x_t_x_t() const -``` - - - ---- -### **sum_E_xu_tm1_xu_tm1** - -```cpp -inline const Matrix & sum_E_xu_tm1_xu_tm1() const -``` - - - ---- -### **sum_E_xu_t_xu_tm1** - -```cpp -inline const Matrix & sum_E_xu_t_xu_tm1() const -``` - - - ---- -### **n_t_tot** - -```cpp -inline size_t n_t_tot() -``` - - - ---- -### **theta** - -```cpp -inline const Vector & theta() const -``` - - - ---- - - -## Protected Function Details - -### **Expectation** - -```cpp -void Expectation( - bool force_common_initial =false -) -``` - - - -**Parameters**: - - * **force_common_initial** whether to force common initial condition for all trials - - ---- -### **Maximization** - -```cpp -void Maximization( - bool calc_dynamics =true, - bool calc_Q =true, - bool calc_init =false, - bool calc_output =false, - bool calc_measurement =false -) -``` - - - -**Parameters**: - - * **calc_dynamics** [optional] whether to caclulate dynamics (A, B) - * **calc_Q** [optional] whether to calculate process noise covariance - * **calc_init** [optional] whether to calculate initial conditions - * **calc_output** [optional] whether to calculate output function - * **calc_measurement** [optional] whether to calculate parameters for measurement/observation law - - ---- -### **MaximizeDynamics** - -```cpp -void MaximizeDynamics() -``` - - - ---- -### **MaximizeQ** - -```cpp -void MaximizeQ() -``` - - - ---- -### **MaximizeInitial** - -```cpp -void MaximizeInitial() -``` - - - ---- -### **MaximizeOutput** - -```cpp -virtual void MaximizeOutput() =0 -``` - - - -**Reimplemented by**: [lds::gaussian::FitEM::MaximizeOutput](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/#function-maximizeoutput), [lds::poisson::FitEM::MaximizeOutput](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/#function-maximizeoutput) - - ---- -### **MaximizeMeasurement** - -```cpp -virtual void MaximizeMeasurement() =0 -``` - - - -**Reimplemented by**: [lds::gaussian::FitEM::MaximizeMeasurement](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/#function-maximizemeasurement), [lds::poisson::FitEM::MaximizeMeasurement](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/#function-maximizemeasurement) - - ---- -### **Smooth** - -```cpp -void Smooth( - bool force_common_initial -) -``` - - - -**Parameters**: - - * **force_common_initial** whether to force common initial conditions - - ---- -### **RecurseKe** - -```cpp -virtual void RecurseKe( - Matrix & Ke, - Cube & P_pre, - Cube & P_post, - size_t t -) =0 -``` - - - -**Parameters**: - - * **Ke** estimator gain - * **P_pre** cov of predicted state est. - * **P_post** cov of postior sate est. - * **t** time - - -**Reimplemented by**: [lds::gaussian::FitEM::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/#function-recurseke), [lds::poisson::FitEM::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/#function-recurseke) - - ---- -### **Reset** - -```cpp -void Reset() -``` - - - ---- -### **InitVars** - -```cpp -void InitVars() -``` - - - ---- -### **UpdateTheta** - -```cpp -Vector UpdateTheta() -``` - - - -**Return**: parameter list - ---- - - -## Protected Attribute Details - -### **u_** - -```cpp -UniformMatrixList< kMatFreeDim2 > u_; -``` - - - ---- -### **z_** - -```cpp -UniformMatrixList< kMatFreeDim2 > z_; -``` - - - ---- -### **x_** - -```cpp -std::vector< Matrix > x_; -``` - - - ---- -### **P_** - -```cpp -std::vector< Cube > P_; -``` - - - ---- -### **P_t_tm1_** - -```cpp -std::vector< Cube > P_t_tm1_; -``` - - - ---- -### **y_** - -```cpp -std::vector< Matrix > y_; -``` - - - ---- -### **diag_y_** - -```cpp -Matrix diag_y_; -``` - - - ---- -### **sum_E_x_t_x_t_** - -```cpp -Matrix sum_E_x_t_x_t_; -``` - - - ---- -### **sum_E_xu_tm1_xu_tm1_** - -```cpp -Matrix sum_E_xu_tm1_xu_tm1_; -``` - - - ---- -### **sum_E_xu_t_xu_tm1_** - -```cpp -Matrix sum_E_xu_t_xu_tm1_; -``` - - - ---- -### **fit_** - -```cpp -Fit fit_; -``` - - - ---- -### **theta_** - -```cpp -Vector theta_; -``` - - - ---- -### **dt_** - -```cpp -data_t dt_ {}; -``` - - - ---- -### **n_u_** - -```cpp -size_t n_u_ {}; -``` - - - ---- -### **n_x_** - -```cpp -size_t n_x_ {}; -``` - - - ---- -### **n_y_** - -```cpp -size_t n_y_ {}; -``` - - - ---- -### **n_trials_** - -```cpp -size_t n_trials_ {}; -``` - - - ---- -### **n_t_** - -```cpp -std::vector< size_t > n_t_; -``` - - - ---- -### **n_t_tot_** - -```cpp -size_t n_t_tot_ {}; -``` - - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time +--- +title: lds::EM + +--- + +# lds::EM + + + + [More...](#detailed-description) + +Inherited by [lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/), [lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**() =default<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**(size_t n_x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**(const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) & fit0, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| virtual | **[~EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em)**() =default | +| const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) & | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M) | +| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata)**()<br>Returns the input/output data to caller. | +| const std::vector< Matrix > & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x)**() const<br>gets estimated state (over time) | +| const std::vector< Matrix > & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y)**() const<br>gets estimated output (over time) | +| const Matrix & | **[sum_E_x_t_x_t](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t)**() const<br>gets state-input covariance | +| const Matrix & | **[sum_E_xu_tm1_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1)**() const<br>gets state-input covariance (t-minus-1) | +| const Matrix & | **[sum_E_xu_t_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1)**() const<br>gets single lag state-input covariance | +| size_t | **[n_t_tot](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot)**()<br>total number of time samples | +| const Vector & | **[theta](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta)**() const<br>gets parameters updated in M step | + +## Protected Functions + +| | Name | +| -------------- | -------------- | +| void | **[Expectation](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation)**(bool force_common_initial =false)<br>Expectation step. | +| void | **[Maximization](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step. | +| void | **[MaximizeDynamics](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics)**() | +| void | **[MaximizeQ](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq)**() | +| void | **[MaximizeInitial](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial)**() | +| virtual void | **[MaximizeOutput](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeoutput)**() =0 | +| virtual void | **[MaximizeMeasurement](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizemeasurement)**() =0 | +| void | **[Smooth](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth)**(bool force_common_initial)<br>get smoothed estimates | +| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-recurseke)**(Matrix & Ke, Cube & P_pre, Cube & P_post, size_t t) =0<br>recursively update estimator gain Ke | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset)**()<br>reset to initial conditions | +| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars)**()<br>Initializes the variables. | +| Vector | **[UpdateTheta](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta)**()<br>updates parameter list, theta | + +## Protected Attributes + +| | Name | +| -------------- | -------------- | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-)** <br>input training data | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-)** <br>measurement training data | +| std::vector< Matrix > | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-)** <br>state estimate | +| std::vector< Cube > | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-)** <br>state estimate cov | +| std::vector< Cube > | **[P_t_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-)** <br>single-lag state covariance | +| std::vector< Matrix > | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-)** <br>output estimate | +| Matrix | **[diag_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-)** | +| Matrix | **[sum_E_x_t_x_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-)** <br>state covariance (current time) | +| Matrix | **[sum_E_xu_tm1_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-)** <br>state-input covariance (t-minus-1) | +| Matrix | **[sum_E_xu_t_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-)** <br>single lag state-input covariance | +| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-)** | +| Vector | **[theta_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-)** <br>sample period | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-)** <br>number of outputs | +| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-)** <br>number of input/output data sequences | +| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-)** <br>number of time steps | +| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-)** <br>total number of time steps across trials | + +## Detailed Description + +```cpp +template <typename Fit > +class lds::EM; +``` + + +--- +--- +## Public Function Details + +### **EM** + +```cpp +EM() =default +``` + + + +--- +### **EM** + +```cpp +EM( + size_t n_x, + data_t dt, + UniformMatrixList< kMatFreeDim2 > && u_train, + UniformMatrixList< kMatFreeDim2 > && z_train +) +``` + + + +**Parameters**: + + * **n_x** number of states + * **dt** sample period + * **u_train** input training data + * **z_train** measurement training data + + +--- +### **EM** + +```cpp +EM( + const Fit & fit0, + UniformMatrixList< kMatFreeDim2 > && u_train, + UniformMatrixList< kMatFreeDim2 > && z_train +) +``` + + + +**Parameters**: + + * **fit0** initial fit + * **u_train** input training data + * **z_train** measurement training data + + +--- +### **~EM** + +```cpp +virtual ~EM() =default +``` + + + +--- +### **Run** + +```cpp +const Fit & Run( + bool calc_dynamics =true, + bool calc_Q =true, + bool calc_init =true, + bool calc_output =true, + bool calc_measurement =true, + size_t max_iter =100, + data_t tol =1e-2 +) +``` + + + +**Parameters**: + + * **calc_dynamics** [optional] whether to calculate dynamics (A, B) + * **calc_Q** [optional] whether to calculate process noise covariance + * **calc_init** [optional] whether to calculate initial conditions + * **calc_output** [optional] whether to calculate output function + * **calc_measurement** [optional] whether to calculate parameters for measurement/observation law + * **max_iter** max number of iterations + * **tol** convergence tolerance (max fractional abs change) + + +**Return**: [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) + +--- +### **ReturnData** + +```cpp +inline std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > > ReturnData() +``` + + + +**Return**: tuple(input data, output data) + +--- +### **x** + +```cpp +inline const std::vector< Matrix > & x() const +``` + + + +--- +### **y** + +```cpp +inline const std::vector< Matrix > & y() const +``` + + + +--- +### **sum_E_x_t_x_t** + +```cpp +inline const Matrix & sum_E_x_t_x_t() const +``` + + + +--- +### **sum_E_xu_tm1_xu_tm1** + +```cpp +inline const Matrix & sum_E_xu_tm1_xu_tm1() const +``` + + + +--- +### **sum_E_xu_t_xu_tm1** + +```cpp +inline const Matrix & sum_E_xu_t_xu_tm1() const +``` + + + +--- +### **n_t_tot** + +```cpp +inline size_t n_t_tot() +``` + + + +--- +### **theta** + +```cpp +inline const Vector & theta() const +``` + + + +--- + + +## Protected Function Details + +### **Expectation** + +```cpp +void Expectation( + bool force_common_initial =false +) +``` + + + +**Parameters**: + + * **force_common_initial** whether to force common initial condition for all trials + + +--- +### **Maximization** + +```cpp +void Maximization( + bool calc_dynamics =true, + bool calc_Q =true, + bool calc_init =false, + bool calc_output =false, + bool calc_measurement =false +) +``` + + + +**Parameters**: + + * **calc_dynamics** [optional] whether to caclulate dynamics (A, B) + * **calc_Q** [optional] whether to calculate process noise covariance + * **calc_init** [optional] whether to calculate initial conditions + * **calc_output** [optional] whether to calculate output function + * **calc_measurement** [optional] whether to calculate parameters for measurement/observation law + + +--- +### **MaximizeDynamics** + +```cpp +void MaximizeDynamics() +``` + + + +--- +### **MaximizeQ** + +```cpp +void MaximizeQ() +``` + + + +--- +### **MaximizeInitial** + +```cpp +void MaximizeInitial() +``` + + + +--- +### **MaximizeOutput** + +```cpp +virtual void MaximizeOutput() =0 +``` + + + +**Reimplemented by**: [lds::gaussian::FitEM::MaximizeOutput](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/#function-maximizeoutput), [lds::poisson::FitEM::MaximizeOutput](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/#function-maximizeoutput) + + +--- +### **MaximizeMeasurement** + +```cpp +virtual void MaximizeMeasurement() =0 +``` + + + +**Reimplemented by**: [lds::gaussian::FitEM::MaximizeMeasurement](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/#function-maximizemeasurement), [lds::poisson::FitEM::MaximizeMeasurement](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/#function-maximizemeasurement) + + +--- +### **Smooth** + +```cpp +void Smooth( + bool force_common_initial +) +``` + + + +**Parameters**: + + * **force_common_initial** whether to force common initial conditions + + +--- +### **RecurseKe** + +```cpp +virtual void RecurseKe( + Matrix & Ke, + Cube & P_pre, + Cube & P_post, + size_t t +) =0 +``` + + + +**Parameters**: + + * **Ke** estimator gain + * **P_pre** cov of predicted state est. + * **P_post** cov of postior sate est. + * **t** time + + +**Reimplemented by**: [lds::gaussian::FitEM::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/#function-recurseke), [lds::poisson::FitEM::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/#function-recurseke) + + +--- +### **Reset** + +```cpp +void Reset() +``` + + + +--- +### **InitVars** + +```cpp +void InitVars() +``` + + + +--- +### **UpdateTheta** + +```cpp +Vector UpdateTheta() +``` + + + +**Return**: parameter list + +--- + + +## Protected Attribute Details + +### **u_** + +```cpp +UniformMatrixList< kMatFreeDim2 > u_; +``` + + + +--- +### **z_** + +```cpp +UniformMatrixList< kMatFreeDim2 > z_; +``` + + + +--- +### **x_** + +```cpp +std::vector< Matrix > x_; +``` + + + +--- +### **P_** + +```cpp +std::vector< Cube > P_; +``` + + + +--- +### **P_t_tm1_** + +```cpp +std::vector< Cube > P_t_tm1_; +``` + + + +--- +### **y_** + +```cpp +std::vector< Matrix > y_; +``` + + + +--- +### **diag_y_** + +```cpp +Matrix diag_y_; +``` + + + +--- +### **sum_E_x_t_x_t_** + +```cpp +Matrix sum_E_x_t_x_t_; +``` + + + +--- +### **sum_E_xu_tm1_xu_tm1_** + +```cpp +Matrix sum_E_xu_tm1_xu_tm1_; +``` + + + +--- +### **sum_E_xu_t_xu_tm1_** + +```cpp +Matrix sum_E_xu_t_xu_tm1_; +``` + + + +--- +### **fit_** + +```cpp +Fit fit_; +``` + + + +--- +### **theta_** + +```cpp +Vector theta_; +``` + + + +--- +### **dt_** + +```cpp +data_t dt_ {}; +``` + + + +--- +### **n_u_** + +```cpp +size_t n_u_ {}; +``` + + + +--- +### **n_x_** + +```cpp +size_t n_x_ {}; +``` + + + +--- +### **n_y_** + +```cpp +size_t n_y_ {}; +``` + + + +--- +### **n_trials_** + +```cpp +size_t n_trials_ {}; +``` + + + +--- +### **n_t_** + +```cpp +std::vector< size_t > n_t_; +``` + + + +--- +### **n_t_tot_** + +```cpp +size_t n_t_tot_ {}; +``` + + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md new file mode 100644 index 00000000..47139854 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md @@ -0,0 +1,561 @@ +--- +title: lds::Fit +summary: LDS Fit Type. + +--- + +# lds::Fit + + + +LDS [Fit]() Type. +<br /> `#include <lds_fit.h>` + +Inherited by [lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/), [lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-fit)**() =default<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/). | +| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-fit)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt)<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/). | +| virtual | **[~Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-~fit)**() =default | +| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-u)**() const<br>gets number of inputs | +| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-x)**() const<br>gets number of states | +| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-y)**() const<br>gets number of outputs | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-dt)**() const<br>gets sample period | +| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-a)**() const<br>gets state matrix | +| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-b)**() const<br>gets input matrix | +| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-g)**() const<br>gets input gain | +| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-m)**() const<br>gets process disturbance | +| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-q)**() const<br>gets process noise covariance | +| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-x0)**() const<br>gets initial state estimate | +| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-p0)**() const<br>gets covariance of initial state estimate | +| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-c)**() const<br>gets output matrix | +| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-d)**() const<br>gets output bias | +| virtual const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-r)**() const =0 | +| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-a)**(const Matrix & A)<br>sets state matrix | +| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-b)**(const Matrix & B)<br>sets input matrix | +| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-g)**(const Vector & g)<br>sets input gain/conversion factor | +| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-m)**(const Vector & m)<br>sets process disturbance | +| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-q)**(const Matrix & Q)<br>sets process noise covariance | +| virtual void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-r)**(const Matrix & R) =0<br>sets output noise covariance (if any) | +| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-x0)**(const Vector & x0)<br>sets initial state estimate | +| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-p0)**(const Matrix & P0)<br>sets initial state estimate covariance | +| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-c)**(const Matrix & C)<br>sets output matrix | +| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-d)**(const Vector & d)<br>sets output bias | +| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f)**(Matrix & x, const Matrix & u, size_t t)<br>system dynamics function | +| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f)**(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)<br>system dynamics function | +| virtual View | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-h)**(Matrix & y, const Matrix & x, size_t t) =0<br>output function | + +## Protected Attributes + +| | Name | +| -------------- | -------------- | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-dt-)** <br>sample period | +| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-a-)** <br>state matrix | +| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-b-)** <br>input matrix | +| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-g-)** <br>input gain | +| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-m-)** <br>process noise mean | +| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-q-)** <br>process noise cov | +| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-c-)** <br>output matrix | +| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-d-)** <br>output bias | +| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-r-)** <br>measurement noise | +| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-x0-)** <br>initial state | +| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-p0-)** <br>initial covar | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-y-)** <br>number of outputs | + +--- +--- +## Public Function Details + +### **Fit** + +```cpp +Fit() =default +``` + + + +--- +### **Fit** + +```cpp +Fit( + size_t n_u, + size_t n_x, + size_t n_y, + data_t dt +) +``` + + + +**Parameters**: + + * **n_u** number of inputs + * **n_x** number of states + * **n_y** number of outputs + * **dt** sample period + + +--- +### **~Fit** + +```cpp +virtual ~Fit() =default +``` + + + +--- +### **n_u** + +```cpp +inline size_t n_u() const +``` + + + +--- +### **n_x** + +```cpp +inline size_t n_x() const +``` + + + +--- +### **n_y** + +```cpp +inline size_t n_y() const +``` + + + +--- +### **dt** + +```cpp +inline data_t dt() const +``` + + + +--- +### **A** + +```cpp +inline const Matrix & A() const +``` + + + +--- +### **B** + +```cpp +inline const Matrix & B() const +``` + + + +--- +### **g** + +```cpp +inline const Vector & g() const +``` + + + +--- +### **m** + +```cpp +inline const Vector & m() const +``` + + + +--- +### **Q** + +```cpp +inline const Matrix & Q() const +``` + + + +--- +### **x0** + +```cpp +inline const Vector & x0() const +``` + + + +--- +### **P0** + +```cpp +inline const Matrix & P0() const +``` + + + +--- +### **C** + +```cpp +inline const Matrix & C() const +``` + + + +--- +### **d** + +```cpp +inline const Vector & d() const +``` + + + +--- +### **R** + +```cpp +virtual const Matrix & R() const =0 +``` + + + +**Reimplemented by**: [lds::gaussian::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-r), [lds::poisson::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-r) + + +--- +### **set_A** + +```cpp +inline void set_A( + const Matrix & A +) +``` + + + +--- +### **set_B** + +```cpp +inline void set_B( + const Matrix & B +) +``` + + + +--- +### **set_g** + +```cpp +inline void set_g( + const Vector & g +) +``` + + + +--- +### **set_m** + +```cpp +inline void set_m( + const Vector & m +) +``` + + + +--- +### **set_Q** + +```cpp +inline void set_Q( + const Matrix & Q +) +``` + + + +--- +### **set_R** + +```cpp +virtual void set_R( + const Matrix & R +) =0 +``` + + + +**Reimplemented by**: [lds::gaussian::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-set-r), [lds::poisson::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-set-r) + + +--- +### **set_x0** + +```cpp +inline void set_x0( + const Vector & x0 +) +``` + + + +--- +### **set_P0** + +```cpp +inline void set_P0( + const Matrix & P0 +) +``` + + + +--- +### **set_C** + +```cpp +inline void set_C( + const Matrix & C +) +``` + + + +--- +### **set_d** + +```cpp +inline void set_d( + const Vector & d +) +``` + + + +--- +### **f** + +```cpp +inline View f( + Matrix & x, + const Matrix & u, + size_t t +) +``` + + + +**Parameters**: + + * **x** state estimate (over time) + * **u** input (over time) + * **t** time index + + +**Return**: view of updated state + +--- +### **f** + +```cpp +inline View f( + Matrix & x_pre, + const Matrix & x_post, + const Matrix & u, + size_t t +) +``` + + + +**Parameters**: + + * **x_pre** predicted state est. + * **x_post** posterior state est. + * **u** input (over time) + * **t** time index + + +**Return**: view of predicted state + +--- +### **h** + +```cpp +virtual View h( + Matrix & y, + const Matrix & x, + size_t t +) =0 +``` + + + +**Parameters**: + + * **y** output estimate (over time) + * **x** state estimate (over time) + * **t** time index + + +**Return**: output + +**Reimplemented by**: [lds::gaussian::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-h), [lds::poisson::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-h) + + +--- + + +## Protected Attribute Details + +### **dt_** + +```cpp +data_t dt_ {}; +``` + + + +--- +### **A_** + +```cpp +Matrix A_; +``` + + + +--- +### **B_** + +```cpp +Matrix B_; +``` + + + +--- +### **g_** + +```cpp +Vector g_; +``` + + + +--- +### **m_** + +```cpp +Vector m_; +``` + + + +--- +### **Q_** + +```cpp +Matrix Q_; +``` + + + +--- +### **C_** + +```cpp +Matrix C_; +``` + + + +--- +### **d_** + +```cpp +Vector d_; +``` + + + +--- +### **R_** + +```cpp +Matrix R_; +``` + + + +--- +### **x0_** + +```cpp +Vector x0_; +``` + + + +--- +### **P0_** + +```cpp +Matrix P0_; +``` + + + +--- +### **n_u_** + +```cpp +size_t n_u_ {}; +``` + + + +--- +### **n_x_** + +```cpp +size_t n_x_ {}; +``` + + + +--- +### **n_y_** + +```cpp +size_t n_y_ {}; +``` + + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md new file mode 100644 index 00000000..e38934fa --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md @@ -0,0 +1,370 @@ +--- +title: lds::SSID + +--- + +# lds::SSID + + + + [More...](#detailed-description) + +Inherited by [lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/), [lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid)**() =default<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) type. | +| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid)**(size_t n_x, size_t n_h, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train, const Vector & d =Vector(1).fill(-kInf))<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) type. | +| std::tuple< [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/), Vector > | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) ssid_wt)<br>Runs fitting by subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) | +| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata)**()<br>Returns the I/O data to caller. | + +## Protected Functions + +| | Name | +| -------------- | -------------- | +| void | **[CalcD](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) t_silence =0.1, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d) | +| void | **[CreateHankelDataMat](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat)**()<br>Creates the block-hankel I/O data matrix. | +| virtual void | **[DecomposeData](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-decomposedata)**() =0<br>Decompose data to lower-triangular matrix (used in Solve) | +| void | **[CalcSVD](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) wt)<br>performs the singular value decomposition (SVD) | +| void | **[Solve](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) wt_dc)<br>solves for LDS parameters | +| void | **[RecomputeExtObs](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs)**()<br>recompute extended observability matrix from estimates of A, C | + +## Protected Attributes + +| | Name | +| -------------- | -------------- | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-)** <br>input training data | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-)** <br>measurement training data | +| Matrix | **[D_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-)** <br>block-Hankel I/O data matrix | +| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-)** <br>fit | +| Matrix | **[g_dc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-)** <br>I/O gain @ DC. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-)** <br>sample period | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-)** <br>number of outputs | +| size_t | **[n_h_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-)** | +| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-)** <br>number of input/output data sequences | +| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-)** <br>number of time steps | +| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-)** <br>total number of time steps across trials | +| Matrix | **[L_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-)** <br>lower triangle decomp of covariance matrix | +| Vector | **[s_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-)** <br>singular values | +| Matrix | **[ext_obs_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-)** <br>extended observability matrix | + +## Detailed Description + +```cpp +template <typename Fit > +class lds::SSID; +``` + + +--- +--- +## Public Function Details + +### **SSID** + +```cpp +SSID() =default +``` + + + +--- +### **SSID** + +```cpp +SSID( + size_t n_x, + size_t n_h, + data_t dt, + UniformMatrixList< kMatFreeDim2 > && u_train, + UniformMatrixList< kMatFreeDim2 > && z_train, + const Vector & d =Vector(1).fill(-kInf) +) +``` + + + +**Parameters**: + + * **n_x** number of states + * **n_h** size of block-hankel data matrix + * **dt** sample period + * **u_train** input training data + * **z_train** measurement training data + * **d** output bias + + +--- +### **Run** + +```cpp +std::tuple< Fit, Vector > Run( + SSIDWt ssid_wt +) +``` + + + +**Parameters**: + + * **ssid_wt** weight for singular value decomp + + +**Return**: tuple ([Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/), singular values) + +--- +### **ReturnData** + +```cpp +inline std::tuple< UniformMatrixList< kMatFreeDim2 >, UniformMatrixList< kMatFreeDim2 > > ReturnData() +``` + + + +**Return**: tuple(input data, output data) + +--- + + +## Protected Function Details + +### **CalcD** + +```cpp +void CalcD( + data_t t_silence =0.1, + data_t thresh_silence =0.001 +) +``` + + + +**Parameters**: + + * **t_silence** threshold on period of time that qualifies as "silence" + * **thresh_silence** threshold on input amplitude u that qualifies as "silence" + + +--- +### **CreateHankelDataMat** + +```cpp +void CreateHankelDataMat() +``` + + + +Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC. + + +--- +### **DecomposeData** + +```cpp +virtual void DecomposeData() =0 +``` + + + +**Reimplemented by**: [lds::gaussian::FitSSID::DecomposeData](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/#function-decomposedata), [lds::poisson::FitSSID::DecomposeData](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/#function-decomposedata) + + +--- +### **CalcSVD** + +```cpp +void CalcSVD( + SSIDWt wt +) +``` + + + +**Parameters**: + + * **ssid_wt** weight for SVD + + +--- +### **Solve** + +```cpp +void Solve( + data_t wt_dc +) +``` + + + +**Parameters**: + + * **wt_dc** weight placed on getting correct DC I/O gain + + +--- +### **RecomputeExtObs** + +```cpp +void RecomputeExtObs() +``` + + + +--- + + +## Protected Attribute Details + +### **u_** + +```cpp +UniformMatrixList< kMatFreeDim2 > u_; +``` + + + +--- +### **z_** + +```cpp +UniformMatrixList< kMatFreeDim2 > z_; +``` + + + +--- +### **D_** + +```cpp +Matrix D_; +``` + + + +--- +### **fit_** + +```cpp +Fit fit_; +``` + + + +--- +### **g_dc_** + +```cpp +Matrix g_dc_; +``` + + + +--- +### **dt_** + +```cpp +data_t dt_ {}; +``` + + + +--- +### **n_u_** + +```cpp +size_t n_u_ {}; +``` + + + +--- +### **n_x_** + +```cpp +size_t n_x_ {}; +``` + + + +--- +### **n_y_** + +```cpp +size_t n_y_ {}; +``` + + + +--- +### **n_h_** + +```cpp +size_t n_h_ {}; +``` + + + +--- +### **n_trials_** + +```cpp +size_t n_trials_ {}; +``` + + + +--- +### **n_t_** + +```cpp +std::vector< size_t > n_t_; +``` + + + +--- +### **n_t_tot_** + +```cpp +size_t n_t_tot_ {}; +``` + + + +--- +### **L_** + +```cpp +Matrix L_; +``` + + + +--- +### **s_** + +```cpp +Vector s_; +``` + + + +--- +### **ext_obs_t_** + +```cpp +Matrix ext_obs_t_; +``` + + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md new file mode 100644 index 00000000..f1cdc293 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md @@ -0,0 +1,360 @@ +--- +title: lds::SwitchedController +summary: SwitchedController Type. + +--- + +# lds::SwitchedController + + + +[SwitchedController]() Type. [More...](#detailed-description) + + +<br /> `#include <lds_sctrl.h>` + +Inherits from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) + +Inherited by [lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/), [lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**() =default<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/). | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > & systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/). | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > && systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/) (moves systems). | +| void | **[Switch](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switch)**(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller. | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc)<br>sets state feedback gains | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc)<br>sets state feedback gains (moving) | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc_inty)<br>sets integral feedback gains | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc_inty)<br>sets integral feedback gains (moving) | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc_u)<br>sets input feedback gains | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc_u)<br>sets input feedback gains (moving) | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) & g)<br>sets input gain used during controller design | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) && g)<br>sets input gain used during controller design (moving) | + +## Protected Attributes + +| | Name | +| -------------- | -------------- | +| std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > | **[systems_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-systems-)** <br>underlying sub-systems which are switched between | +| size_t | **[n_sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-n-sys-)** <br>number of systems | +| size_t | **[idx_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-idx-)** <br>current system/controller index. | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-list-)** | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_inty_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-inty-list-)** | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_u_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-u-list-)** | +| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) | **[g_design_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-g-design-list-)** | + +## Additional inherited members + +**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) by moving the system object. | +| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | +| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | +| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys)**() const | +| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc)**() const<br>Get state feedback controller gain. | +| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty)**() const<br>Get integral controller gain. | +| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u)**() const<br>Get input feedback controller gain. | +| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design)**() const<br>Get input gain used in controller design. | +| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref)**() const<br>Get reference input. | +| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref)**() const<br>Get reference state. | +| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref)**() const<br>Get reference output. | +| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type)**() const<br>Get controller type. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb)**() const<br>Get control lower bound. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub)**() const<br>Get control upper bound. | +| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys)<br>Set system. | +| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | +| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | +| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref)**(const Vector & y_ref)<br>Set reference output (y_ref) | +| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | +| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | +| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | +| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset)**()<br>reset system and control variables. | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print)**()<br>prints variables to stdout | + +**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-)** <br>underlying LDS | +| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-)** <br>control signal | +| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-)** <br>control signal that is _returned_ to user | +| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-)** <br>input gain of the system used for controller design | +| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-)** <br>reference input | +| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-)** <br>reference input at previous time step | +| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-)** <br>reference state | +| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-)** <br>reference output | +| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-)** | +| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-)** <br>state controller gain | +| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-)** <br>input controller gain (optional when control updates \deltaU) | +| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-)** <br>integral controller gain | +| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-)** | +| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-)** | +| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-)** | +| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-)** | +| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | +| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-)** <br>integrated error | +| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | +| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | +| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-)** | +| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-)** | +| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-)** <br>lower bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-)** <br>upper bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-)** <br>antiwindup time constant | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | +| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-)** <br>controller type | + + +## Detailed Description + +```cpp +template <typename System > +class lds::SwitchedController; +``` + + +--- +--- +## Public Function Details + +### **SwitchedController** + +```cpp +SwitchedController() =default +``` + + + +--- +### **SwitchedController** + +```cpp +inline SwitchedController( + const std::vector< System > & systems, + data_t u_lb, + data_t u_ub, + size_t control_type =0 +) +``` + + + +**Parameters**: + + * **systems** vector of sub-systems + * **u_lb** lower bound on control (u) + * **u_ub** upper bound on control (u) + * **control_type** [optional] control type bit mask + + +--- +### **SwitchedController** + +```cpp +inline SwitchedController( + std::vector< System > && systems, + data_t u_lb, + data_t u_ub, + size_t control_type =0 +) +``` + + + +**Parameters**: + + * **systems** vector of sub-systems + * **u_lb** lower bound on control (u) + * **u_ub** upper bound on control (u) + * **control_type** [optional] control type bit mask + + +--- +### **Switch** + +```cpp +inline void Switch( + size_t idx, + bool do_force_switch =false +) +``` + + + +**Parameters**: + + * **idx** index + * **do_force_switch** whether to force a system switch even if already there. + + +--- +### **set_Kc** + +```cpp +inline void set_Kc( + const UniformMatrixList<> & Kc +) +``` + + + +--- +### **set_Kc** + +```cpp +inline void set_Kc( + UniformMatrixList<> && Kc +) +``` + + + +--- +### **set_Kc_inty** + +```cpp +inline void set_Kc_inty( + const UniformMatrixList<> & Kc_inty +) +``` + + + +--- +### **set_Kc_inty** + +```cpp +inline void set_Kc_inty( + UniformMatrixList<> && Kc_inty +) +``` + + + +--- +### **set_Kc_u** + +```cpp +inline void set_Kc_u( + const UniformMatrixList<> & Kc_u +) +``` + + + +--- +### **set_Kc_u** + +```cpp +inline void set_Kc_u( + UniformMatrixList<> && Kc_u +) +``` + + + +--- +### **set_g_design** + +```cpp +inline void set_g_design( + const UniformVectorList & g +) +``` + + + +--- +### **set_g_design** + +```cpp +inline void set_g_design( + UniformVectorList && g +) +``` + + + +--- + + +## Protected Attribute Details + +### **systems_** + +```cpp +std::vector< System > systems_; +``` + + + +--- +### **n_sys_** + +```cpp +size_t n_sys_ {}; +``` + + + +--- +### **idx_** + +```cpp +size_t idx_ {}; +``` + + + +--- +### **Kc_list_** + +```cpp +UniformMatrixList Kc_list_; +``` + + + +--- +### **Kc_inty_list_** + +```cpp +UniformMatrixList Kc_inty_list_; +``` + + + +--- +### **Kc_u_list_** + +```cpp +UniformMatrixList Kc_u_list_; +``` + + + +--- +### **g_design_list_** + +```cpp +UniformVectorList g_design_list_; +``` + + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md new file mode 100644 index 00000000..0b29291a --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md @@ -0,0 +1,886 @@ +--- +title: lds::System +summary: Linear Dynamical System Type. + +--- + +# lds::System + + + +Linear Dynamical [System]() Type. +<br /> `#include <lds_sys.h>` + +Inherited by [lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/), [lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-system)**() =default<br>Constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/). | +| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-system)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0)<br>constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) | +| virtual | **[~System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-~system)**() | +| void | **[Filter](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-filter)**(const Vector & u_tm1, const Vector & z)<br>Filter data to produce causal state estimates. | +| virtual const Vector & | **[Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-simulate)**(const Vector & u_tm1) =0<br>simulates system (single time step) | +| void | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-f)**(const Vector & u, bool do_add_noise =false)<br>system dynamics function | +| virtual void | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h)**() =0<br>system output function | +| virtual Vector | **[h_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h-)**(Vector x) =0<br>system output function (stateless) | +| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-u)**() const<br>Get number of inputs. | +| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-x)**() const<br>Get number of states. | +| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-y)**() const<br>Get number of outputs. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-dt)**() const<br>Get sample period. | +| const Vector & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x)**() const<br>Get current state. | +| const Matrix & | **[P](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p)**() const<br>Get covariance of state estimate. | +| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m)**() const<br>Get current process disturbance/bias. | +| const Matrix & | **[P_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p-m)**() const<br>Get covariance of process disturbance estimate. | +| const Vector & | **[cx](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-cx)**() const<br>Get C*x. | +| const Vector & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-y)**() const<br>Get output. | +| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x0)**() const<br>Get initial state. | +| const Vector & | **[m0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m0)**() const<br>Get initial disturbance. | +| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-a)**() const<br>Get state matrix. | +| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-b)**() const<br>Get input matrix. | +| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-g)**() const<br>Get input gain/conversion factor. | +| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-c)**() const<br>Get output matrix. | +| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-d)**() const<br>Get output bias. | +| const Matrix & | **[Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke)**() const<br>Get estimator gain. | +| const Matrix & | **[Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke-m)**() const<br>Get estimator gain for process disturbance (m) | +| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q)**()<br>Get process noise covariance. | +| const Matrix & | **[Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q-m)**()<br>Get process noise covariance of disturbance evoluation. | +| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0)**()<br>Get covariance of initial state. | +| const Matrix & | **[P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0-m)**()<br>Get covariance of initial process disturbance. | +| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-a)**(const Matrix & A)<br>Set state matrix. | +| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-b)**(const Matrix & B)<br>Set input matrix. | +| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-m)**(const Vector & m, bool do_force_assign =false)<br>Set process disturbance. | +| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-g)**(const Vector & g)<br>Set input gain. | +| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q)**(const Matrix & Q)<br>Set process noise covariance. | +| void | **[set_Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q-m)**(const Matrix & Q_m)<br>Set process noise covariance of disturbance evoluation. | +| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x0)**(const Vector & x0)<br>Set initial state. | +| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0)**(const Matrix & P0)<br>Set covariance of initial state. | +| void | **[set_P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0-m)**(const Matrix & P0_m)<br>Set covariance of initial process disturbance. | +| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-c)**(const Matrix & C)<br>Set output matrix. | +| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-d)**(const Vector & d)<br>Set output bias. | +| void | **[set_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x)**(const Vector & x)<br>Set state of system. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-reset)**()<br>Reset system variables. | +| std::vector< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[nstep_pred_block](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-nstep-pred-block)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > u, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > z, size_t n_pred =1) | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-print)**()<br>Print system variables to stdout. | + +## Protected Functions + +| | Name | +| -------------- | -------------- | +| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-recurseke)**() =0<br>Recursively recalculate estimator gain (Ke) | +| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-initvars)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0) | + +## Public Attributes + +| | Name | +| -------------- | -------------- | +| bool | **[do_adapt_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-do-adapt-m)** <br>whether to adaptively estimate disturbance m | + +## Protected Attributes + +| | Name | +| -------------- | -------------- | +| std::size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-x-)** <br>number of states | +| std::size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-u-)** <br>number of inputs | +| std::size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-y-)** <br>number of outputs | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-dt-)** <br>sample period | +| Vector | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x-)** <br>state | +| Matrix | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-)** <br>covariance of state estimate | +| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m-)** <br>process disturbance | +| Matrix | **[P_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-m-)** <br>covariance of disturbance estimate | +| Vector | **[cx_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-cx-)** <br>C*x. | +| Vector | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-y-)** <br>output | +| Vector | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-z-)** <br>measurement | +| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x0-)** <br>initial state | +| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-)** <br>covariance of initial state estimate | +| Vector | **[m0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m0-)** <br>initial process disturbance | +| Matrix | **[P0_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-m-)** <br>covariance of initial disturbance est. | +| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-a-)** <br>state matrix | +| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-b-)** <br>input matrix | +| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-g-)** <br>input gain | +| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-)** <br>covariance of process noise | +| Matrix | **[Q_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-m-)** <br>covariance of disturbance random walk | +| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-c-)** <br>output matrix | +| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-d-)** <br>output bias | +| Matrix | **[Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-)** <br>estimator gain | +| Matrix | **[Ke_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-m-)** <br>estimator gain for process disturbance | + +--- +--- +## Public Function Details + +### **System** + +```cpp +System() =default +``` + + + +--- +### **System** + +```cpp +System( + size_t n_u, + size_t n_x, + size_t n_y, + data_t dt, + data_t p0 =kDefaultP0, + data_t q0 =kDefaultQ0 +) +``` + + + +**Parameters**: + + * **n_u** number of inputs + * **n_x** number of states + * **n_y** number of outputs + * **dt** sample period + * **p0** diagonal elements for state estimate covariance + * **q0** diagonal elements for process noise covariance + + +--- +### **~System** + +```cpp +inline virtual ~System() +``` + + + +--- +### **Filter** + +```cpp +void Filter( + const Vector & u_tm1, + const Vector & z +) +``` + + + +**Parameters**: + + * **u_tm1** input at t-minus-1 + * **z_t** current measurement + + +Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating. + + +--- +### **Simulate** + +```cpp +virtual const Vector & Simulate( + const Vector & u_tm1 +) =0 +``` + + + +**Parameters**: + + * **u_tm1** input at time t-1 + + +**Return**: simulated measurement at time t + +**Reimplemented by**: [lds::gaussian::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-simulate), [lds::poisson::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-simulate) + + +--- +### **f** + +```cpp +inline void f( + const Vector & u, + bool do_add_noise =false +) +``` + + + +**Parameters**: + + * **u** input + * **do_add_noise** whether to add simulated process noise + + +--- +### **h** + +```cpp +virtual void h() =0 +``` + + + +**Reimplemented by**: [lds::gaussian::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h), [lds::poisson::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h) + + +--- +### **h_** + +```cpp +virtual Vector h_( + Vector x +) =0 +``` + + + +**Parameters**: + + * **x_t** state at time t + + +**Return**: predicted state at time t + 1 + +**Reimplemented by**: [lds::gaussian::System::h_](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h-), [lds::poisson::System::h_](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h-) + + +--- +### **n_u** + +```cpp +inline size_t n_u() const +``` + + + +--- +### **n_x** + +```cpp +inline size_t n_x() const +``` + + + +--- +### **n_y** + +```cpp +inline size_t n_y() const +``` + + + +--- +### **dt** + +```cpp +inline data_t dt() const +``` + + + +--- +### **x** + +```cpp +inline const Vector & x() const +``` + + + +--- +### **P** + +```cpp +inline const Matrix & P() const +``` + + + +--- +### **m** + +```cpp +inline const Vector & m() const +``` + + + +--- +### **P_m** + +```cpp +inline const Matrix & P_m() const +``` + + + +--- +### **cx** + +```cpp +inline const Vector & cx() const +``` + + + +--- +### **y** + +```cpp +inline const Vector & y() const +``` + + + +--- +### **x0** + +```cpp +inline const Vector & x0() const +``` + + + +--- +### **m0** + +```cpp +inline const Vector & m0() const +``` + + + +--- +### **A** + +```cpp +inline const Matrix & A() const +``` + + + +--- +### **B** + +```cpp +inline const Matrix & B() const +``` + + + +--- +### **g** + +```cpp +inline const Vector & g() const +``` + + + +--- +### **C** + +```cpp +inline const Matrix & C() const +``` + + + +--- +### **d** + +```cpp +inline const Vector & d() const +``` + + + +--- +### **Ke** + +```cpp +inline const Matrix & Ke() const +``` + + + +--- +### **Ke_m** + +```cpp +inline const Matrix & Ke_m() const +``` + + + +--- +### **Q** + +```cpp +inline const Matrix & Q() +``` + + + +--- +### **Q_m** + +```cpp +inline const Matrix & Q_m() +``` + + + +--- +### **P0** + +```cpp +inline const Matrix & P0() +``` + + + +--- +### **P0_m** + +```cpp +inline const Matrix & P0_m() +``` + + + +--- +### **set_A** + +```cpp +inline void set_A( + const Matrix & A +) +``` + + + +--- +### **set_B** + +```cpp +inline void set_B( + const Matrix & B +) +``` + + + +--- +### **set_m** + +```cpp +inline void set_m( + const Vector & m, + bool do_force_assign =false +) +``` + + + +--- +### **set_g** + +```cpp +inline void set_g( + const Vector & g +) +``` + + + +--- +### **set_Q** + +```cpp +inline void set_Q( + const Matrix & Q +) +``` + + + +--- +### **set_Q_m** + +```cpp +inline void set_Q_m( + const Matrix & Q_m +) +``` + + + +--- +### **set_x0** + +```cpp +inline void set_x0( + const Vector & x0 +) +``` + + + +--- +### **set_P0** + +```cpp +inline void set_P0( + const Matrix & P0 +) +``` + + + +--- +### **set_P0_m** + +```cpp +inline void set_P0_m( + const Matrix & P0_m +) +``` + + + +--- +### **set_C** + +```cpp +inline void set_C( + const Matrix & C +) +``` + + + +--- +### **set_d** + +```cpp +inline void set_d( + const Vector & d +) +``` + + + +--- +### **set_x** + +```cpp +inline void set_x( + const Vector & x +) +``` + + + +--- +### **Reset** + +```cpp +void Reset() +``` + + + +--- +### **nstep_pred_block** + +```cpp +std::vector< UniformMatrixList< kMatFreeDim2 > > nstep_pred_block( + UniformMatrixList< kMatFreeDim2 > u, + UniformMatrixList< kMatFreeDim2 > z, + size_t n_pred =1 +) +``` + + + +--- +### **Print** + +```cpp +void Print() +``` + + + +--- + + +## Protected Function Details + +### **RecurseKe** + +```cpp +virtual void RecurseKe() =0 +``` + + + +**Reimplemented by**: [lds::gaussian::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-recurseke), [lds::poisson::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-recurseke) + + +--- +### **InitVars** + +```cpp +void InitVars( + data_t p0 =kDefaultP0, + data_t q0 =kDefaultQ0 +) +``` + + + +--- + + +## Public Attribute Details + +### **do_adapt_m** + +```cpp +bool do_adapt_m {}; +``` + + + +--- + + +## Protected Attribute Details + +### **n_x_** + +```cpp +std::size_t n_x_ {}; +``` + + + +--- +### **n_u_** + +```cpp +std::size_t n_u_ {}; +``` + + + +--- +### **n_y_** + +```cpp +std::size_t n_y_ {}; +``` + + + +--- +### **dt_** + +```cpp +data_t dt_ {}; +``` + + + +--- +### **x_** + +```cpp +Vector x_; +``` + + + +--- +### **P_** + +```cpp +Matrix P_; +``` + + + +--- +### **m_** + +```cpp +Vector m_; +``` + + + +--- +### **P_m_** + +```cpp +Matrix P_m_; +``` + + + +--- +### **cx_** + +```cpp +Vector cx_; +``` + + + +--- +### **y_** + +```cpp +Vector y_; +``` + + + +--- +### **z_** + +```cpp +Vector z_; +``` + + + +--- +### **x0_** + +```cpp +Vector x0_; +``` + + + +--- +### **P0_** + +```cpp +Matrix P0_; +``` + + + +--- +### **m0_** + +```cpp +Vector m0_; +``` + + + +--- +### **P0_m_** + +```cpp +Matrix P0_m_; +``` + + + +--- +### **A_** + +```cpp +Matrix A_; +``` + + + +--- +### **B_** + +```cpp +Matrix B_; +``` + + + +--- +### **g_** + +```cpp +Vector g_; +``` + + + +--- +### **Q_** + +```cpp +Matrix Q_; +``` + + + +--- +### **Q_m_** + +```cpp +Matrix Q_m_; +``` + + + +--- +### **C_** + +```cpp +Matrix C_; +``` + + + +--- +### **d_** + +```cpp +Vector d_; +``` + + + +--- +### **Ke_** + +```cpp +Matrix Ke_; +``` + + + +--- +### **Ke_m_** + +```cpp +Matrix Ke_m_; +``` + + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformMatrixList.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_matrix_list.md similarity index 50% rename from misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformMatrixList.md rename to misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_matrix_list.md index b7acbf92..437ac7f7 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1UniformMatrixList.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_matrix_list.md @@ -1,244 +1,261 @@ ---- -title: lds::UniformMatrixList - ---- - -# lds::UniformMatrixList - - - - [More...](#detailed-description) - -Inherits from std::vector< Matrix > - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-uniformmatrixlist)**() =default<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/). | -| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-uniformmatrixlist)**(const std::vector< Matrix > & mats, std::array< size_t, 2 > dim ={0, 0})<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) by copying existing vector of Matrix if dimensions consistent. | -| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-uniformmatrixlist)**(std::vector< Matrix > && mats, std::array< size_t, 2 > dim ={0, 0})<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) by moving existing vector of Matrix if dimensions consistent. | -| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-uniformmatrixlist)**(std::initializer_list< Matrix > mats, std::array< size_t, 2 > dim ={0, 0})<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) from initializer_list of Matrix if dimensions consistent. | -| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-uniformmatrixlist)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< D > & that)<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) (copy). | -| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-uniformmatrixlist)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< D > && that)<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) (move). | -| | **[~UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-~uniformmatrixlist)**() =default<br>Destroys the object. | -| const std::array< size_t, 2 > & | **[dim](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-dim)**(size_t n =0) const<br>gets dimensions of uniformly sized matrices | -| size_t | **[size](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-size)**()<br>size of container | -| const Matrix & | **[at](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-at)**(size_t n)<br>gets reference to n^th element | -| void | **[Swap](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-swap)**(Matrix & that, size_t n)<br>swaps input matrix with n^th matrix of list | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< D > & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-operator=)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< D > & that)<br>assigns the contents (copy) | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< D > & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/#function-operator=)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< D > && that)<br>assigns the contents (move) | - -## Detailed Description - -```cpp -template <MatrixListFreeDim D =kMatFreeDimNone> -class lds::UniformMatrixList; -``` - - ---- ---- -## Public Function Details - -### **UniformMatrixList** - -```cpp -UniformMatrixList() =default -``` - - - ---- -### **UniformMatrixList** - -```cpp -explicit UniformMatrixList( - const std::vector< Matrix > & mats, - std::array< size_t, 2 > dim ={0, 0} -) -``` - - - -**Parameters**: - - * **mats** input matrices - * **dim** dimensions - - ---- -### **UniformMatrixList** - -```cpp -explicit UniformMatrixList( - std::vector< Matrix > && mats, - std::array< size_t, 2 > dim ={0, 0} -) -``` - - - -**Parameters**: - - * **mats** input matrices - * **dim** dimensions - - ---- -### **UniformMatrixList** - -```cpp -UniformMatrixList( - std::initializer_list< Matrix > mats, - std::array< size_t, 2 > dim ={0, 0} -) -``` - - - -**Parameters**: - - * **mats** input matrices - * **dim** dimensions - - ---- -### **UniformMatrixList** - -```cpp -UniformMatrixList( - const UniformMatrixList< D > & that -) -``` - - - -**Parameters**: - - * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) - - ---- -### **UniformMatrixList** - -```cpp -UniformMatrixList( - UniformMatrixList< D > && that -) -``` - - - -**Parameters**: - - * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) - - ---- -### **~UniformMatrixList** - -```cpp -~UniformMatrixList() =default -``` - - - ---- -### **dim** - -```cpp -inline const std::array< size_t, 2 > & dim( - size_t n =0 -) const -``` - - - -**Parameters**: - - * **n** [optional] index in list of matrices - - -**Return**: dimensions - ---- -### **size** - -```cpp -inline size_t size() -``` - - - ---- -### **at** - -```cpp -inline const Matrix & at( - size_t n -) -``` - - - ---- -### **Swap** - -```cpp -inline void Swap( - Matrix & that, - size_t n -) -``` - - - -**Parameters**: - - * **that** input matrix - * **n** index where the matrix is moved - - ---- -### **operator=** - -```cpp -inline UniformMatrixList< D > & operator=( - const UniformMatrixList< D > & that -) -``` - - - -**Parameters**: - - * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) - - -**Return**: reference to object - ---- -### **operator=** - -```cpp -inline UniformMatrixList< D > & operator=( - UniformMatrixList< D > && that -) -``` - - - -**Parameters**: - - * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) - - -**Return**: reference to object - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: lds::UniformMatrixList + +--- + +# lds::UniformMatrixList + + + + [More...](#detailed-description) + +Inherits from std::vector< Matrix > + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist)**() =default<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/). | +| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist)**(const std::vector< Matrix > & mats, std::array< size_t, 2 > dim ={0, 0})<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) by copying existing vector of Matrix if dimensions consistent. | +| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist)**(std::vector< Matrix > && mats, std::array< size_t, 2 > dim ={0, 0})<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) by moving existing vector of Matrix if dimensions consistent. | +| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist)**(std::initializer_list< Matrix > mats, std::array< size_t, 2 > dim ={0, 0})<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) from initializer_list of Matrix if dimensions consistent. | +| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< D > & that)<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) (copy). | +| | **[UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< D > && that)<br>Constructs a new [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) (move). | +| | **[~UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-~uniformmatrixlist)**() =default<br>Destroys the object. | +| const std::array< size_t, 2 > & | **[dim](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-dim)**(size_t n =0) const<br>gets dimensions of uniformly sized matrices | +| size_t | **[size](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-size)**()<br>size of container | +| const Matrix & | **[at](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-at)**(size_t n)<br>gets reference to n^th element | +| void | **[Swap](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-swap)**(Matrix & that, size_t n)<br>swaps input matrix with n^th matrix of list | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< D > & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< D > & that)<br>assigns the contents (copy) | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< D > & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< D > && that)<br>assigns the contents (move) | +| void | **[append](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-append)**(const Matrix & mat)<br>appends a matrix to the list | + +## Detailed Description + +```cpp +template <MatrixListFreeDim D =kMatFreeDimNone> +class lds::UniformMatrixList; +``` + + +--- +--- +## Public Function Details + +### **UniformMatrixList** + +```cpp +UniformMatrixList() =default +``` + + + +--- +### **UniformMatrixList** + +```cpp +explicit UniformMatrixList( + const std::vector< Matrix > & mats, + std::array< size_t, 2 > dim ={0, 0} +) +``` + + + +**Parameters**: + + * **mats** input matrices + * **dim** dimensions + + +--- +### **UniformMatrixList** + +```cpp +explicit UniformMatrixList( + std::vector< Matrix > && mats, + std::array< size_t, 2 > dim ={0, 0} +) +``` + + + +**Parameters**: + + * **mats** input matrices + * **dim** dimensions + + +--- +### **UniformMatrixList** + +```cpp +UniformMatrixList( + std::initializer_list< Matrix > mats, + std::array< size_t, 2 > dim ={0, 0} +) +``` + + + +**Parameters**: + + * **mats** input matrices + * **dim** dimensions + + +--- +### **UniformMatrixList** + +```cpp +UniformMatrixList( + const UniformMatrixList< D > & that +) +``` + + + +**Parameters**: + + * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) + + +--- +### **UniformMatrixList** + +```cpp +UniformMatrixList( + UniformMatrixList< D > && that +) +``` + + + +**Parameters**: + + * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) + + +--- +### **~UniformMatrixList** + +```cpp +~UniformMatrixList() =default +``` + + + +--- +### **dim** + +```cpp +inline const std::array< size_t, 2 > & dim( + size_t n =0 +) const +``` + + + +**Parameters**: + + * **n** [optional] index in list of matrices + + +**Return**: dimensions + +--- +### **size** + +```cpp +inline size_t size() +``` + + + +--- +### **at** + +```cpp +inline const Matrix & at( + size_t n +) +``` + + + +--- +### **Swap** + +```cpp +inline void Swap( + Matrix & that, + size_t n +) +``` + + + +**Parameters**: + + * **that** input matrix + * **n** index where the matrix is moved + + +--- +### **operator=** + +```cpp +inline UniformMatrixList< D > & operator=( + const UniformMatrixList< D > & that +) +``` + + + +**Parameters**: + + * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) + + +**Return**: reference to object + +--- +### **operator=** + +```cpp +inline UniformMatrixList< D > & operator=( + UniformMatrixList< D > && that +) +``` + + + +**Parameters**: + + * **that** another [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) + + +**Return**: reference to object + +--- +### **append** + +```cpp +void append( + const Matrix & mat +) +``` + + + +**Parameters**: + + * **mat** input matrix + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md new file mode 100644 index 00000000..936fae46 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md @@ -0,0 +1,235 @@ +--- +title: lds::UniformSystemList + +--- + +# lds::UniformSystemList + + + + [More...](#detailed-description) + +Inherits from std::vector< System > + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist)**() =default<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/). | +| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > & systems, std::array< size_t, 3 > dim ={0, 0, 0})<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) by copying existing vector of [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) if dimensions consistent. | +| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > && systems, std::array< size_t, 3 > dim ={0, 0, 0})<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) by moving existing vector of [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) if dimensions consistent. | +| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist)**(std::initializer_list< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > systems, std::array< size_t, 3 > dim ={0, 0, 0})<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) from initializer_list of [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) if dimensions consistent. | +| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist)**(const [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) & that)<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) (copy). | +| | **[UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist)**([UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) && that)<br>Constructs a new [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) (move). | +| | **[~UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-~uniformsystemlist)**() =default<br>Destroys the object. | +| const std::array< size_t, 3 > & | **[dim](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-dim)**() const<br>gets dimensions of the uniformly sized systems | +| size_t | **[size](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-size)**()<br>size of container | +| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & | **[at](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-at)**(size_t n)<br>gets reference to n^th element | +| void | **[Swap](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-swap)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & that, size_t n)<br>swaps input system with n^th system of list | +| [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=)**(const [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) & that)<br>assigns the contents (copy) | +| [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=)**([UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) && that)<br>assigns the contents (move) | + +## Detailed Description + +```cpp +template <typename System > +class lds::UniformSystemList; +``` + + +--- +--- +## Public Function Details + +### **UniformSystemList** + +```cpp +UniformSystemList() =default +``` + + + +--- +### **UniformSystemList** + +```cpp +explicit UniformSystemList( + const std::vector< System > & systems, + std::array< size_t, 3 > dim ={0, 0, 0} +) +``` + + + +**Parameters**: + + * **systems** input systems + * **dim** dimensions (n_u, n_x, n_y) + + +--- +### **UniformSystemList** + +```cpp +explicit UniformSystemList( + std::vector< System > && systems, + std::array< size_t, 3 > dim ={0, 0, 0} +) +``` + + + +**Parameters**: + + * **systems** input systems + * **dim** dimensions (n_u, n_x, n_y) + + +--- +### **UniformSystemList** + +```cpp +UniformSystemList( + std::initializer_list< System > systems, + std::array< size_t, 3 > dim ={0, 0, 0} +) +``` + + + +**Parameters**: + + * **systems** input systems + * **dim** dimensions (n_u, n_x, n_y) + + +--- +### **UniformSystemList** + +```cpp +UniformSystemList( + const UniformSystemList & that +) +``` + + + +**Parameters**: + + * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) + + +--- +### **UniformSystemList** + +```cpp +UniformSystemList( + UniformSystemList && that +) +``` + + + +**Parameters**: + + * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) + + +--- +### **~UniformSystemList** + +```cpp +~UniformSystemList() =default +``` + + + +--- +### **dim** + +```cpp +inline const std::array< size_t, 3 > & dim() const +``` + + + +--- +### **size** + +```cpp +inline size_t size() +``` + + + +--- +### **at** + +```cpp +inline const System & at( + size_t n +) +``` + + + +--- +### **Swap** + +```cpp +inline void Swap( + System & that, + size_t n +) +``` + + + +**Parameters**: + + * **that** input system + * **n** index where the system is moved + + +--- +### **operator=** + +```cpp +inline UniformSystemList & operator=( + const UniformSystemList & that +) +``` + + + +**Parameters**: + + * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) + + +**Return**: reference to object + +--- +### **operator=** + +```cpp +inline UniformSystemList & operator=( + UniformSystemList && that +) +``` + + + +**Parameters**: + + * **that** another [UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/) + + +**Return**: reference to object + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md new file mode 100644 index 00000000..7f443dd4 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md @@ -0,0 +1,225 @@ +--- +title: lds::UniformVectorList + +--- + +# lds::UniformVectorList + + + +Inherits from std::vector< Vector > + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist)**() =default<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/). | +| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist)**(const std::vector< Vector > & vecs, size_t dim =0)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) by copying existing vector of Vector if dimensions consistent. | +| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist)**(std::vector< Vector > && vecs, size_t dim =0)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) by moving existing vector of Vector if dimensions consistent. | +| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist)**(std::initializer_list< Vector > vecs, size_t dim =0)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) from initializer_list of Vector if dimensions consistent. | +| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) & that)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) (copy) | +| | **[UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) && that)<br>Constructs a new [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) (move) | +| | **[~UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-~uniformvectorlist)**() =default<br>Destroys the object. | +| size_t | **[dim](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-dim)**() const<br>gets dimensions of the uniformly sized matrices | +| size_t | **[size](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-size)**()<br>size of container | +| const Vector & | **[at](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-at)**(size_t n)<br>gets reference to n^th element | +| void | **[Swap](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-swap)**(Vector & that, size_t n)<br>swaps input matrix with n^th vector of list | +| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) & that)<br>assigns the contents (copy) | +| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) & | **[operator=](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) && that)<br>assigns the contents (move) | + +--- +--- +## Public Function Details + +### **UniformVectorList** + +```cpp +UniformVectorList() =default +``` + + + +--- +### **UniformVectorList** + +```cpp +explicit UniformVectorList( + const std::vector< Vector > & vecs, + size_t dim =0 +) +``` + + + +**Parameters**: + + * **vecs** input vectors + * **dims** dimension + + +--- +### **UniformVectorList** + +```cpp +explicit UniformVectorList( + std::vector< Vector > && vecs, + size_t dim =0 +) +``` + + + +**Parameters**: + + * **vecs** input vectors + * **dim** dimension + + +--- +### **UniformVectorList** + +```cpp +UniformVectorList( + std::initializer_list< Vector > vecs, + size_t dim =0 +) +``` + + + +**Parameters**: + + * **vecs** input vectors + * **dim** dimension + + +--- +### **UniformVectorList** + +```cpp +UniformVectorList( + const UniformVectorList & that +) +``` + + + +**Parameters**: + + * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) + + +--- +### **UniformVectorList** + +```cpp +UniformVectorList( + UniformVectorList && that +) +``` + + + +**Parameters**: + + * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) + + +--- +### **~UniformVectorList** + +```cpp +~UniformVectorList() =default +``` + + + +--- +### **dim** + +```cpp +inline size_t dim() const +``` + + + +--- +### **size** + +```cpp +inline size_t size() +``` + + + +--- +### **at** + +```cpp +inline const Vector & at( + size_t n +) +``` + + + +--- +### **Swap** + +```cpp +inline void Swap( + Vector & that, + size_t n +) +``` + + + +**Parameters**: + + * **that** input vector + * **n** index where the vector is moved + + +--- +### **operator=** + +```cpp +inline UniformVectorList & operator=( + const UniformVectorList & that +) +``` + + + +**Parameters**: + + * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) + + +**Return**: reference to object + +--- +### **operator=** + +```cpp +inline UniformVectorList & operator=( + UniformVectorList && that +) +``` + + + +**Parameters**: + + * **that** another [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) + + +**Return**: reference to object + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Controller.md deleted file mode 100644 index e8f65930..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Controller.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: lds::gaussian::Controller -summary: Gaussian-observation Controller Type. - ---- - -# lds::gaussian::Controller - - - -Gaussian-observation [Controller]() Type. -<br /> `#include <lds_gaussian_ctrl.h>` - -Inherits from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/#function-set-y-ref)**(const Vector & y_ref) override<br>sets reference output | - -## Additional inherited members - -**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) by moving the system object. | -| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | -| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | -| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-sys)**() const | -| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc)**() const<br>Get state feedback controller gain. | -| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-inty)**() const<br>Get integral controller gain. | -| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-u)**() const<br>Get input feedback controller gain. | -| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-g-design)**() const<br>Get input gain used in controller design. | -| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ref)**() const<br>Get reference input. | -| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-x-ref)**() const<br>Get reference state. | -| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-y-ref)**() const<br>Get reference output. | -| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control-type)**() const<br>Get controller type. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-lb)**() const<br>Get control lower bound. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ub)**() const<br>Get control upper bound. | -| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys)<br>Set system. | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | -| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | -| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | -| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | -| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | -| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | -| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-reset)**()<br>reset system and control variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-print)**()<br>prints variables to stdout | - -**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-sys-)** <br>underlying LDS | -| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-)** <br>control signal | -| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-return-)** <br>control signal that is _returned_ to user | -| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-g-design-)** <br>input gain of the system used for controller design | -| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-)** <br>reference input | -| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-prev-)** <br>reference input at previous time step | -| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-x-ref-)** <br>reference state | -| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-y-ref-)** <br>reference output | -| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-cx-ref-)** | -| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-)** <br>state controller gain | -| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-u-)** <br>input controller gain (optional when control updates ) | -| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-inty-)** <br>integral controller gain | -| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-du-ref-)** | -| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-ref-)** | -| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-ref-)** | -| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-)** | -| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | -| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-)** <br>integrated error | -| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | -| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | -| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-control-prev-)** | -| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-lock-control-prev-)** | -| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-lb-)** <br>lower bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ub-)** <br>upper bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-tau-awu-)** <br>antiwindup time constant | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-k-awu-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | -| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-control-type-)** <br>controller type | - - ---- ---- -## Public Function Details - -### **set_y_ref** - -```cpp -inline virtual void set_y_ref( - const Vector & y_ref -) override -``` - - - -**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-y-ref) - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Fit.md deleted file mode 100644 index 5334c351..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1Fit.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: lds::gaussian::Fit -summary: GLDS Fit Type. - ---- - -# lds::gaussian::Fit - - - -GLDS [Fit]() Type. -<br /> `#include <lds_gaussian_fit.h>` - -Inherits from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-fit)**() =default | -| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-fit)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt)<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/). | -| virtual const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-r)**() const override<br>gets measurement noise covariance | -| virtual void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-set-r)**(const Matrix & R) override<br>sets measurement noise covariance | -| virtual View | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/#function-h)**(Matrix & y, const Matrix & x, size_t t) override<br>output function | - -## Additional inherited members - -**Public Functions inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/)** - -| | Name | -| -------------- | -------------- | -| virtual | **[~Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-~fit)**() =default | -| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-u)**() const<br>gets number of inputs | -| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-x)**() const<br>gets number of states | -| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-y)**() const<br>gets number of outputs | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-dt)**() const<br>gets sample period | -| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-a)**() const<br>gets state matrix | -| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-b)**() const<br>gets input matrix | -| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-g)**() const<br>gets input gain | -| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-m)**() const<br>gets process disturbance | -| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-q)**() const<br>gets process noise covariance | -| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-x0)**() const<br>gets initial state estimate | -| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-p0)**() const<br>gets covariance of initial state estimate | -| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-c)**() const<br>gets output matrix | -| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-d)**() const<br>gets output bias | -| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-a)**(const Matrix & A)<br>sets state matrix | -| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-b)**(const Matrix & B)<br>sets input matrix | -| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-g)**(const Vector & g)<br>sets input gain/conversion factor | -| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-m)**(const Vector & m)<br>sets process disturbance | -| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-q)**(const Matrix & Q)<br>sets process noise covariance | -| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-x0)**(const Vector & x0)<br>sets initial state estimate | -| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-p0)**(const Matrix & P0)<br>sets initial state estimate covariance | -| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-c)**(const Matrix & C)<br>sets output matrix | -| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-d)**(const Vector & d)<br>sets output bias | -| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-f)**(Matrix & x, const Matrix & u, size_t t)<br>system dynamics function | -| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-f)**(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)<br>system dynamics function | - -**Protected Attributes inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/)** - -| | Name | -| -------------- | -------------- | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-dt-)** <br>sample period | -| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-a-)** <br>state matrix | -| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-b-)** <br>input matrix | -| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-g-)** <br>input gain | -| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-m-)** <br>process noise mean | -| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-q-)** <br>process noise cov | -| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-c-)** <br>output matrix | -| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-d-)** <br>output bias | -| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-r-)** <br>measurement noise | -| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-x0-)** <br>initial state | -| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-p0-)** <br>initial covar | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-y-)** <br>number of outputs | - - ---- ---- -## Public Function Details - -### **Fit** - -```cpp -Fit() =default -``` - - - ---- -### **Fit** - -```cpp -Fit( - size_t n_u, - size_t n_x, - size_t n_y, - data_t dt -) -``` - - - -**Parameters**: - - * **n_u** number of inputs - * **n_x** number of states - * **n_y** number of outputs - * **dt** sample period - - ---- -### **R** - -```cpp -inline virtual const Matrix & R() const override -``` - - - -**Reimplements**: [lds::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-r) - - ---- -### **set_R** - -```cpp -inline virtual void set_R( - const Matrix & R -) override -``` - - - -**Reimplements**: [lds::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-r) - - ---- -### **h** - -```cpp -inline virtual View h( - Matrix & y, - const Matrix & x, - size_t t -) override -``` - - - -**Parameters**: - - * **y** output estimate (over time) - * **x** state estimate (over time) - * **t** time index - - -**Return**: output - -**Reimplements**: [lds::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-h) - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitEM.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitEM.md deleted file mode 100644 index 2a3d68db..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitEM.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: lds::gaussian::FitEM -summary: GLDS E-M Fit Type. - ---- - -# lds::gaussian::FitEM - - - -GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/) Type. [More...](#detailed-description) - - -<br /> `#include <lds_gaussian_fit_em.h>` - -Inherits from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/) - -## Additional inherited members - -**Public Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** - -| | Name | -| -------------- | -------------- | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**() =default<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**(size_t n_x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**(const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) & fit0, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| virtual | **[~EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-~em)**() =default | -| const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) & | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-run)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M) | -| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-returndata)**()<br>Returns the input/output data to caller. | -| const std::vector< Matrix > & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-x)**() const<br>gets estimated state (over time) | -| const std::vector< Matrix > & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-y)**() const<br>gets estimated output (over time) | -| const Matrix & | **[sum_E_x_t_x_t](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-x-t-x-t)**() const<br>gets state-input covariance | -| const Matrix & | **[sum_E_xu_tm1_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-xu-tm1-xu-tm1)**() const<br>gets state-input covariance (t-minus-1) | -| const Matrix & | **[sum_E_xu_t_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-xu-t-xu-tm1)**() const<br>gets single lag state-input covariance | -| size_t | **[n_t_tot](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-n-t-tot)**()<br>total number of time samples | -| const Vector & | **[theta](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-theta)**() const<br>gets parameters updated in M step | - -**Protected Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** - -| | Name | -| -------------- | -------------- | -| void | **[Expectation](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-expectation)**(bool force_common_initial =false)<br>Expectation step. | -| void | **[Maximization](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximization)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step. | -| void | **[MaximizeDynamics](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizedynamics)**() | -| void | **[MaximizeQ](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizeq)**() | -| void | **[MaximizeInitial](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizeinitial)**() | -| void | **[Smooth](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-smooth)**(bool force_common_initial)<br>get smoothed estimates | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-reset)**()<br>reset to initial conditions | -| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-initvars)**()<br>Initializes the variables. | -| Vector | **[UpdateTheta](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-updatetheta)**()<br>updates parameter list, theta | - -**Protected Attributes inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** - -| | Name | -| -------------- | -------------- | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-u-)** <br>input training data | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-z-)** <br>measurement training data | -| std::vector< Matrix > | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-x-)** <br>state estimate | -| std::vector< Cube > | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-p-)** <br>state estimate cov | -| std::vector< Cube > | **[P_t_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-p-t-tm1-)** <br>single-lag state covariance | -| std::vector< Matrix > | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-y-)** <br>output estimate | -| Matrix | **[diag_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-diag-y-)** | -| Matrix | **[sum_E_x_t_x_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-x-t-x-t-)** <br>state covariance (current time) | -| Matrix | **[sum_E_xu_tm1_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-xu-tm1-xu-tm1-)** <br>state-input covariance (t-minus-1) | -| Matrix | **[sum_E_xu_t_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-xu-t-xu-tm1-)** <br>single lag state-input covariance | -| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-fit-)** | -| Vector | **[theta_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-theta-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-dt-)** <br>sample period | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-y-)** <br>number of outputs | -| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-trials-)** <br>number of input/output data sequences | -| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-t-)** <br>number of time steps | -| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-t-tot-)** <br>total number of time steps across trials | - - -## Detailed Description - -```cpp -class lds::gaussian::FitEM; -``` - - - -This type is used in the process of fitting GLDS models by expectation-maximization ([EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)). - ---- ---- -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitSSID.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitSSID.md deleted file mode 100644 index 026a136c..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1FitSSID.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: lds::gaussian::FitSSID -summary: Subspace Identification (SSID) for GLDS. - ---- - -# lds::gaussian::FitSSID - - - -Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for GLDS. -<br /> `#include <lds_gaussian_fit_ssid.h>` - -Inherits from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/) - -## Additional inherited members - -**Public Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** - -| | Name | -| -------------- | -------------- | -| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-ssid)**() =default<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) type. | -| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-ssid)**(size_t n_x, size_t n_h, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train, const Vector & d =Vector(1).fill(-kInf))<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) type. | -| std::tuple< [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/), Vector > | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-run)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) ssid_wt)<br>Runs fitting by subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) | -| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-returndata)**()<br>Returns the I/O data to caller. | - -**Protected Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** - -| | Name | -| -------------- | -------------- | -| void | **[CalcD](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-calcd)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) t_silence =0.1, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d) | -| void | **[CreateHankelDataMat](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-createhankeldatamat)**()<br>Creates the block-hankel I/O data matrix. | -| void | **[CalcSVD](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-calcsvd)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) wt)<br>performs the singular value decomposition (SVD) | -| void | **[Solve](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-solve)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) wt_dc)<br>solves for LDS parameters | -| void | **[RecomputeExtObs](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-recomputeextobs)**()<br>recompute extended observability matrix from estimates of A, C | - -**Protected Attributes inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** - -| | Name | -| -------------- | -------------- | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-u-)** <br>input training data | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-z-)** <br>measurement training data | -| Matrix | **[D_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-d-)** <br>block-Hankel I/O data matrix | -| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-fit-)** <br>fit | -| Matrix | **[g_dc_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-g-dc-)** <br>I/O gain @ DC. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-dt-)** <br>sample period | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-y-)** <br>number of outputs | -| size_t | **[n_h_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-h-)** | -| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-trials-)** <br>number of input/output data sequences | -| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-t-)** <br>number of time steps | -| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-t-tot-)** <br>total number of time steps across trials | -| Matrix | **[L_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-l-)** <br>lower triangle decomp of covariance matrix | -| Vector | **[s_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-s-)** <br>singular values | -| Matrix | **[ext_obs_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-ext-obs-t-)** <br>extended observability matrix | - - ---- ---- -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1SwitchedController.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1SwitchedController.md deleted file mode 100644 index bddd9804..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1SwitchedController.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: lds::gaussian::SwitchedController -summary: Gaussian-observation SwitchedController Type. - ---- - -# lds::gaussian::SwitchedController - - - -Gaussian-observation [SwitchedController]() Type. -<br /> `#include <lds_gaussian_sctrl.h>` - -Inherits from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/), [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/#function-set-y-ref)**(const Vector & y_ref) override<br>sets reference output | - -## Additional inherited members - -**Public Functions inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/)** - -| | Name | -| -------------- | -------------- | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**() =default<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/). | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > & systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/). | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > && systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/) (moves systems). | -| void | **[Switch](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switch)**(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller. | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc)<br>sets state feedback gains | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc)<br>sets state feedback gains (moving) | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-inty)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc_inty)<br>sets integral feedback gains | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-inty)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc_inty)<br>sets integral feedback gains (moving) | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-u)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc_u)<br>sets input feedback gains | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-u)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc_u)<br>sets input feedback gains (moving) | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-g-design)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) & g)<br>sets input gain used during controller design | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-g-design)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) && g)<br>sets input gain used during controller design (moving) | - -**Protected Attributes inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/)** - -| | Name | -| -------------- | -------------- | -| std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > | **[systems_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-systems-)** <br>underlying sub-systems which are switched between | -| size_t | **[n_sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-n-sys-)** <br>number of systems | -| size_t | **[idx_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-idx-)** <br>current system/controller index. | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-list-)** | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_inty_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-inty-list-)** | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_u_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-u-list-)** | -| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) | **[g_design_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-g-design-list-)** | - -**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) by moving the system object. | -| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | -| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | -| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-sys)**() const | -| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc)**() const<br>Get state feedback controller gain. | -| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-inty)**() const<br>Get integral controller gain. | -| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-u)**() const<br>Get input feedback controller gain. | -| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-g-design)**() const<br>Get input gain used in controller design. | -| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ref)**() const<br>Get reference input. | -| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-x-ref)**() const<br>Get reference state. | -| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-y-ref)**() const<br>Get reference output. | -| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control-type)**() const<br>Get controller type. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-lb)**() const<br>Get control lower bound. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ub)**() const<br>Get control upper bound. | -| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys)<br>Set system. | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | -| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | -| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | -| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | -| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | -| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | -| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-reset)**()<br>reset system and control variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-print)**()<br>prints variables to stdout | - -**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-sys-)** <br>underlying LDS | -| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-)** <br>control signal | -| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-return-)** <br>control signal that is _returned_ to user | -| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-g-design-)** <br>input gain of the system used for controller design | -| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-)** <br>reference input | -| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-prev-)** <br>reference input at previous time step | -| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-x-ref-)** <br>reference state | -| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-y-ref-)** <br>reference output | -| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-cx-ref-)** | -| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-)** <br>state controller gain | -| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-u-)** <br>input controller gain (optional when control updates ) | -| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-inty-)** <br>integral controller gain | -| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-du-ref-)** | -| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-ref-)** | -| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-ref-)** | -| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-)** | -| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | -| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-)** <br>integrated error | -| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | -| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | -| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-control-prev-)** | -| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-lock-control-prev-)** | -| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-lb-)** <br>lower bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ub-)** <br>upper bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-tau-awu-)** <br>antiwindup time constant | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-k-awu-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | -| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-control-type-)** <br>controller type | - - ---- ---- -## Public Function Details - -### **set_y_ref** - -```cpp -inline virtual void set_y_ref( - const Vector & y_ref -) override -``` - - - -**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-y-ref) - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1System.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1System.md deleted file mode 100644 index 79fe52f8..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1System.md +++ /dev/null @@ -1,312 +0,0 @@ ---- -title: lds::gaussian::System -summary: Gaussian LDS Type. - ---- - -# lds::gaussian::System - - - -Gaussian LDS Type. -<br /> `#include <lds_gaussian_sys.h>` - -Inherits from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-system)**() =default<br>Constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/). | -| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-system)**(std::size_t n_u, std::size_t n_x, std::size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) r0 =kDefaultR0)<br>Constructs a new Gaussian [System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/). | -| virtual const Vector & | **[Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-simulate)**(const Vector & u_tm1) override<br>Simulate system measurement. | -| const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-r)**() const<br>Get output noise covariance. | -| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-set-q)**(const Matrix & Q) | -| void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-set-r)**(const Matrix & R)<br>Set output noise covariance. | -| void | **[set_Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-set-ke)**(const Matrix & Ke)<br>Set estimator gain. | -| void | **[set_Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-set-ke-m)**(const Matrix & Ke_m)<br>Set disturbance estimator gain. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-print)**()<br>Print system variables to stdout. | - -## Protected Functions - -| | Name | -| -------------- | -------------- | -| virtual void | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-h)**() override<br>[System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/) output function. | -| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#function-recurseke)**() override<br>Recursively update estimator gain. | - -## Protected Attributes - -| | Name | -| -------------- | -------------- | -| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#variable-r-)** <br>covariance of output noise | -| bool | **[do_recurse_Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/#variable-do-recurse-ke-)** <br>whether to recursively calculate estimator gain | - -## Additional inherited members - -**Public Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| virtual | **[~System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-~system)**() | -| void | **[Filter](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-filter)**(const Vector & u_tm1, const Vector & z)<br>Filter data to produce causal state estimates. | -| void | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-f)**(const Vector & u, bool do_add_noise =false)<br>system dynamics function | -| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-u)**() const<br>Get number of inputs. | -| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-x)**() const<br>Get number of states. | -| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-y)**() const<br>Get number of outputs. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-dt)**() const<br>Get sample period. | -| const Vector & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-x)**() const<br>Get current state. | -| const Matrix & | **[P](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p)**() const<br>Get covariance of state estimate. | -| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-m)**() const<br>Get current process disturbance/bias. | -| const Matrix & | **[P_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p-m)**() const<br>Get covariance of process disturbance estimate. | -| const Vector & | **[cx](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-cx)**() const<br>Get C*x. | -| const Vector & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-y)**() const<br>Get output. | -| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-x0)**() const<br>Get initial state. | -| const Vector & | **[m0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-m0)**() const<br>Get initial disturbance. | -| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-a)**() const<br>Get state matrix. | -| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-b)**() const<br>Get input matrix. | -| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-g)**() const<br>Get input gain/conversion factor. | -| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-c)**() const<br>Get output matrix. | -| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-d)**() const<br>Get output bias. | -| const Matrix & | **[Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-ke)**() const<br>Get estimator gain. | -| const Matrix & | **[Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-ke-m)**() const<br>Get estimator gain for process disturbance (m) | -| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-q)**()<br>Get process noise covariance. | -| const Matrix & | **[Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-q-m)**()<br>Get process noise covariance of disturbance evoluation. | -| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p0)**()<br>Get covariance of initial state. | -| const Matrix & | **[P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p0-m)**()<br>Get covariance of initial process disturbance. | -| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-a)**(const Matrix & A)<br>Set state matrix. | -| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-b)**(const Matrix & B)<br>Set input matrix. | -| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-m)**(const Vector & m, bool do_force_assign =false)<br>Set process disturbance. | -| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-g)**(const Vector & g)<br>Set input gain. | -| void | **[set_Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-q-m)**(const Matrix & Q_m)<br>Set process noise covariance of disturbance evoluation. | -| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-x0)**(const Vector & x0)<br>Set initial state. | -| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-p0)**(const Matrix & P0)<br>Set covariance of initial state. | -| void | **[set_P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-p0-m)**(const Matrix & P0_m)<br>Set covariance of initial process disturbance. | -| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-c)**(const Matrix & C)<br>Set output matrix. | -| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-d)**(const Vector & d)<br>Set output bias. | -| void | **[set_x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-x)**(const Vector & x)<br>Set state of system. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-reset)**()<br>Reset system variables. | - -**Protected Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-initvars)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0) | - -**Public Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| bool | **[do_adapt_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-do-adapt-m)** <br>whether to adaptively estimate disturbance m | - -**Protected Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| std::size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-x-)** <br>number of states | -| std::size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-u-)** <br>number of inputs | -| std::size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-y-)** <br>number of outputs | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-dt-)** <br>sample period | -| Vector | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-x-)** <br>state | -| Matrix | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p-)** <br>covariance of state estimate | -| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-m-)** <br>process disturbance | -| Matrix | **[P_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p-m-)** <br>covariance of disturbance estimate | -| Vector | **[cx_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-cx-)** <br>C*x. | -| Vector | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-y-)** <br>output | -| Vector | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-z-)** <br>measurement | -| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-x0-)** <br>initial state | -| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p0-)** <br>covariance of initial state estimate | -| Vector | **[m0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-m0-)** <br>initial process disturbance | -| Matrix | **[P0_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p0-m-)** <br>covariance of initial disturbance est. | -| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-a-)** <br>state matrix | -| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-b-)** <br>input matrix | -| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-g-)** <br>input gain | -| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-q-)** <br>covariance of process noise | -| Matrix | **[Q_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-q-m-)** <br>covariance of disturbance random walk | -| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-c-)** <br>output matrix | -| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-d-)** <br>output bias | -| Matrix | **[Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-ke-)** <br>estimator gain | -| Matrix | **[Ke_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-ke-m-)** <br>estimator gain for process disturbance | - - ---- ---- -## Public Function Details - -### **System** - -```cpp -System() =default -``` - - - ---- -### **System** - -```cpp -System( - std::size_t n_u, - std::size_t n_x, - std::size_t n_y, - data_t dt, - data_t p0 =kDefaultP0, - data_t q0 =kDefaultQ0, - data_t r0 =kDefaultR0 -) -``` - - - -**Parameters**: - - * **n_u** number of inputs (u) - * **n_x** number of states (x) - * **n_y** number of outputs (y) - * **dt** sample period - * **p0** [optional] initial diagonal elements of state estimate covariance (P) - * **q0** [optional] initial diagonal elements of process noise covariance (Q) - * **r0** [optional] initial diagonal elements of output noise covariance (R) - - ---- -### **Simulate** - -```cpp -virtual const Vector & Simulate( - const Vector & u_tm1 -) override -``` - - - -**Parameters**: - - * **u_tm1** input at t-1 - - -**Return**: z measurement - -**Reimplements**: [lds::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-simulate) - - -Simulate system and produce measurement - - ---- -### **R** - -```cpp -inline const Matrix & R() const -``` - - - ---- -### **set_Q** - -```cpp -inline void set_Q( - const Matrix & Q -) -``` - - - ---- -### **set_R** - -```cpp -inline void set_R( - const Matrix & R -) -``` - - - ---- -### **set_Ke** - -```cpp -inline void set_Ke( - const Matrix & Ke -) -``` - - - ---- -### **set_Ke_m** - -```cpp -inline void set_Ke_m( - const Matrix & Ke_m -) -``` - - - ---- -### **Print** - -```cpp -void Print() -``` - - - ---- - - -## Protected Function Details - -### **h** - -```cpp -inline virtual void h() override -``` - - - -**Reimplements**: [lds::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-h) - - ---- -### **RecurseKe** - -```cpp -virtual void RecurseKe() override -``` - - - -**Reimplements**: [lds::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-recurseke) - - ---- - - -## Protected Attribute Details - -### **R_** - -```cpp -Matrix R_; -``` - - - ---- -### **do_recurse_Ke_** - -```cpp -bool do_recurse_Ke_ {}; -``` - - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md new file mode 100644 index 00000000..8650210a --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md @@ -0,0 +1,116 @@ +--- +title: lds::gaussian::Controller +summary: Gaussian-observation Controller Type. + +--- + +# lds::gaussian::Controller + + + +Gaussian-observation [Controller]() Type. +<br /> `#include <lds_gaussian_ctrl.h>` + +Inherits from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/#function-set-y-ref)**(const Vector & y_ref) override<br>sets reference output | + +## Additional inherited members + +**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) by moving the system object. | +| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | +| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | +| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys)**() const | +| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc)**() const<br>Get state feedback controller gain. | +| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty)**() const<br>Get integral controller gain. | +| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u)**() const<br>Get input feedback controller gain. | +| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design)**() const<br>Get input gain used in controller design. | +| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref)**() const<br>Get reference input. | +| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref)**() const<br>Get reference state. | +| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref)**() const<br>Get reference output. | +| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type)**() const<br>Get controller type. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb)**() const<br>Get control lower bound. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub)**() const<br>Get control upper bound. | +| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys)<br>Set system. | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | +| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | +| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | +| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | +| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | +| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | +| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset)**()<br>reset system and control variables. | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print)**()<br>prints variables to stdout | + +**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-)** <br>underlying LDS | +| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-)** <br>control signal | +| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-)** <br>control signal that is _returned_ to user | +| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-)** <br>input gain of the system used for controller design | +| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-)** <br>reference input | +| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-)** <br>reference input at previous time step | +| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-)** <br>reference state | +| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-)** <br>reference output | +| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-)** | +| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-)** <br>state controller gain | +| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-)** <br>input controller gain (optional when control updates \deltaU) | +| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-)** <br>integral controller gain | +| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-)** | +| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-)** | +| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-)** | +| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-)** | +| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | +| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-)** <br>integrated error | +| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | +| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | +| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-)** | +| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-)** | +| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-)** <br>lower bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-)** <br>upper bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-)** <br>antiwindup time constant | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | +| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-)** <br>controller type | + + +--- +--- +## Public Function Details + +### **set_y_ref** + +```cpp +inline virtual void set_y_ref( + const Vector & y_ref +) override +``` + + + +**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref) + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md new file mode 100644 index 00000000..e7a6c3fb --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md @@ -0,0 +1,168 @@ +--- +title: lds::gaussian::Fit +summary: GLDS Fit Type. + +--- + +# lds::gaussian::Fit + + + +GLDS [Fit]() Type. +<br /> `#include <lds_gaussian_fit.h>` + +Inherits from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-fit)**() =default | +| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-fit)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt)<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/). | +| virtual const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-r)**() const override<br>gets measurement noise covariance | +| virtual void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-set-r)**(const Matrix & R) override<br>sets measurement noise covariance | +| virtual View | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-h)**(Matrix & y, const Matrix & x, size_t t) override<br>output function | + +## Additional inherited members + +**Public Functions inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/)** + +| | Name | +| -------------- | -------------- | +| virtual | **[~Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-~fit)**() =default | +| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-u)**() const<br>gets number of inputs | +| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-x)**() const<br>gets number of states | +| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-y)**() const<br>gets number of outputs | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-dt)**() const<br>gets sample period | +| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-a)**() const<br>gets state matrix | +| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-b)**() const<br>gets input matrix | +| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-g)**() const<br>gets input gain | +| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-m)**() const<br>gets process disturbance | +| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-q)**() const<br>gets process noise covariance | +| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-x0)**() const<br>gets initial state estimate | +| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-p0)**() const<br>gets covariance of initial state estimate | +| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-c)**() const<br>gets output matrix | +| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-d)**() const<br>gets output bias | +| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-a)**(const Matrix & A)<br>sets state matrix | +| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-b)**(const Matrix & B)<br>sets input matrix | +| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-g)**(const Vector & g)<br>sets input gain/conversion factor | +| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-m)**(const Vector & m)<br>sets process disturbance | +| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-q)**(const Matrix & Q)<br>sets process noise covariance | +| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-x0)**(const Vector & x0)<br>sets initial state estimate | +| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-p0)**(const Matrix & P0)<br>sets initial state estimate covariance | +| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-c)**(const Matrix & C)<br>sets output matrix | +| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-d)**(const Vector & d)<br>sets output bias | +| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f)**(Matrix & x, const Matrix & u, size_t t)<br>system dynamics function | +| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f)**(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)<br>system dynamics function | + +**Protected Attributes inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/)** + +| | Name | +| -------------- | -------------- | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-dt-)** <br>sample period | +| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-a-)** <br>state matrix | +| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-b-)** <br>input matrix | +| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-g-)** <br>input gain | +| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-m-)** <br>process noise mean | +| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-q-)** <br>process noise cov | +| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-c-)** <br>output matrix | +| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-d-)** <br>output bias | +| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-r-)** <br>measurement noise | +| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-x0-)** <br>initial state | +| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-p0-)** <br>initial covar | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-y-)** <br>number of outputs | + + +--- +--- +## Public Function Details + +### **Fit** + +```cpp +Fit() =default +``` + + + +--- +### **Fit** + +```cpp +Fit( + size_t n_u, + size_t n_x, + size_t n_y, + data_t dt +) +``` + + + +**Parameters**: + + * **n_u** number of inputs + * **n_x** number of states + * **n_y** number of outputs + * **dt** sample period + + +--- +### **R** + +```cpp +inline virtual const Matrix & R() const override +``` + + + +**Reimplements**: [lds::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-r) + + +--- +### **set_R** + +```cpp +inline virtual void set_R( + const Matrix & R +) override +``` + + + +**Reimplements**: [lds::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-r) + + +--- +### **h** + +```cpp +inline virtual View h( + Matrix & y, + const Matrix & x, + size_t t +) override +``` + + + +**Parameters**: + + * **y** output estimate (over time) + * **x** state estimate (over time) + * **t** time index + + +**Return**: output + +**Reimplements**: [lds::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-h) + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md new file mode 100644 index 00000000..73c6f4b0 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md @@ -0,0 +1,96 @@ +--- +title: lds::gaussian::FitEM +summary: GLDS E-M Fit Type. + +--- + +# lds::gaussian::FitEM + + + +GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/) Type. [More...](#detailed-description) + + +<br /> `#include <lds_gaussian_fit_em.h>` + +Inherits from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/) + +## Additional inherited members + +**Public Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** + +| | Name | +| -------------- | -------------- | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**() =default<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**(size_t n_x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**(const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) & fit0, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| virtual | **[~EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em)**() =default | +| const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) & | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M) | +| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata)**()<br>Returns the input/output data to caller. | +| const std::vector< Matrix > & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x)**() const<br>gets estimated state (over time) | +| const std::vector< Matrix > & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y)**() const<br>gets estimated output (over time) | +| const Matrix & | **[sum_E_x_t_x_t](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t)**() const<br>gets state-input covariance | +| const Matrix & | **[sum_E_xu_tm1_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1)**() const<br>gets state-input covariance (t-minus-1) | +| const Matrix & | **[sum_E_xu_t_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1)**() const<br>gets single lag state-input covariance | +| size_t | **[n_t_tot](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot)**()<br>total number of time samples | +| const Vector & | **[theta](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta)**() const<br>gets parameters updated in M step | + +**Protected Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** + +| | Name | +| -------------- | -------------- | +| void | **[Expectation](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation)**(bool force_common_initial =false)<br>Expectation step. | +| void | **[Maximization](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step. | +| void | **[MaximizeDynamics](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics)**() | +| void | **[MaximizeQ](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq)**() | +| void | **[MaximizeInitial](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial)**() | +| void | **[Smooth](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth)**(bool force_common_initial)<br>get smoothed estimates | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset)**()<br>reset to initial conditions | +| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars)**()<br>Initializes the variables. | +| Vector | **[UpdateTheta](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta)**()<br>updates parameter list, theta | + +**Protected Attributes inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** + +| | Name | +| -------------- | -------------- | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-)** <br>input training data | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-)** <br>measurement training data | +| std::vector< Matrix > | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-)** <br>state estimate | +| std::vector< Cube > | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-)** <br>state estimate cov | +| std::vector< Cube > | **[P_t_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-)** <br>single-lag state covariance | +| std::vector< Matrix > | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-)** <br>output estimate | +| Matrix | **[diag_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-)** | +| Matrix | **[sum_E_x_t_x_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-)** <br>state covariance (current time) | +| Matrix | **[sum_E_xu_tm1_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-)** <br>state-input covariance (t-minus-1) | +| Matrix | **[sum_E_xu_t_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-)** <br>single lag state-input covariance | +| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-)** | +| Vector | **[theta_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-)** <br>sample period | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-)** <br>number of outputs | +| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-)** <br>number of input/output data sequences | +| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-)** <br>number of time steps | +| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-)** <br>total number of time steps across trials | + + +## Detailed Description + +```cpp +class lds::gaussian::FitEM; +``` + + + + + +``` + This type is used in the process of fitting GLDS models by + expectation-maximization (EM). +``` + +--- +--- +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md new file mode 100644 index 00000000..c14c9b7e --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md @@ -0,0 +1,63 @@ +--- +title: lds::gaussian::FitSSID +summary: Subspace Identification (SSID) for GLDS. + +--- + +# lds::gaussian::FitSSID + + + +Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for GLDS. +<br /> `#include <lds_gaussian_fit_ssid.h>` + +Inherits from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/) + +## Additional inherited members + +**Public Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** + +| | Name | +| -------------- | -------------- | +| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid)**() =default<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) type. | +| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid)**(size_t n_x, size_t n_h, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train, const Vector & d =Vector(1).fill(-kInf))<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) type. | +| std::tuple< [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/), Vector > | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) ssid_wt)<br>Runs fitting by subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) | +| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata)**()<br>Returns the I/O data to caller. | + +**Protected Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** + +| | Name | +| -------------- | -------------- | +| void | **[CalcD](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) t_silence =0.1, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d) | +| void | **[CreateHankelDataMat](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat)**()<br>Creates the block-hankel I/O data matrix. | +| void | **[CalcSVD](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) wt)<br>performs the singular value decomposition (SVD) | +| void | **[Solve](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) wt_dc)<br>solves for LDS parameters | +| void | **[RecomputeExtObs](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs)**()<br>recompute extended observability matrix from estimates of A, C | + +**Protected Attributes inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** + +| | Name | +| -------------- | -------------- | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-)** <br>input training data | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-)** <br>measurement training data | +| Matrix | **[D_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-)** <br>block-Hankel I/O data matrix | +| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-)** <br>fit | +| Matrix | **[g_dc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-)** <br>I/O gain @ DC. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-)** <br>sample period | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-)** <br>number of outputs | +| size_t | **[n_h_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-)** | +| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-)** <br>number of input/output data sequences | +| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-)** <br>number of time steps | +| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-)** <br>total number of time steps across trials | +| Matrix | **[L_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-)** <br>lower triangle decomp of covariance matrix | +| Vector | **[s_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-)** <br>singular values | +| Matrix | **[ext_obs_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-)** <br>extended observability matrix | + + +--- +--- +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md new file mode 100644 index 00000000..74faeb8e --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md @@ -0,0 +1,145 @@ +--- +title: lds::gaussian::SwitchedController +summary: Gaussian-observation SwitchedController Type. + +--- + +# lds::gaussian::SwitchedController + + + +Gaussian-observation [SwitchedController]() Type. +<br /> `#include <lds_gaussian_sctrl.h>` + +Inherits from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/), [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/#function-set-y-ref)**(const Vector & y_ref) override<br>sets reference output | + +## Additional inherited members + +**Public Functions inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/)** + +| | Name | +| -------------- | -------------- | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**() =default<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/). | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > & systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/). | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > && systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/) (moves systems). | +| void | **[Switch](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switch)**(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller. | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc)<br>sets state feedback gains | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc)<br>sets state feedback gains (moving) | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc_inty)<br>sets integral feedback gains | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc_inty)<br>sets integral feedback gains (moving) | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc_u)<br>sets input feedback gains | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc_u)<br>sets input feedback gains (moving) | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) & g)<br>sets input gain used during controller design | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) && g)<br>sets input gain used during controller design (moving) | + +**Protected Attributes inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/)** + +| | Name | +| -------------- | -------------- | +| std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > | **[systems_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-systems-)** <br>underlying sub-systems which are switched between | +| size_t | **[n_sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-n-sys-)** <br>number of systems | +| size_t | **[idx_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-idx-)** <br>current system/controller index. | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-list-)** | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_inty_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-inty-list-)** | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_u_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-u-list-)** | +| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) | **[g_design_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-g-design-list-)** | + +**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) by moving the system object. | +| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | +| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | +| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys)**() const | +| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc)**() const<br>Get state feedback controller gain. | +| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty)**() const<br>Get integral controller gain. | +| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u)**() const<br>Get input feedback controller gain. | +| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design)**() const<br>Get input gain used in controller design. | +| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref)**() const<br>Get reference input. | +| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref)**() const<br>Get reference state. | +| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref)**() const<br>Get reference output. | +| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type)**() const<br>Get controller type. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb)**() const<br>Get control lower bound. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub)**() const<br>Get control upper bound. | +| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys)<br>Set system. | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | +| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | +| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | +| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | +| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | +| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | +| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset)**()<br>reset system and control variables. | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print)**()<br>prints variables to stdout | + +**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-)** <br>underlying LDS | +| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-)** <br>control signal | +| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-)** <br>control signal that is _returned_ to user | +| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-)** <br>input gain of the system used for controller design | +| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-)** <br>reference input | +| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-)** <br>reference input at previous time step | +| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-)** <br>reference state | +| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-)** <br>reference output | +| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-)** | +| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-)** <br>state controller gain | +| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-)** <br>input controller gain (optional when control updates \deltaU) | +| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-)** <br>integral controller gain | +| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-)** | +| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-)** | +| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-)** | +| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-)** | +| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | +| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-)** <br>integrated error | +| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | +| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | +| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-)** | +| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-)** | +| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-)** <br>lower bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-)** <br>upper bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-)** <br>antiwindup time constant | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | +| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-)** <br>controller type | + + +--- +--- +## Public Function Details + +### **set_y_ref** + +```cpp +inline virtual void set_y_ref( + const Vector & y_ref +) override +``` + + + +**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref) + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md new file mode 100644 index 00000000..8cecdcfe --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md @@ -0,0 +1,328 @@ +--- +title: lds::gaussian::System +summary: Gaussian LDS Type. + +--- + +# lds::gaussian::System + + + +Gaussian LDS Type. +<br /> `#include <lds_gaussian_sys.h>` + +Inherits from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-system)**() =default<br>Constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/). | +| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-system)**(std::size_t n_u, std::size_t n_x, std::size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) r0 =kDefaultR0)<br>Constructs a new Gaussian [System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/). | +| virtual const Vector & | **[Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-simulate)**(const Vector & u_tm1) override<br>Simulate system measurement. | +| const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-r)**() const<br>Get output noise covariance. | +| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-q)**(const Matrix & Q) | +| void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-r)**(const Matrix & R)<br>Set output noise covariance. | +| void | **[set_Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-ke)**(const Matrix & Ke)<br>Set estimator gain. | +| void | **[set_Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-ke-m)**(const Matrix & Ke_m)<br>Set disturbance estimator gain. | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-print)**()<br>Print system variables to stdout. | + +## Protected Functions + +| | Name | +| -------------- | -------------- | +| virtual void | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h)**() override<br>[System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/) output function. | +| virtual Vector | **[h_](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h-)**(Vector x) override<br>[System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/) output function: stateless. | +| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-recurseke)**() override<br>Recursively update estimator gain. | + +## Protected Attributes + +| | Name | +| -------------- | -------------- | +| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#variable-r-)** <br>covariance of output noise | +| bool | **[do_recurse_Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#variable-do-recurse-ke-)** <br>whether to recursively calculate estimator gain | + +## Additional inherited members + +**Public Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| virtual | **[~System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-~system)**() | +| void | **[Filter](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-filter)**(const Vector & u_tm1, const Vector & z)<br>Filter data to produce causal state estimates. | +| void | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-f)**(const Vector & u, bool do_add_noise =false)<br>system dynamics function | +| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-u)**() const<br>Get number of inputs. | +| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-x)**() const<br>Get number of states. | +| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-y)**() const<br>Get number of outputs. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-dt)**() const<br>Get sample period. | +| const Vector & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x)**() const<br>Get current state. | +| const Matrix & | **[P](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p)**() const<br>Get covariance of state estimate. | +| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m)**() const<br>Get current process disturbance/bias. | +| const Matrix & | **[P_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p-m)**() const<br>Get covariance of process disturbance estimate. | +| const Vector & | **[cx](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-cx)**() const<br>Get C*x. | +| const Vector & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-y)**() const<br>Get output. | +| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x0)**() const<br>Get initial state. | +| const Vector & | **[m0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m0)**() const<br>Get initial disturbance. | +| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-a)**() const<br>Get state matrix. | +| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-b)**() const<br>Get input matrix. | +| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-g)**() const<br>Get input gain/conversion factor. | +| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-c)**() const<br>Get output matrix. | +| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-d)**() const<br>Get output bias. | +| const Matrix & | **[Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke)**() const<br>Get estimator gain. | +| const Matrix & | **[Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke-m)**() const<br>Get estimator gain for process disturbance (m) | +| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q)**()<br>Get process noise covariance. | +| const Matrix & | **[Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q-m)**()<br>Get process noise covariance of disturbance evoluation. | +| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0)**()<br>Get covariance of initial state. | +| const Matrix & | **[P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0-m)**()<br>Get covariance of initial process disturbance. | +| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-a)**(const Matrix & A)<br>Set state matrix. | +| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-b)**(const Matrix & B)<br>Set input matrix. | +| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-m)**(const Vector & m, bool do_force_assign =false)<br>Set process disturbance. | +| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-g)**(const Vector & g)<br>Set input gain. | +| void | **[set_Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q-m)**(const Matrix & Q_m)<br>Set process noise covariance of disturbance evoluation. | +| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x0)**(const Vector & x0)<br>Set initial state. | +| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0)**(const Matrix & P0)<br>Set covariance of initial state. | +| void | **[set_P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0-m)**(const Matrix & P0_m)<br>Set covariance of initial process disturbance. | +| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-c)**(const Matrix & C)<br>Set output matrix. | +| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-d)**(const Vector & d)<br>Set output bias. | +| void | **[set_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x)**(const Vector & x)<br>Set state of system. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-reset)**()<br>Reset system variables. | +| std::vector< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[nstep_pred_block](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-nstep-pred-block)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > u, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > z, size_t n_pred =1) | + +**Protected Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-initvars)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0) | + +**Public Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| bool | **[do_adapt_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-do-adapt-m)** <br>whether to adaptively estimate disturbance m | + +**Protected Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| std::size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-x-)** <br>number of states | +| std::size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-u-)** <br>number of inputs | +| std::size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-y-)** <br>number of outputs | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-dt-)** <br>sample period | +| Vector | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x-)** <br>state | +| Matrix | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-)** <br>covariance of state estimate | +| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m-)** <br>process disturbance | +| Matrix | **[P_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-m-)** <br>covariance of disturbance estimate | +| Vector | **[cx_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-cx-)** <br>C*x. | +| Vector | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-y-)** <br>output | +| Vector | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-z-)** <br>measurement | +| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x0-)** <br>initial state | +| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-)** <br>covariance of initial state estimate | +| Vector | **[m0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m0-)** <br>initial process disturbance | +| Matrix | **[P0_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-m-)** <br>covariance of initial disturbance est. | +| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-a-)** <br>state matrix | +| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-b-)** <br>input matrix | +| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-g-)** <br>input gain | +| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-)** <br>covariance of process noise | +| Matrix | **[Q_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-m-)** <br>covariance of disturbance random walk | +| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-c-)** <br>output matrix | +| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-d-)** <br>output bias | +| Matrix | **[Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-)** <br>estimator gain | +| Matrix | **[Ke_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-m-)** <br>estimator gain for process disturbance | + + +--- +--- +## Public Function Details + +### **System** + +```cpp +System() =default +``` + + + +--- +### **System** + +```cpp +System( + std::size_t n_u, + std::size_t n_x, + std::size_t n_y, + data_t dt, + data_t p0 =kDefaultP0, + data_t q0 =kDefaultQ0, + data_t r0 =kDefaultR0 +) +``` + + + +**Parameters**: + + * **n_u** number of inputs (u) + * **n_x** number of states (x) + * **n_y** number of outputs (y) + * **dt** sample period + * **p0** [optional] initial diagonal elements of state estimate covariance (P) + * **q0** [optional] initial diagonal elements of process noise covariance (Q) + * **r0** [optional] initial diagonal elements of output noise covariance (R) + + +--- +### **Simulate** + +```cpp +virtual const Vector & Simulate( + const Vector & u_tm1 +) override +``` + + + +**Parameters**: + + * **u_tm1** input at t-1 + + +**Return**: z measurement + +**Reimplements**: [lds::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-simulate) + + +Simulate system and produce measurement + + +--- +### **R** + +```cpp +inline const Matrix & R() const +``` + + + +--- +### **set_Q** + +```cpp +inline void set_Q( + const Matrix & Q +) +``` + + + +--- +### **set_R** + +```cpp +inline void set_R( + const Matrix & R +) +``` + + + +--- +### **set_Ke** + +```cpp +inline void set_Ke( + const Matrix & Ke +) +``` + + + +--- +### **set_Ke_m** + +```cpp +inline void set_Ke_m( + const Matrix & Ke_m +) +``` + + + +--- +### **Print** + +```cpp +void Print() +``` + + + +--- + + +## Protected Function Details + +### **h** + +```cpp +inline virtual void h() override +``` + + + +**Reimplements**: [lds::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h) + + +--- +### **h_** + +```cpp +inline virtual Vector h_( + Vector x +) override +``` + + + +**Reimplements**: [lds::System::h_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h-) + + +--- +### **RecurseKe** + +```cpp +virtual void RecurseKe() override +``` + + + +**Reimplements**: [lds::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-recurseke) + + +--- + + +## Protected Attribute Details + +### **R_** + +```cpp +Matrix R_; +``` + + + +--- +### **do_recurse_Ke_** + +```cpp +bool do_recurse_Ke_ {}; +``` + + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Controller.md deleted file mode 100644 index fa222cf4..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Controller.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: lds::poisson::Controller -summary: PLDS Controller Type. - ---- - -# lds::poisson::Controller - - - -PLDS [Controller]() Type. -<br /> `#include <lds_poisson_ctrl.h>` - -Inherits from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/#function-set-y-ref)**(const Vector & y_ref) override<br>Set reference output. | - -## Additional inherited members - -**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) by moving the system object. | -| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | -| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | -| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-sys)**() const | -| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc)**() const<br>Get state feedback controller gain. | -| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-inty)**() const<br>Get integral controller gain. | -| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-u)**() const<br>Get input feedback controller gain. | -| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-g-design)**() const<br>Get input gain used in controller design. | -| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ref)**() const<br>Get reference input. | -| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-x-ref)**() const<br>Get reference state. | -| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-y-ref)**() const<br>Get reference output. | -| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control-type)**() const<br>Get controller type. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-lb)**() const<br>Get control lower bound. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ub)**() const<br>Get control upper bound. | -| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys)<br>Set system. | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | -| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | -| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | -| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | -| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | -| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | -| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-reset)**()<br>reset system and control variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-print)**()<br>prints variables to stdout | - -**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-sys-)** <br>underlying LDS | -| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-)** <br>control signal | -| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-return-)** <br>control signal that is _returned_ to user | -| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-g-design-)** <br>input gain of the system used for controller design | -| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-)** <br>reference input | -| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-prev-)** <br>reference input at previous time step | -| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-x-ref-)** <br>reference state | -| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-y-ref-)** <br>reference output | -| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-cx-ref-)** | -| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-)** <br>state controller gain | -| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-u-)** <br>input controller gain (optional when control updates ) | -| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-inty-)** <br>integral controller gain | -| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-du-ref-)** | -| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-ref-)** | -| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-ref-)** | -| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-)** | -| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | -| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-)** <br>integrated error | -| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | -| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | -| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-control-prev-)** | -| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-lock-control-prev-)** | -| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-lb-)** <br>lower bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ub-)** <br>upper bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-tau-awu-)** <br>antiwindup time constant | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-k-awu-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | -| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-control-type-)** <br>controller type | - - ---- ---- -## Public Function Details - -### **set_y_ref** - -```cpp -inline virtual void set_y_ref( - const Vector & y_ref -) override -``` - - - -**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-y-ref) - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Fit.md deleted file mode 100644 index 3e58f45f..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1Fit.md +++ /dev/null @@ -1,168 +0,0 @@ ---- -title: lds::poisson::Fit -summary: PLDS Fit Type. - ---- - -# lds::poisson::Fit - - - -PLDS [Fit]() Type. -<br /> `#include <lds_poisson_fit.h>` - -Inherits from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-fit)**() =default | -| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-fit)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt)<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/). | -| virtual View | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-h)**(Matrix & y, const Matrix & x, size_t t) override<br>output function | -| virtual void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-set-r)**(const Matrix & R) override<br>sets output noise covariance (if any) | -| virtual const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/#function-r)**() const override | - -## Additional inherited members - -**Public Functions inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/)** - -| | Name | -| -------------- | -------------- | -| virtual | **[~Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-~fit)**() =default | -| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-u)**() const<br>gets number of inputs | -| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-x)**() const<br>gets number of states | -| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-n-y)**() const<br>gets number of outputs | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-dt)**() const<br>gets sample period | -| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-a)**() const<br>gets state matrix | -| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-b)**() const<br>gets input matrix | -| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-g)**() const<br>gets input gain | -| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-m)**() const<br>gets process disturbance | -| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-q)**() const<br>gets process noise covariance | -| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-x0)**() const<br>gets initial state estimate | -| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-p0)**() const<br>gets covariance of initial state estimate | -| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-c)**() const<br>gets output matrix | -| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-d)**() const<br>gets output bias | -| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-a)**(const Matrix & A)<br>sets state matrix | -| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-b)**(const Matrix & B)<br>sets input matrix | -| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-g)**(const Vector & g)<br>sets input gain/conversion factor | -| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-m)**(const Vector & m)<br>sets process disturbance | -| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-q)**(const Matrix & Q)<br>sets process noise covariance | -| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-x0)**(const Vector & x0)<br>sets initial state estimate | -| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-p0)**(const Matrix & P0)<br>sets initial state estimate covariance | -| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-c)**(const Matrix & C)<br>sets output matrix | -| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-d)**(const Vector & d)<br>sets output bias | -| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-f)**(Matrix & x, const Matrix & u, size_t t)<br>system dynamics function | -| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-f)**(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)<br>system dynamics function | - -**Protected Attributes inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/)** - -| | Name | -| -------------- | -------------- | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-dt-)** <br>sample period | -| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-a-)** <br>state matrix | -| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-b-)** <br>input matrix | -| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-g-)** <br>input gain | -| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-m-)** <br>process noise mean | -| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-q-)** <br>process noise cov | -| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-c-)** <br>output matrix | -| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-d-)** <br>output bias | -| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-r-)** <br>measurement noise | -| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-x0-)** <br>initial state | -| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-p0-)** <br>initial covar | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#variable-n-y-)** <br>number of outputs | - - ---- ---- -## Public Function Details - -### **Fit** - -```cpp -Fit() =default -``` - - - ---- -### **Fit** - -```cpp -inline Fit( - size_t n_u, - size_t n_x, - size_t n_y, - data_t dt -) -``` - - - -**Parameters**: - - * **n_u** number of inputs - * **n_x** number of states - * **n_y** number of outputs - * **dt** sample period - - ---- -### **h** - -```cpp -inline virtual View h( - Matrix & y, - const Matrix & x, - size_t t -) override -``` - - - -**Parameters**: - - * **y** output estimate (over time) - * **x** state estimate (over time) - * **t** time index - - -**Return**: output - -**Reimplements**: [lds::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-h) - - ---- -### **set_R** - -```cpp -inline virtual void set_R( - const Matrix & R -) override -``` - - - -**Reimplements**: [lds::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-set-r) - - ---- -### **R** - -```cpp -inline virtual const Matrix & R() const override -``` - - - -**Reimplements**: [lds::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/#function-r) - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitEM.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitEM.md deleted file mode 100644 index 14af83b9..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitEM.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: lds::poisson::FitEM -summary: PLDS E-M Fit Type. - ---- - -# lds::poisson::FitEM - - - -PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/) Type. [More...](#detailed-description) - - -<br /> `#include <lds_poisson_fit_em.h>` - -Inherits from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/) - -## Additional inherited members - -**Public Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** - -| | Name | -| -------------- | -------------- | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**() =default<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**(size_t n_x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-em)**(const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) & fit0, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)[Fit]() type. | -| virtual | **[~EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-~em)**() =default | -| const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) & | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-run)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M) | -| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-returndata)**()<br>Returns the input/output data to caller. | -| const std::vector< Matrix > & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-x)**() const<br>gets estimated state (over time) | -| const std::vector< Matrix > & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-y)**() const<br>gets estimated output (over time) | -| const Matrix & | **[sum_E_x_t_x_t](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-x-t-x-t)**() const<br>gets state-input covariance | -| const Matrix & | **[sum_E_xu_tm1_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-xu-tm1-xu-tm1)**() const<br>gets state-input covariance (t-minus-1) | -| const Matrix & | **[sum_E_xu_t_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-sum-e-xu-t-xu-tm1)**() const<br>gets single lag state-input covariance | -| size_t | **[n_t_tot](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-n-t-tot)**()<br>total number of time samples | -| const Vector & | **[theta](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-theta)**() const<br>gets parameters updated in M step | - -**Protected Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** - -| | Name | -| -------------- | -------------- | -| void | **[Expectation](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-expectation)**(bool force_common_initial =false)<br>Expectation step. | -| void | **[Maximization](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximization)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step. | -| void | **[MaximizeDynamics](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizedynamics)**() | -| void | **[MaximizeQ](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizeq)**() | -| void | **[MaximizeInitial](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-maximizeinitial)**() | -| void | **[Smooth](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-smooth)**(bool force_common_initial)<br>get smoothed estimates | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-reset)**()<br>reset to initial conditions | -| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-initvars)**()<br>Initializes the variables. | -| Vector | **[UpdateTheta](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#function-updatetheta)**()<br>updates parameter list, theta | - -**Protected Attributes inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** - -| | Name | -| -------------- | -------------- | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-u-)** <br>input training data | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-z-)** <br>measurement training data | -| std::vector< Matrix > | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-x-)** <br>state estimate | -| std::vector< Cube > | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-p-)** <br>state estimate cov | -| std::vector< Cube > | **[P_t_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-p-t-tm1-)** <br>single-lag state covariance | -| std::vector< Matrix > | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-y-)** <br>output estimate | -| Matrix | **[diag_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-diag-y-)** | -| Matrix | **[sum_E_x_t_x_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-x-t-x-t-)** <br>state covariance (current time) | -| Matrix | **[sum_E_xu_tm1_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-xu-tm1-xu-tm1-)** <br>state-input covariance (t-minus-1) | -| Matrix | **[sum_E_xu_t_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-sum-e-xu-t-xu-tm1-)** <br>single lag state-input covariance | -| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-fit-)** | -| Vector | **[theta_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-theta-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-dt-)** <br>sample period | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-y-)** <br>number of outputs | -| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-trials-)** <br>number of input/output data sequences | -| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-t-)** <br>number of time steps | -| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1em/#variable-n-t-tot-)** <br>total number of time steps across trials | - - -## Detailed Description - -```cpp -class lds::poisson::FitEM; -``` - - - -This type is used in the process of fitting PLDS models by expectation-maximization ([EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)). - ---- ---- -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitSSID.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitSSID.md deleted file mode 100644 index ab728c5c..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1FitSSID.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: lds::poisson::FitSSID -summary: Subspace Identification (SSID) for PLDS. - ---- - -# lds::poisson::FitSSID - - - -Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for PLDS. -<br /> `#include <lds_poisson_fit_ssid.h>` - -Inherits from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/) - -## Additional inherited members - -**Public Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** - -| | Name | -| -------------- | -------------- | -| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-ssid)**() =default<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) type. | -| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-ssid)**(size_t n_x, size_t n_h, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train, const Vector & d =Vector(1).fill(-kInf))<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) type. | -| std::tuple< [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/), Vector > | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-run)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) ssid_wt)<br>Runs fitting by subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) | -| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-returndata)**()<br>Returns the I/O data to caller. | - -**Protected Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** - -| | Name | -| -------------- | -------------- | -| void | **[CalcD](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-calcd)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) t_silence =0.1, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d) | -| void | **[CreateHankelDataMat](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-createhankeldatamat)**()<br>Creates the block-hankel I/O data matrix. | -| void | **[CalcSVD](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-calcsvd)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) wt)<br>performs the singular value decomposition (SVD) | -| void | **[Solve](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-solve)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) wt_dc)<br>solves for LDS parameters | -| void | **[RecomputeExtObs](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#function-recomputeextobs)**()<br>recompute extended observability matrix from estimates of A, C | - -**Protected Attributes inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** - -| | Name | -| -------------- | -------------- | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-u-)** <br>input training data | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-z-)** <br>measurement training data | -| Matrix | **[D_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-d-)** <br>block-Hankel I/O data matrix | -| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-fit-)** <br>fit | -| Matrix | **[g_dc_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-g-dc-)** <br>I/O gain @ DC. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-dt-)** <br>sample period | -| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-u-)** <br>number of inputs | -| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-x-)** <br>number of states | -| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-y-)** <br>number of outputs | -| size_t | **[n_h_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-h-)** | -| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-trials-)** <br>number of input/output data sequences | -| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-t-)** <br>number of time steps | -| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-n-t-tot-)** <br>total number of time steps across trials | -| Matrix | **[L_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-l-)** <br>lower triangle decomp of covariance matrix | -| Vector | **[s_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-s-)** <br>singular values | -| Matrix | **[ext_obs_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/#variable-ext-obs-t-)** <br>extended observability matrix | - - ---- ---- -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1SwitchedController.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1SwitchedController.md deleted file mode 100644 index a5c7f33c..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1SwitchedController.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -title: lds::poisson::SwitchedController -summary: Poisson-observation SwitchedController Type. - ---- - -# lds::poisson::SwitchedController - - - -Poisson-observation [SwitchedController]() Type. -<br /> `#include <lds_poisson_sctrl.h>` - -Inherits from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/), [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/#function-set-y-ref)**(const Vector & y_ref) override<br>Set reference output. | - -## Additional inherited members - -**Public Functions inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/)** - -| | Name | -| -------------- | -------------- | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**() =default<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/). | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > & systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/). | -| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switchedcontroller)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > && systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/) (moves systems). | -| void | **[Switch](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-switch)**(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller. | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc)<br>sets state feedback gains | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc)<br>sets state feedback gains (moving) | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-inty)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc_inty)<br>sets integral feedback gains | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-inty)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc_inty)<br>sets integral feedback gains (moving) | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-u)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> & Kc_u)<br>sets input feedback gains | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-kc-u)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)<> && Kc_u)<br>sets input feedback gains (moving) | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-g-design)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) & g)<br>sets input gain used during controller design | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#function-set-g-design)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) && g)<br>sets input gain used during controller design (moving) | - -**Protected Attributes inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/)** - -| | Name | -| -------------- | -------------- | -| std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) > | **[systems_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-systems-)** <br>underlying sub-systems which are switched between | -| size_t | **[n_sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-n-sys-)** <br>number of systems | -| size_t | **[idx_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-idx-)** <br>current system/controller index. | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-list-)** | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_inty_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-inty-list-)** | -| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/) | **[Kc_u_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-kc-u-list-)** | -| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/) | **[g_design_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/#variable-g-design-list-)** | - -**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/). | -| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/) by moving the system object. | -| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | -| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | -| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-sys)**() const | -| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc)**() const<br>Get state feedback controller gain. | -| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-inty)**() const<br>Get integral controller gain. | -| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-kc-u)**() const<br>Get input feedback controller gain. | -| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-g-design)**() const<br>Get input gain used in controller design. | -| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ref)**() const<br>Get reference input. | -| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-x-ref)**() const<br>Get reference state. | -| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-y-ref)**() const<br>Get reference output. | -| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-control-type)**() const<br>Get controller type. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-lb)**() const<br>Get control lower bound. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-u-ub)**() const<br>Get control upper bound. | -| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) & sys)<br>Set system. | -| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | -| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | -| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | -| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | -| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | -| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | -| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | -| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | -| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | -| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-reset)**()<br>reset system and control variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-print)**()<br>prints variables to stdout | - -**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** - -| | Name | -| -------------- | -------------- | -| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-sys-)** <br>underlying LDS | -| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-)** <br>control signal | -| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-return-)** <br>control signal that is _returned_ to user | -| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-g-design-)** <br>input gain of the system used for controller design | -| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-)** <br>reference input | -| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ref-prev-)** <br>reference input at previous time step | -| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-x-ref-)** <br>reference state | -| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-y-ref-)** <br>reference output | -| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-cx-ref-)** | -| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-)** <br>state controller gain | -| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-u-)** <br>input controller gain (optional when control updates ) | -| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-kc-inty-)** <br>integral controller gain | -| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-du-ref-)** | -| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-ref-)** | -| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-ref-)** | -| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-dv-)** | -| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | -| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-)** <br>integrated error | -| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | -| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | -| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-control-prev-)** | -| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-do-lock-control-prev-)** | -| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-lb-)** <br>lower bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-u-ub-)** <br>upper bound on control | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-tau-awu-)** <br>antiwindup time constant | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-k-awu-)** | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | -| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#variable-control-type-)** <br>controller type | - - ---- ---- -## Public Function Details - -### **set_y_ref** - -```cpp -inline virtual void set_y_ref( - const Vector & y_ref -) override -``` - - - -**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/#function-set-y-ref) - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1System.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1System.md deleted file mode 100644 index dd54d6b2..00000000 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1System.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -title: lds::poisson::System -summary: Poisson System type. - ---- - -# lds::poisson::System - - - -Poisson [System]() type. -<br /> `#include <lds_poisson_sys.h>` - -Inherits from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/) - -## Public Functions - -| | Name | -| -------------- | -------------- | -| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-system)**() =default<br>Constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/). | -| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-system)**(std::size_t n_u, std::size_t n_x, std::size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0)<br>Constructs a new Poisson [System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/). | -| virtual const Vector & | **[Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-simulate)**(const Vector & u_tm1) override<br>Simulate system measurement. | - -## Protected Functions - -| | Name | -| -------------- | -------------- | -| virtual void | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-h)**() override<br>[System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/) output function. | -| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/#function-recurseke)**() override<br>Recursively recalculate estimator gain (Ke) | - -## Additional inherited members - -**Public Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| virtual | **[~System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-~system)**() | -| void | **[Filter](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-filter)**(const Vector & u_tm1, const Vector & z)<br>Filter data to produce causal state estimates. | -| void | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-f)**(const Vector & u, bool do_add_noise =false)<br>system dynamics function | -| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-u)**() const<br>Get number of inputs. | -| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-x)**() const<br>Get number of states. | -| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-n-y)**() const<br>Get number of outputs. | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-dt)**() const<br>Get sample period. | -| const Vector & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-x)**() const<br>Get current state. | -| const Matrix & | **[P](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p)**() const<br>Get covariance of state estimate. | -| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-m)**() const<br>Get current process disturbance/bias. | -| const Matrix & | **[P_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p-m)**() const<br>Get covariance of process disturbance estimate. | -| const Vector & | **[cx](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-cx)**() const<br>Get C*x. | -| const Vector & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-y)**() const<br>Get output. | -| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-x0)**() const<br>Get initial state. | -| const Vector & | **[m0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-m0)**() const<br>Get initial disturbance. | -| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-a)**() const<br>Get state matrix. | -| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-b)**() const<br>Get input matrix. | -| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-g)**() const<br>Get input gain/conversion factor. | -| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-c)**() const<br>Get output matrix. | -| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-d)**() const<br>Get output bias. | -| const Matrix & | **[Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-ke)**() const<br>Get estimator gain. | -| const Matrix & | **[Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-ke-m)**() const<br>Get estimator gain for process disturbance (m) | -| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-q)**()<br>Get process noise covariance. | -| const Matrix & | **[Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-q-m)**()<br>Get process noise covariance of disturbance evoluation. | -| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p0)**()<br>Get covariance of initial state. | -| const Matrix & | **[P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-p0-m)**()<br>Get covariance of initial process disturbance. | -| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-a)**(const Matrix & A)<br>Set state matrix. | -| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-b)**(const Matrix & B)<br>Set input matrix. | -| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-m)**(const Vector & m, bool do_force_assign =false)<br>Set process disturbance. | -| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-g)**(const Vector & g)<br>Set input gain. | -| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-q)**(const Matrix & Q)<br>Set process noise covariance. | -| void | **[set_Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-q-m)**(const Matrix & Q_m)<br>Set process noise covariance of disturbance evoluation. | -| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-x0)**(const Vector & x0)<br>Set initial state. | -| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-p0)**(const Matrix & P0)<br>Set covariance of initial state. | -| void | **[set_P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-p0-m)**(const Matrix & P0_m)<br>Set covariance of initial process disturbance. | -| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-c)**(const Matrix & C)<br>Set output matrix. | -| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-d)**(const Vector & d)<br>Set output bias. | -| void | **[set_x](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-set-x)**(const Vector & x)<br>Set state of system. | -| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-reset)**()<br>Reset system variables. | -| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-print)**()<br>Print system variables to stdout. | - -**Protected Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-initvars)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0) | - -**Public Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| bool | **[do_adapt_m](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-do-adapt-m)** <br>whether to adaptively estimate disturbance m | - -**Protected Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** - -| | Name | -| -------------- | -------------- | -| std::size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-x-)** <br>number of states | -| std::size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-u-)** <br>number of inputs | -| std::size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-n-y-)** <br>number of outputs | -| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-dt-)** <br>sample period | -| Vector | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-x-)** <br>state | -| Matrix | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p-)** <br>covariance of state estimate | -| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-m-)** <br>process disturbance | -| Matrix | **[P_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p-m-)** <br>covariance of disturbance estimate | -| Vector | **[cx_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-cx-)** <br>C*x. | -| Vector | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-y-)** <br>output | -| Vector | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-z-)** <br>measurement | -| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-x0-)** <br>initial state | -| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p0-)** <br>covariance of initial state estimate | -| Vector | **[m0_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-m0-)** <br>initial process disturbance | -| Matrix | **[P0_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-p0-m-)** <br>covariance of initial disturbance est. | -| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-a-)** <br>state matrix | -| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-b-)** <br>input matrix | -| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-g-)** <br>input gain | -| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-q-)** <br>covariance of process noise | -| Matrix | **[Q_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-q-m-)** <br>covariance of disturbance random walk | -| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-c-)** <br>output matrix | -| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-d-)** <br>output bias | -| Matrix | **[Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-ke-)** <br>estimator gain | -| Matrix | **[Ke_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#variable-ke-m-)** <br>estimator gain for process disturbance | - - ---- ---- -## Public Function Details - -### **System** - -```cpp -System() =default -``` - - - ---- -### **System** - -```cpp -System( - std::size_t n_u, - std::size_t n_x, - std::size_t n_y, - data_t dt, - data_t p0 =kDefaultP0, - data_t q0 =kDefaultQ0 -) -``` - - - -**Parameters**: - - * **n_u** number of inputs - * **n_x** number of states - * **n_y** number of outputs - * **dt** sample period - * **p0** [optional] initial diagonal elements of state estimate covariance (P) - * **q0** [optional] initial diagonal elements of process noise covariance (Q) - - ---- -### **Simulate** - -```cpp -virtual const Vector & Simulate( - const Vector & u_tm1 -) override -``` - - - -**Parameters**: - - * **u_tm1** input at t-1 - - -**Return**: z measurement - -**Reimplements**: [lds::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-simulate) - - -Simulate system and produce measurement - - ---- - - -## Protected Function Details - -### **h** - -```cpp -inline virtual void h() override -``` - - - -**Reimplements**: [lds::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-h) - - ---- -### **RecurseKe** - -```cpp -virtual void RecurseKe() override -``` - - - -**Reimplements**: [lds::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1system/#function-recurseke) - - -Recursively recalculate estimator gain (Ke). - -References: - -Smith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15. - -Eden UT, ..., Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16. - - ---- - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md new file mode 100644 index 00000000..ddf5c236 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md @@ -0,0 +1,116 @@ +--- +title: lds::poisson::Controller +summary: PLDS Controller Type. + +--- + +# lds::poisson::Controller + + + +PLDS [Controller]() Type. +<br /> `#include <lds_poisson_ctrl.h>` + +Inherits from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/#function-set-y-ref)**(const Vector & y_ref) override<br>Set reference output. | + +## Additional inherited members + +**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) by moving the system object. | +| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | +| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | +| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys)**() const | +| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc)**() const<br>Get state feedback controller gain. | +| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty)**() const<br>Get integral controller gain. | +| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u)**() const<br>Get input feedback controller gain. | +| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design)**() const<br>Get input gain used in controller design. | +| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref)**() const<br>Get reference input. | +| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref)**() const<br>Get reference state. | +| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref)**() const<br>Get reference output. | +| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type)**() const<br>Get controller type. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb)**() const<br>Get control lower bound. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub)**() const<br>Get control upper bound. | +| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys)<br>Set system. | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | +| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | +| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | +| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | +| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | +| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | +| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset)**()<br>reset system and control variables. | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print)**()<br>prints variables to stdout | + +**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-)** <br>underlying LDS | +| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-)** <br>control signal | +| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-)** <br>control signal that is _returned_ to user | +| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-)** <br>input gain of the system used for controller design | +| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-)** <br>reference input | +| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-)** <br>reference input at previous time step | +| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-)** <br>reference state | +| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-)** <br>reference output | +| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-)** | +| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-)** <br>state controller gain | +| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-)** <br>input controller gain (optional when control updates \deltaU) | +| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-)** <br>integral controller gain | +| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-)** | +| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-)** | +| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-)** | +| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-)** | +| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | +| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-)** <br>integrated error | +| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | +| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | +| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-)** | +| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-)** | +| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-)** <br>lower bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-)** <br>upper bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-)** <br>antiwindup time constant | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | +| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-)** <br>controller type | + + +--- +--- +## Public Function Details + +### **set_y_ref** + +```cpp +inline virtual void set_y_ref( + const Vector & y_ref +) override +``` + + + +**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref) + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md new file mode 100644 index 00000000..b875bbeb --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md @@ -0,0 +1,168 @@ +--- +title: lds::poisson::Fit +summary: PLDS Fit Type. + +--- + +# lds::poisson::Fit + + + +PLDS [Fit]() Type. +<br /> `#include <lds_poisson_fit.h>` + +Inherits from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-fit)**() =default | +| | **[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-fit)**(size_t n_u, size_t n_x, size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt)<br>Constructs a new [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/). | +| virtual View | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-h)**(Matrix & y, const Matrix & x, size_t t) override<br>output function | +| virtual void | **[set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-set-r)**(const Matrix & R) override<br>sets output noise covariance (if any) | +| virtual const Matrix & | **[R](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-r)**() const override | + +## Additional inherited members + +**Public Functions inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/)** + +| | Name | +| -------------- | -------------- | +| virtual | **[~Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-~fit)**() =default | +| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-u)**() const<br>gets number of inputs | +| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-x)**() const<br>gets number of states | +| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-y)**() const<br>gets number of outputs | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-dt)**() const<br>gets sample period | +| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-a)**() const<br>gets state matrix | +| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-b)**() const<br>gets input matrix | +| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-g)**() const<br>gets input gain | +| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-m)**() const<br>gets process disturbance | +| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-q)**() const<br>gets process noise covariance | +| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-x0)**() const<br>gets initial state estimate | +| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-p0)**() const<br>gets covariance of initial state estimate | +| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-c)**() const<br>gets output matrix | +| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-d)**() const<br>gets output bias | +| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-a)**(const Matrix & A)<br>sets state matrix | +| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-b)**(const Matrix & B)<br>sets input matrix | +| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-g)**(const Vector & g)<br>sets input gain/conversion factor | +| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-m)**(const Vector & m)<br>sets process disturbance | +| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-q)**(const Matrix & Q)<br>sets process noise covariance | +| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-x0)**(const Vector & x0)<br>sets initial state estimate | +| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-p0)**(const Matrix & P0)<br>sets initial state estimate covariance | +| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-c)**(const Matrix & C)<br>sets output matrix | +| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-d)**(const Vector & d)<br>sets output bias | +| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f)**(Matrix & x, const Matrix & u, size_t t)<br>system dynamics function | +| View | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f)**(Matrix & x_pre, const Matrix & x_post, const Matrix & u, size_t t)<br>system dynamics function | + +**Protected Attributes inherited from [lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/)** + +| | Name | +| -------------- | -------------- | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-dt-)** <br>sample period | +| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-a-)** <br>state matrix | +| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-b-)** <br>input matrix | +| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-g-)** <br>input gain | +| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-m-)** <br>process noise mean | +| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-q-)** <br>process noise cov | +| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-c-)** <br>output matrix | +| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-d-)** <br>output bias | +| Matrix | **[R_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-r-)** <br>measurement noise | +| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-x0-)** <br>initial state | +| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-p0-)** <br>initial covar | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-y-)** <br>number of outputs | + + +--- +--- +## Public Function Details + +### **Fit** + +```cpp +Fit() =default +``` + + + +--- +### **Fit** + +```cpp +inline Fit( + size_t n_u, + size_t n_x, + size_t n_y, + data_t dt +) +``` + + + +**Parameters**: + + * **n_u** number of inputs + * **n_x** number of states + * **n_y** number of outputs + * **dt** sample period + + +--- +### **h** + +```cpp +inline virtual View h( + Matrix & y, + const Matrix & x, + size_t t +) override +``` + + + +**Parameters**: + + * **y** output estimate (over time) + * **x** state estimate (over time) + * **t** time index + + +**Return**: output + +**Reimplements**: [lds::Fit::h](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-h) + + +--- +### **set_R** + +```cpp +inline virtual void set_R( + const Matrix & R +) override +``` + + + +**Reimplements**: [lds::Fit::set_R](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-r) + + +--- +### **R** + +```cpp +inline virtual const Matrix & R() const override +``` + + + +**Reimplements**: [lds::Fit::R](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-r) + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md new file mode 100644 index 00000000..bbf0f88b --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md @@ -0,0 +1,96 @@ +--- +title: lds::poisson::FitEM +summary: PLDS E-M Fit Type. + +--- + +# lds::poisson::FitEM + + + +PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/) Type. [More...](#detailed-description) + + +<br /> `#include <lds_poisson_fit_em.h>` + +Inherits from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/) + +## Additional inherited members + +**Public Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** + +| | Name | +| -------------- | -------------- | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**() =default<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**(size_t n_x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| | **[EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em)**(const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) & fit0, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train)<br>Constructs a new [EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)[Fit]() type. | +| virtual | **[~EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em)**() =default | +| const [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) & | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M) | +| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata)**()<br>Returns the input/output data to caller. | +| const std::vector< Matrix > & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x)**() const<br>gets estimated state (over time) | +| const std::vector< Matrix > & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y)**() const<br>gets estimated output (over time) | +| const Matrix & | **[sum_E_x_t_x_t](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t)**() const<br>gets state-input covariance | +| const Matrix & | **[sum_E_xu_tm1_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1)**() const<br>gets state-input covariance (t-minus-1) | +| const Matrix & | **[sum_E_xu_t_xu_tm1](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1)**() const<br>gets single lag state-input covariance | +| size_t | **[n_t_tot](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot)**()<br>total number of time samples | +| const Vector & | **[theta](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta)**() const<br>gets parameters updated in M step | + +**Protected Functions inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** + +| | Name | +| -------------- | -------------- | +| void | **[Expectation](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation)**(bool force_common_initial =false)<br>Expectation step. | +| void | **[Maximization](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization)**(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step. | +| void | **[MaximizeDynamics](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics)**() | +| void | **[MaximizeQ](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq)**() | +| void | **[MaximizeInitial](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial)**() | +| void | **[Smooth](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth)**(bool force_common_initial)<br>get smoothed estimates | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset)**()<br>reset to initial conditions | +| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars)**()<br>Initializes the variables. | +| Vector | **[UpdateTheta](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta)**()<br>updates parameter list, theta | + +**Protected Attributes inherited from [lds::EM< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** + +| | Name | +| -------------- | -------------- | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-)** <br>input training data | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-)** <br>measurement training data | +| std::vector< Matrix > | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-)** <br>state estimate | +| std::vector< Cube > | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-)** <br>state estimate cov | +| std::vector< Cube > | **[P_t_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-)** <br>single-lag state covariance | +| std::vector< Matrix > | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-)** <br>output estimate | +| Matrix | **[diag_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-)** | +| Matrix | **[sum_E_x_t_x_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-)** <br>state covariance (current time) | +| Matrix | **[sum_E_xu_tm1_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-)** <br>state-input covariance (t-minus-1) | +| Matrix | **[sum_E_xu_t_xu_tm1_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-)** <br>single lag state-input covariance | +| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-)** | +| Vector | **[theta_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-)** <br>sample period | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-)** <br>number of outputs | +| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-)** <br>number of input/output data sequences | +| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-)** <br>number of time steps | +| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-)** <br>total number of time steps across trials | + + +## Detailed Description + +```cpp +class lds::poisson::FitEM; +``` + + + + + +``` + This type is used in the process of fitting PLDS models by + expectation-maximization (EM). +``` + +--- +--- +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md new file mode 100644 index 00000000..3f33c77d --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md @@ -0,0 +1,63 @@ +--- +title: lds::poisson::FitSSID +summary: Subspace Identification (SSID) for PLDS. + +--- + +# lds::poisson::FitSSID + + + +Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for PLDS. +<br /> `#include <lds_poisson_fit_ssid.h>` + +Inherits from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/) + +## Additional inherited members + +**Public Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** + +| | Name | +| -------------- | -------------- | +| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid)**() =default<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) type. | +| | **[SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid)**(size_t n_x, size_t n_h, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && u_train, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > && z_train, const Vector & d =Vector(1).fill(-kInf))<br>Constructs a new [SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)[Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) type. | +| std::tuple< [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/), Vector > | **[Run](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) ssid_wt)<br>Runs fitting by subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) | +| std::tuple< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) >, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[ReturnData](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata)**()<br>Returns the I/O data to caller. | + +**Protected Functions inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** + +| | Name | +| -------------- | -------------- | +| void | **[CalcD](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) t_silence =0.1, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d) | +| void | **[CreateHankelDataMat](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat)**()<br>Creates the block-hankel I/O data matrix. | +| void | **[CalcSVD](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd)**([SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt) wt)<br>performs the singular value decomposition (SVD) | +| void | **[Solve](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) wt_dc)<br>solves for LDS parameters | +| void | **[RecomputeExtObs](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs)**()<br>recompute extended observability matrix from estimates of A, C | + +**Protected Attributes inherited from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** + +| | Name | +| -------------- | -------------- | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-)** <br>input training data | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-)** <br>measurement training data | +| Matrix | **[D_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-)** <br>block-Hankel I/O data matrix | +| [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/) | **[fit_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-)** <br>fit | +| Matrix | **[g_dc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-)** <br>I/O gain @ DC. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-)** <br>sample period | +| size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-)** <br>number of inputs | +| size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-)** <br>number of states | +| size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-)** <br>number of outputs | +| size_t | **[n_h_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-)** | +| size_t | **[n_trials_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-)** <br>number of input/output data sequences | +| std::vector< size_t > | **[n_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-)** <br>number of time steps | +| size_t | **[n_t_tot_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-)** <br>total number of time steps across trials | +| Matrix | **[L_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-)** <br>lower triangle decomp of covariance matrix | +| Vector | **[s_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-)** <br>singular values | +| Matrix | **[ext_obs_t_](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-)** <br>extended observability matrix | + + +--- +--- +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md new file mode 100644 index 00000000..f20892f1 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md @@ -0,0 +1,145 @@ +--- +title: lds::poisson::SwitchedController +summary: Poisson-observation SwitchedController Type. + +--- + +# lds::poisson::SwitchedController + + + +Poisson-observation [SwitchedController]() Type. +<br /> `#include <lds_poisson_sctrl.h>` + +Inherits from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/), [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| virtual void | **[set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/#function-set-y-ref)**(const Vector & y_ref) override<br>Set reference output. | + +## Additional inherited members + +**Public Functions inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/)** + +| | Name | +| -------------- | -------------- | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**() =default<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/). | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**(const std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > & systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/). | +| | **[SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller)**(std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > && systems, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/) (moves systems). | +| void | **[Switch](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switch)**(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller. | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc)<br>sets state feedback gains | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc)<br>sets state feedback gains (moving) | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc_inty)<br>sets integral feedback gains | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc_inty)<br>sets integral feedback gains (moving) | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u)**(const [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> & Kc_u)<br>sets input feedback gains | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)<> && Kc_u)<br>sets input feedback gains (moving) | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design)**(const [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) & g)<br>sets input gain used during controller design | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design)**([UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) && g)<br>sets input gain used during controller design (moving) | + +**Protected Attributes inherited from [lds::SwitchedController< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/)** + +| | Name | +| -------------- | -------------- | +| std::vector< [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) > | **[systems_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-systems-)** <br>underlying sub-systems which are switched between | +| size_t | **[n_sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-n-sys-)** <br>number of systems | +| size_t | **[idx_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-idx-)** <br>current system/controller index. | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-list-)** | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_inty_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-inty-list-)** | +| [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/) | **[Kc_u_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-u-list-)** | +| [UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/) | **[g_design_list_](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-g-design-list-)** | + +**Public Functions inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**() =default<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/). | +| | **[Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller)**([System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) && sys, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub, size_t control_type =0)<br>Constructs a new [Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/) by moving the system object. | +| const Vector & | **[Control](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control)**(const Vector & z, bool do_control =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step) | +| const Vector & | **[ControlOutputReference](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference)**(const Vector & z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_soft_start =0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step) | +| const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & | **[sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys)**() const | +| const Matrix & | **[Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc)**() const<br>Get state feedback controller gain. | +| const Matrix & | **[Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty)**() const<br>Get integral controller gain. | +| const Matrix & | **[Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u)**() const<br>Get input feedback controller gain. | +| const Vector & | **[g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design)**() const<br>Get input gain used in controller design. | +| const Vector & | **[u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref)**() const<br>Get reference input. | +| const Vector & | **[x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref)**() const<br>Get reference state. | +| const Vector & | **[y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref)**() const<br>Get reference output. | +| size_t | **[control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type)**() const<br>Get controller type. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu)**() const<br>Get time constant of anti-integral-windup. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb)**() const<br>Get control lower bound. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub)**() const<br>Get control upper bound. | +| void | **[set_sys](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys)**(const [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) & sys)<br>Set system. | +| void | **[set_g_design](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design)**(const Vector & g_design)<br>Set input gain used in controller design (g_design) | +| void | **[set_u_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref)**(const Vector & u_ref)<br>Set reference input (u_ref) | +| void | **[set_x_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref)**(const Vector & x_ref)<br>Set reference state (x_ref) | +| void | **[set_Kc](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc)**(const Matrix & Kc)<br>Set state controller gain. | +| void | **[set_Kc_inty](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty)**(const Matrix & Kc_inty)<br>Set integral controller gain. | +| void | **[set_Kc_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u)**(const Matrix & Kc_u)<br>Set input controller gain. | +| void | **[set_tau_awu](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) tau)<br>Set time constant of anti-integral-windup. | +| void | **[set_control_type](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type)**(size_t control_type)<br>Sets the control type. | +| void | **[set_u_lb](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_lb)<br>sets control lower bound | +| void | **[set_u_ub](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) u_ub)<br>Sets control upper bound. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset)**()<br>reset system and control variables. | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print)**()<br>prints variables to stdout | + +**Protected Attributes inherited from [lds::Controller< System >](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** + +| | Name | +| -------------- | -------------- | +| [System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) | **[sys_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-)** <br>underlying LDS | +| Vector | **[u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-)** <br>control signal | +| Vector | **[u_return_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-)** <br>control signal that is _returned_ to user | +| Vector | **[g_design_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-)** <br>input gain of the system used for controller design | +| Vector | **[u_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-)** <br>reference input | +| Vector | **[u_ref_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-)** <br>reference input at previous time step | +| Vector | **[x_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-)** <br>reference state | +| Vector | **[y_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-)** <br>reference output | +| Vector | **[cx_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-)** | +| Matrix | **[Kc_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-)** <br>state controller gain | +| Matrix | **[Kc_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-)** <br>input controller gain (optional when control updates \deltaU) | +| Matrix | **[Kc_inty_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-)** <br>integral controller gain | +| Vector | **[du_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-)** | +| Vector | **[dv_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-)** | +| Vector | **[v_ref_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-)** | +| Vector | **[dv_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-)** | +| Vector | **[v_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-)** <br>Control after g inversion (e.g., control in physical units) | +| Vector | **[int_e_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-)** <br>integrated error | +| Vector | **[int_e_awu_adjust_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-)** <br>anti-windup adjustment to intE | +| Vector | **[u_sat_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-)** <br>control signal after saturation (for antiWindup) | +| bool | **[do_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-)** | +| bool | **[do_lock_control_prev_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-)** | +| bool | **[u_saturated_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-)** <br>whether control signal has reached saturation limits | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_lb_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-)** <br>lower bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[u_ub_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-)** <br>upper bound on control | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[tau_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-)** <br>antiwindup time constant | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[k_awu_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-)** | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[t_since_control_onset_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-)** <br>time since control epoch onset | +| size_t | **[control_type_](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-)** <br>controller type | + + +--- +--- +## Public Function Details + +### **set_y_ref** + +```cpp +inline virtual void set_y_ref( + const Vector & y_ref +) override +``` + + + +**Reimplements**: [lds::Controller::set_y_ref](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref) + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md new file mode 100644 index 00000000..e4e2d313 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md @@ -0,0 +1,240 @@ +--- +title: lds::poisson::System +summary: Poisson System type. + +--- + +# lds::poisson::System + + + +Poisson [System]() type. +<br /> `#include <lds_poisson_sys.h>` + +Inherits from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/) + +## Public Functions + +| | Name | +| -------------- | -------------- | +| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-system)**() =default<br>Constructs a new [System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/). | +| | **[System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-system)**(std::size_t n_u, std::size_t n_x, std::size_t n_y, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) dt, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0)<br>Constructs a new Poisson [System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/). | +| virtual const Vector & | **[Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-simulate)**(const Vector & u_tm1) override<br>Simulate system measurement. | + +## Protected Functions + +| | Name | +| -------------- | -------------- | +| virtual void | **[h](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h)**() override<br>[System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/) output function. | +| virtual Vector | **[h_](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h-)**(Vector x) override<br>[System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/) output function: stateless. | +| virtual void | **[RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-recurseke)**() override<br>Recursively recalculate estimator gain (Ke) | + +## Additional inherited members + +**Public Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| virtual | **[~System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-~system)**() | +| void | **[Filter](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-filter)**(const Vector & u_tm1, const Vector & z)<br>Filter data to produce causal state estimates. | +| void | **[f](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-f)**(const Vector & u, bool do_add_noise =false)<br>system dynamics function | +| size_t | **[n_u](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-u)**() const<br>Get number of inputs. | +| size_t | **[n_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-x)**() const<br>Get number of states. | +| size_t | **[n_y](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-y)**() const<br>Get number of outputs. | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-dt)**() const<br>Get sample period. | +| const Vector & | **[x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x)**() const<br>Get current state. | +| const Matrix & | **[P](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p)**() const<br>Get covariance of state estimate. | +| const Vector & | **[m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m)**() const<br>Get current process disturbance/bias. | +| const Matrix & | **[P_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p-m)**() const<br>Get covariance of process disturbance estimate. | +| const Vector & | **[cx](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-cx)**() const<br>Get C*x. | +| const Vector & | **[y](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-y)**() const<br>Get output. | +| const Vector & | **[x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x0)**() const<br>Get initial state. | +| const Vector & | **[m0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m0)**() const<br>Get initial disturbance. | +| const Matrix & | **[A](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-a)**() const<br>Get state matrix. | +| const Matrix & | **[B](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-b)**() const<br>Get input matrix. | +| const Vector & | **[g](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-g)**() const<br>Get input gain/conversion factor. | +| const Matrix & | **[C](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-c)**() const<br>Get output matrix. | +| const Vector & | **[d](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-d)**() const<br>Get output bias. | +| const Matrix & | **[Ke](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke)**() const<br>Get estimator gain. | +| const Matrix & | **[Ke_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke-m)**() const<br>Get estimator gain for process disturbance (m) | +| const Matrix & | **[Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q)**()<br>Get process noise covariance. | +| const Matrix & | **[Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q-m)**()<br>Get process noise covariance of disturbance evoluation. | +| const Matrix & | **[P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0)**()<br>Get covariance of initial state. | +| const Matrix & | **[P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0-m)**()<br>Get covariance of initial process disturbance. | +| void | **[set_A](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-a)**(const Matrix & A)<br>Set state matrix. | +| void | **[set_B](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-b)**(const Matrix & B)<br>Set input matrix. | +| void | **[set_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-m)**(const Vector & m, bool do_force_assign =false)<br>Set process disturbance. | +| void | **[set_g](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-g)**(const Vector & g)<br>Set input gain. | +| void | **[set_Q](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q)**(const Matrix & Q)<br>Set process noise covariance. | +| void | **[set_Q_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q-m)**(const Matrix & Q_m)<br>Set process noise covariance of disturbance evoluation. | +| void | **[set_x0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x0)**(const Vector & x0)<br>Set initial state. | +| void | **[set_P0](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0)**(const Matrix & P0)<br>Set covariance of initial state. | +| void | **[set_P0_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0-m)**(const Matrix & P0_m)<br>Set covariance of initial process disturbance. | +| void | **[set_C](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-c)**(const Matrix & C)<br>Set output matrix. | +| void | **[set_d](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-d)**(const Vector & d)<br>Set output bias. | +| void | **[set_x](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x)**(const Vector & x)<br>Set state of system. | +| void | **[Reset](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-reset)**()<br>Reset system variables. | +| std::vector< [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > > | **[nstep_pred_block](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-nstep-pred-block)**([UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > u, [UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)< [kMatFreeDim2](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2) > z, size_t n_pred =1) | +| void | **[Print](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-print)**()<br>Print system variables to stdout. | + +**Protected Functions inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| void | **[InitVars](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-initvars)**([data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) p0 =kDefaultP0, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) q0 =kDefaultQ0) | + +**Public Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| bool | **[do_adapt_m](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-do-adapt-m)** <br>whether to adaptively estimate disturbance m | + +**Protected Attributes inherited from [lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** + +| | Name | +| -------------- | -------------- | +| std::size_t | **[n_x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-x-)** <br>number of states | +| std::size_t | **[n_u_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-u-)** <br>number of inputs | +| std::size_t | **[n_y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-y-)** <br>number of outputs | +| [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[dt_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-dt-)** <br>sample period | +| Vector | **[x_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x-)** <br>state | +| Matrix | **[P_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-)** <br>covariance of state estimate | +| Vector | **[m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m-)** <br>process disturbance | +| Matrix | **[P_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-m-)** <br>covariance of disturbance estimate | +| Vector | **[cx_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-cx-)** <br>C*x. | +| Vector | **[y_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-y-)** <br>output | +| Vector | **[z_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-z-)** <br>measurement | +| Vector | **[x0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x0-)** <br>initial state | +| Matrix | **[P0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-)** <br>covariance of initial state estimate | +| Vector | **[m0_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m0-)** <br>initial process disturbance | +| Matrix | **[P0_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-m-)** <br>covariance of initial disturbance est. | +| Matrix | **[A_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-a-)** <br>state matrix | +| Matrix | **[B_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-b-)** <br>input matrix | +| Vector | **[g_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-g-)** <br>input gain | +| Matrix | **[Q_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-)** <br>covariance of process noise | +| Matrix | **[Q_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-m-)** <br>covariance of disturbance random walk | +| Matrix | **[C_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-c-)** <br>output matrix | +| Vector | **[d_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-d-)** <br>output bias | +| Matrix | **[Ke_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-)** <br>estimator gain | +| Matrix | **[Ke_m_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-m-)** <br>estimator gain for process disturbance | + + +--- +--- +## Public Function Details + +### **System** + +```cpp +System() =default +``` + + + +--- +### **System** + +```cpp +System( + std::size_t n_u, + std::size_t n_x, + std::size_t n_y, + data_t dt, + data_t p0 =kDefaultP0, + data_t q0 =kDefaultQ0 +) +``` + + + +**Parameters**: + + * **n_u** number of inputs + * **n_x** number of states + * **n_y** number of outputs + * **dt** sample period + * **p0** [optional] initial diagonal elements of state estimate covariance (P) + * **q0** [optional] initial diagonal elements of process noise covariance (Q) + + +--- +### **Simulate** + +```cpp +virtual const Vector & Simulate( + const Vector & u_tm1 +) override +``` + + + +**Parameters**: + + * **u_tm1** input at t-1 + + +**Return**: z measurement + +**Reimplements**: [lds::System::Simulate](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-simulate) + + +Simulate system and produce measurement + + +--- + + +## Protected Function Details + +### **h** + +```cpp +inline virtual void h() override +``` + + + +**Reimplements**: [lds::System::h](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h) + + +--- +### **h_** + +```cpp +inline virtual Vector h_( + Vector x +) override +``` + + + +**Reimplements**: [lds::System::h_](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h-) + + +--- +### **RecurseKe** + +```cpp +virtual void RecurseKe() override +``` + + + +**Reimplements**: [lds::System::RecurseKe](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-recurseke) + + +Recursively recalculate estimator gain (Ke). + +References: + +Smith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15. + +Eden UT, ..., Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16. + + +--- + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Examples/_index.md b/misc/docs-hugo/content/docs/api/Examples/_index.md index 359dbc8c..3bea99c0 100644 --- a/misc/docs-hugo/content/docs/api/Examples/_index.md +++ b/misc/docs-hugo/content/docs/api/Examples/_index.md @@ -1,46 +1,46 @@ ---- -title: Examples - ---- - -# Examples - - - - - - - -- **[eg_glds_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/#example-eg-glds-ctrl.cpp)** - - - - - -- **[eg_glds_du_plds_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/#example-eg-glds-du-plds-ctrl.cpp)** - - - - - -- **[eg_plds_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/#example-eg-plds-ctrl.cpp)** - - - - - -- **[eg_plds_est.cpp](/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/#example-eg-plds-est.cpp)** - - - - - -- **[eg_plds_switched_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/#example-eg-plds-switched-ctrl.cpp)** - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time +--- +title: Examples + +--- + +# Examples + + + + + + + +- **[eg_glds_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/#example-eg-glds-ctrl.cpp)** <br>Example GLDS Control. + + + + + +- **[eg_glds_du_plds_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/#example-eg-glds-du-plds-ctrl.cpp)** <br>Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). + + + + + +- **[eg_plds_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/#example-eg-plds-ctrl.cpp)** <br>Example PLDS Control. + + + + + +- **[eg_plds_est.cpp](/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/#example-eg-plds-est.cpp)** <br>Example PLDS Estimation. + + + + + +- **[eg_plds_switched_ctrl.cpp](/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/#example-eg-plds-switched-ctrl.cpp)** <br>Example Switched PLDS Control. + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md index 6a17bad8..55f80a7d 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md @@ -1,273 +1,275 @@ ---- -title: eg_glds_ctrl.cpp - ---- - -# eg_glds_ctrl.cpp - - - -Example GLDS Control - -```cpp -//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::Matrix; -using lds::Vector; -using lds::data_t; -using std::cout; - -auto main() -> int { - cout << " ********** Example Gaussian LDS Control ********** \n\n"; - - // Make 1st-order SISO system, sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(5.0 / dt); - - // construct ground truth system to be controlled... - // initializes to random walk model with top-most n_y state observed - lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); - - // Ground-truth parameters for the controlled system - // (stand-in for physical system to be controlled) - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = exp(-dt / 0.01); - Matrix b_true = Matrix(n_x, n_u).fill(2e-4); - // control signal to model input unit conversion e.g., V -> mW/mm2: - Vector g_true = Vector(n_y).fill(10.0); - - // output noise covariance - Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; - - size_t which_m = 0; // whether low or high disturbance (0, 1) - data_t m_low = 5 * dt * (1 - a_true[0]); - data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. - data_t m_high = 20 * dt * (1 - a_true[0]); - data_t pr_hi2lo = pr_lo2hi; - - // initially let m be low - Vector m0_true = Vector(n_x).fill(m_low); - - // Assign params. - controlled_system.set_A(a_true); - controlled_system.set_B(b_true); - controlled_system.set_m(m0_true); - controlled_system.set_g(g_true); - controlled_system.set_R(r_true); - - cout << ".....................................\n"; - cout << "controlled_system:\n"; - cout << ".....................................\n"; - controlled_system.Print(); - cout << ".....................................\n"; - - // make a controller - lds::gaussian::Controller controller; - { - // Create **incorrect** model used for control. - // (e.g., imperfect model fitting) - Matrix b_controller = b_true / 2; - - // let's assume zero process disturbance initially - // (will be re-estimating) - Vector m_controller = Vector(n_x, arma::fill::zeros); - - // for this demo, just use arbitrary default R - Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; - - lds::gaussian::System controller_system(controlled_system); - controller_system.set_B(b_controller); - controller_system.set_m(m_controller); - controller_system.set_R(r_controller); - controller_system.Reset(); // reset to new m - - // going to adaptively re-estimate the disturbance - controller_system.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise - // acting on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; - controller_system.set_Q_m(q_m); - - // create controller - // lower and upper bounds on control signal (e.g., in Volts) - data_t u_lb = 0.0; // [=] V - data_t u_ub = 5.0; // [=] V - controller = std::move( - lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); - } - - // Control variables: - // if following enabled, adapts set point with re-estimated process - // disturbance n.b., should not need integral action if this is enabled as the - // adaptive estimator minimizes DC error - bool do_adaptive_set_point = false; - - // Reference/target output, controller gains - Vector y_ref0 = Vector(n_y).fill(20.0 * dt); - Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error - Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err - - // setting initial state to target to avoid error at onset: - Vector x0 = Vector(n_x).fill(y_ref0[0]); - - // set up controller type bit mask so controller knows how to proceed - size_t control_type = 0; - if (do_adaptive_set_point) { - // adapt set point with estimated disturbance - control_type = control_type | lds::kControlTypeAdaptM; - } else { - // use integral action to minimize DC error - control_type = control_type | lds::kControlTypeIntY; - } - - // set controller type - controller.set_control_type(control_type); - - // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): - Vector g_design = Vector(n_u).fill(9); - - // Set params. - // **n.b. using arbitrary defaults for Q, R in this example. Really, these - // should be set by users, as they tune characteristics of Kalman filter. - // Users can also choose not to recursively calculate the estimator gain and - // supply it (setKe) instead of covariances.** - controller.set_y_ref(y_ref0); - controller.set_Kc(k_x); - controller.set_Kc_inty(k_inty); - controller.set_g_design(g_design); - - cout << ".....................................\n"; - cout << "control system:\n"; - cout << ".....................................\n"; - controller.Print(); - cout << ".....................................\n"; - - // set up variables for simulation - // create Matrix to save outputs in... - Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; - - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // simulated control signal ([=] V) - Matrix u(n_u, n_t, arma::fill::zeros); - - // outputs, states and gain/disturbance params - // *_hat indicates online estimates - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_x, n_t, arma::fill::zeros); - - // *_true indicates ground truth (system being controlled) - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_x, n_t, arma::fill::zeros); - - // set initial val - y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); - y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); - - x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); - x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); - - m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); - m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // simulate a stochastically switched disturbance - Vector chance = arma::randu<Vector>(1); - if (which_m == 0) // low disturbance - { - if (chance[0] < pr_lo2hi) { // switches low -> high disturbance - m0_true = std::vector<data_t>(n_x, m_high); - which_m = 1; - } - } else { // high disturbance - if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance - m0_true = std::vector<data_t>(n_x, m_low); - which_m = 0; - } - } - controlled_system.set_m(m0_true); - - // input - Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); - - // Simulate the true system. - z.col(t) = controlled_system.Simulate(u_tm1); - - // This method uses a steady-state solution to control problem to calculate - // x_ref, u_ref from reference output y_ref. Therefore, it is only - // applicable to regulation problems or cases where reference trajectory - // changes slowly compared to system dynamics. - u.col(t) = controller.ControlOutputReference(z.col(t)); - - // save the signals - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - m_true.col(t) = controlled_system.m(); - - y_hat.col(t) = controller.sys().y(); - x_hat.col(t) = controller.sys().x(); - m_hat.col(t) = controller.sys().m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - cout << "Saving simulation data to disk.\n"; - - // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, - // xTrue, mTrue saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_glds_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_glds_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_glds_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_hat", replace)); - - cout << "fin.\n"; - return 0; -} -``` - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: eg_glds_ctrl.cpp +summary: Example GLDS Control. + +--- + +# eg_glds_ctrl.cpp + + + +Example GLDS Control ```cpp + +//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout; + +auto main() -> int { + cout << " ********** Example Gaussian LDS Control ********** \n\n"; + + // Make 1st-order SISO system, sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(5.0 / dt); + + // construct ground truth system to be controlled... + // initializes to random walk model with top-most n_y state observed + lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); + + // Ground-truth parameters for the controlled system + // (stand-in for physical system to be controlled) + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = exp(-dt / 0.01); + Matrix b_true = Matrix(n_x, n_u).fill(2e-4); + // control signal to model input unit conversion e.g., V -> mW/mm2: + Vector g_true = Vector(n_y).fill(10.0); + + // output noise covariance + Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; + + size_t which_m = 0; // whether low or high disturbance (0, 1) + data_t m_low = 5 * dt * (1 - a_true[0]); + data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. + data_t m_high = 20 * dt * (1 - a_true[0]); + data_t pr_hi2lo = pr_lo2hi; + + // initially let m be low + Vector m0_true = Vector(n_x).fill(m_low); + + // Assign params. + controlled_system.set_A(a_true); + controlled_system.set_B(b_true); + controlled_system.set_m(m0_true); + controlled_system.set_g(g_true); + controlled_system.set_R(r_true); + + cout << ".....................................\n"; + cout << "controlled_system:\n"; + cout << ".....................................\n"; + controlled_system.Print(); + cout << ".....................................\n"; + + // make a controller + lds::gaussian::Controller controller; + { + // Create **incorrect** model used for control. + // (e.g., imperfect model fitting) + Matrix b_controller = b_true / 2; + + // let's assume zero process disturbance initially + // (will be re-estimating) + Vector m_controller = Vector(n_x, arma::fill::zeros); + + // for this demo, just use arbitrary default R + Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; + + lds::gaussian::System controller_system(controlled_system); + controller_system.set_B(b_controller); + controller_system.set_m(m_controller); + controller_system.set_R(r_controller); + controller_system.Reset(); // reset to new m + + // going to adaptively re-estimate the disturbance + controller_system.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise + // acting on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; + controller_system.set_Q_m(q_m); + + // create controller + // lower and upper bounds on control signal (e.g., in Volts) + data_t u_lb = 0.0; // [=] V + data_t u_ub = 5.0; // [=] V + controller = std::move( + lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); + } + + // Control variables: + // if following enabled, adapts set point with re-estimated process + // disturbance n.b., should not need integral action if this is enabled as the + // adaptive estimator minimizes DC error + bool do_adaptive_set_point = false; + + // Reference/target output, controller gains + Vector y_ref0 = Vector(n_y).fill(20.0 * dt); + Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error + Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err + + // setting initial state to target to avoid error at onset: + Vector x0 = Vector(n_x).fill(y_ref0[0]); + + // set up controller type bit mask so controller knows how to proceed + size_t control_type = 0; + if (do_adaptive_set_point) { + // adapt set point with estimated disturbance + control_type = control_type | lds::kControlTypeAdaptM; + } else { + // use integral action to minimize DC error + control_type = control_type | lds::kControlTypeIntY; + } + + // set controller type + controller.set_control_type(control_type); + + // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): + Vector g_design = Vector(n_u).fill(9); + + // Set params. + // **n.b. using arbitrary defaults for Q, R in this example. Really, these + // should be set by users, as they tune characteristics of Kalman filter. + // Users can also choose not to recursively calculate the estimator gain and + // supply it (setKe) instead of covariances.** + controller.set_y_ref(y_ref0); + controller.set_Kc(k_x); + controller.set_Kc_inty(k_inty); + controller.set_g_design(g_design); + + cout << ".....................................\n"; + cout << "control system:\n"; + cout << ".....................................\n"; + controller.Print(); + cout << ".....................................\n"; + + // set up variables for simulation + // create Matrix to save outputs in... + Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; + + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // simulated control signal ([=] V) + Matrix u(n_u, n_t, arma::fill::zeros); + + // outputs, states and gain/disturbance params + // *_hat indicates online estimates + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_x, n_t, arma::fill::zeros); + + // *_true indicates ground truth (system being controlled) + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_x, n_t, arma::fill::zeros); + + // set initial val + y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); + y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); + + x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); + x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); + + m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); + m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // simulate a stochastically switched disturbance + Vector chance = arma::randu<Vector>(1); + if (which_m == 0) // low disturbance + { + if (chance[0] < pr_lo2hi) { // switches low -> high disturbance + m0_true = std::vector<data_t>(n_x, m_high); + which_m = 1; + } + } else { // high disturbance + if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance + m0_true = std::vector<data_t>(n_x, m_low); + which_m = 0; + } + } + controlled_system.set_m(m0_true); + + // input + Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); + + // Simulate the true system. + z.col(t) = controlled_system.Simulate(u_tm1); + + // This method uses a steady-state solution to control problem to calculate + // x_ref, u_ref from reference output y_ref. Therefore, it is only + // applicable to regulation problems or cases where reference trajectory + // changes slowly compared to system dynamics. + u.col(t) = controller.ControlOutputReference(z.col(t)); + + // save the signals + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + m_true.col(t) = controlled_system.m(); + + y_hat.col(t) = controller.sys().y(); + x_hat.col(t) = controller.sys().x(); + m_hat.col(t) = controller.sys().m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + cout << "Saving simulation data to disk.\n"; + + // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, + // xTrue, mTrue saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_glds_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_glds_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_glds_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_hat", replace)); + + cout << "fin.\n"; + return 0; +} +``` + +_Filename: eg_glds_ctrl.cpp_ + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md index 0a5b7ea0..33b998e0 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md @@ -1,274 +1,276 @@ ---- -title: eg_glds_du_plds_ctrl.cpp - ---- - -# eg_glds_du_plds_ctrl.cpp - - - -Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). - -```cpp -//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::data_t; -using lds::Matrix; -using lds::Vector; -using std::cout; - -auto main() -> int { - cout << " ********** Example Gaussian LDS du Control of PLDS ********** \n\n"; - - // Make 1st-order SISO system, sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(5.0 / dt); - - // construct ground truth system to be controlled... - // initializes to random walk model with top-most n_y state observed - lds::poisson::System controlled_system(n_u, n_x, n_y, dt); - - // Ground-truth parameters for the controlled system - // (stand-in for physical system to be controlled) - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = exp(-dt / 0.01); - Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); - // control signal to model input unit conversion e.g., V -> mW/mm2: - Vector g_true = Vector(n_y).fill(10.0); - - size_t which_m = 0; // whether low or high disturbance (0, 1) - data_t m_low = log(1 * dt) * (1 - a_true[0]); - data_t pr_lo2hi = - 0; // 1e-3; // probability of going from low to high disturb. - data_t m_high = log(20 * dt) * (1 - a_true[0]); - data_t pr_hi2lo = pr_lo2hi; - - // initially let m be low - Vector m0_true = Vector(n_x).fill(m_low); - Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); - - // Assign params. - controlled_system.set_A(a_true); - controlled_system.set_B(b_true); - controlled_system.set_m(m0_true); - controlled_system.set_g(g_true); - controlled_system.set_x0(x0_true); - controlled_system.Reset(); - - cout << ".....................................\n"; - cout << "controlled_system:\n"; - cout << ".....................................\n"; - controlled_system.Print(); - cout << ".....................................\n"; - - // make a controller - lds::gaussian::Controller controller; - { - // Create **incorrect** model used for control. - // (e.g., imperfect model fitting) - Matrix b_controller = b_true / 50; - - // let's assume zero process disturbance initially - // (will be re-estimating) - Vector m_controller = Vector(n_x, arma::fill::zeros); - - // process noise covariance - Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; - - // output noise covariance - Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; - - lds::gaussian::System controller_system(n_u, n_x, n_y, dt); - controller_system.set_A(a_true); - controller_system.set_B(b_controller); - controller_system.set_g(g_true); - controller_system.set_m(m_controller); - controller_system.set_Q(q_controller); - controller_system.set_R(r_controller); - controller_system.Reset(); // reset to new m - - // going to adaptively re-estimate the disturbance - controller_system.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise - // acting on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; - controller_system.set_Q_m(q_m); - - // create controller - // lower and upper bounds on control signal (e.g., in Volts) - data_t u_lb = 0.0; // [=] V - data_t u_ub = 5.0; // [=] V - controller = std::move( - lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); - } - - // Control variables: - // Reference/target output, controller gains - Vector y_ref0 = Vector(n_y).fill(20.0 * dt); - - // to design for this example, augmented state with control and made the input - // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = - // 1e-2. - Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error - Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err - Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp - - // set up controller type bit mask so controller knows how to proceed - size_t control_type = 0; - // use integral action to minimize DC error - control_type = control_type | lds::kControlTypeIntY; - // update change in control (LP filters control) - control_type = control_type | lds::kControlTypeDeltaU; - - // set controller type - controller.set_control_type(control_type); - - // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): - Vector g_design = Vector(n_u).fill(10); - - // Set params. - // **n.b. using arbitrary defaults for Q, R in this example. Really, these - // should be set by users, as they tune characteristics of Kalman filter. - // Users can also choose not to recursively calculate the estimator gain and - // supply it (setKe) instead of covariances.** - controller.set_y_ref(y_ref0); - controller.set_Kc(k_x); - controller.set_Kc_inty(k_inty); - controller.set_Kc_u(k_u); - controller.set_g_design(g_design); - - cout << ".....................................\n"; - cout << "control system:\n"; - cout << ".....................................\n"; - controller.Print(); - cout << ".....................................\n"; - - // set up variables for simulation - // create Matrix to save outputs in... - Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; - - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // simulated control signal ([=] V) - Matrix u(n_u, n_t, arma::fill::zeros); - - // outputs, states and gain/disturbance params - // *_hat indicates online estimates - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_x, n_t, arma::fill::zeros); - - // *_true indicates ground truth (system being controlled) - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_x, n_t, arma::fill::zeros); - - // get initial val - y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); - y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); - - x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); - x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); - - m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); - m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // simulate a stochastically switched disturbance - Vector chance = arma::randu<Vector>(1); - if (which_m == 0) // low disturbance - { - if (chance[0] < pr_lo2hi) { // switches low -> high disturbance - m0_true = std::vector<data_t>(n_x, m_high); - which_m = 1; - } - } else { // high disturbance - if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance - m0_true = std::vector<data_t>(n_x, m_low); - which_m = 0; - } - } - controlled_system.set_m(m0_true); - - // input - Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); - - // Simulate the true system. - z.col(t) = controlled_system.Simulate(u_tm1); - - // This method uses a steady-state solution to control problem to calculate - // x_ref, u_ref from reference output y_ref. Therefore, it is only - // applicable to regulation problems or cases where reference trajectory - // changes slowly compared to system dynamics. - u.col(t) = controller.ControlOutputReference(z.col(t)); - - // save the signals - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - m_true.col(t) = controlled_system.m(); - - y_hat.col(t) = controller.sys().y(); - x_hat.col(t) = controller.sys().x(); - m_hat.col(t) = controller.sys().m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - cout << "Saving simulation data to disk.\n"; - - // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, - // xTrue, mTrue saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_hat", replace)); - - cout << "fin.\n"; - return 0; -} -``` - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: eg_glds_du_plds_ctrl.cpp +summary: Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). + +--- + +# eg_glds_du_plds_ctrl.cpp + + + +Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp + +//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::data_t; +using lds::Matrix; +using lds::Vector; +using std::cout; + +auto main() -> int { + cout << " ********** Example Gaussian LDS du Control of PLDS ********** \n\n"; + + // Make 1st-order SISO system, sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(5.0 / dt); + + // construct ground truth system to be controlled... + // initializes to random walk model with top-most n_y state observed + lds::poisson::System controlled_system(n_u, n_x, n_y, dt); + + // Ground-truth parameters for the controlled system + // (stand-in for physical system to be controlled) + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = exp(-dt / 0.01); + Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); + // control signal to model input unit conversion e.g., V -> mW/mm2: + Vector g_true = Vector(n_y).fill(10.0); + + size_t which_m = 0; // whether low or high disturbance (0, 1) + data_t m_low = log(1 * dt) * (1 - a_true[0]); + data_t pr_lo2hi = + 0; // 1e-3; // probability of going from low to high disturb. + data_t m_high = log(20 * dt) * (1 - a_true[0]); + data_t pr_hi2lo = pr_lo2hi; + + // initially let m be low + Vector m0_true = Vector(n_x).fill(m_low); + Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); + + // Assign params. + controlled_system.set_A(a_true); + controlled_system.set_B(b_true); + controlled_system.set_m(m0_true); + controlled_system.set_g(g_true); + controlled_system.set_x0(x0_true); + controlled_system.Reset(); + + cout << ".....................................\n"; + cout << "controlled_system:\n"; + cout << ".....................................\n"; + controlled_system.Print(); + cout << ".....................................\n"; + + // make a controller + lds::gaussian::Controller controller; + { + // Create **incorrect** model used for control. + // (e.g., imperfect model fitting) + Matrix b_controller = b_true / 50; + + // let's assume zero process disturbance initially + // (will be re-estimating) + Vector m_controller = Vector(n_x, arma::fill::zeros); + + // process noise covariance + Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; + + // output noise covariance + Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; + + lds::gaussian::System controller_system(n_u, n_x, n_y, dt); + controller_system.set_A(a_true); + controller_system.set_B(b_controller); + controller_system.set_g(g_true); + controller_system.set_m(m_controller); + controller_system.set_Q(q_controller); + controller_system.set_R(r_controller); + controller_system.Reset(); // reset to new m + + // going to adaptively re-estimate the disturbance + controller_system.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise + // acting on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; + controller_system.set_Q_m(q_m); + + // create controller + // lower and upper bounds on control signal (e.g., in Volts) + data_t u_lb = 0.0; // [=] V + data_t u_ub = 5.0; // [=] V + controller = std::move( + lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); + } + + // Control variables: + // Reference/target output, controller gains + Vector y_ref0 = Vector(n_y).fill(20.0 * dt); + + // to design for this example, augmented state with control and made the input + // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = + // 1e-2. + Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error + Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err + Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp + + // set up controller type bit mask so controller knows how to proceed + size_t control_type = 0; + // use integral action to minimize DC error + control_type = control_type | lds::kControlTypeIntY; + // update change in control (LP filters control) + control_type = control_type | lds::kControlTypeDeltaU; + + // set controller type + controller.set_control_type(control_type); + + // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): + Vector g_design = Vector(n_u).fill(10); + + // Set params. + // **n.b. using arbitrary defaults for Q, R in this example. Really, these + // should be set by users, as they tune characteristics of Kalman filter. + // Users can also choose not to recursively calculate the estimator gain and + // supply it (setKe) instead of covariances.** + controller.set_y_ref(y_ref0); + controller.set_Kc(k_x); + controller.set_Kc_inty(k_inty); + controller.set_Kc_u(k_u); + controller.set_g_design(g_design); + + cout << ".....................................\n"; + cout << "control system:\n"; + cout << ".....................................\n"; + controller.Print(); + cout << ".....................................\n"; + + // set up variables for simulation + // create Matrix to save outputs in... + Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; + + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // simulated control signal ([=] V) + Matrix u(n_u, n_t, arma::fill::zeros); + + // outputs, states and gain/disturbance params + // *_hat indicates online estimates + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_x, n_t, arma::fill::zeros); + + // *_true indicates ground truth (system being controlled) + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_x, n_t, arma::fill::zeros); + + // get initial val + y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); + y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); + + x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); + x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); + + m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); + m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // simulate a stochastically switched disturbance + Vector chance = arma::randu<Vector>(1); + if (which_m == 0) // low disturbance + { + if (chance[0] < pr_lo2hi) { // switches low -> high disturbance + m0_true = std::vector<data_t>(n_x, m_high); + which_m = 1; + } + } else { // high disturbance + if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance + m0_true = std::vector<data_t>(n_x, m_low); + which_m = 0; + } + } + controlled_system.set_m(m0_true); + + // input + Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); + + // Simulate the true system. + z.col(t) = controlled_system.Simulate(u_tm1); + + // This method uses a steady-state solution to control problem to calculate + // x_ref, u_ref from reference output y_ref. Therefore, it is only + // applicable to regulation problems or cases where reference trajectory + // changes slowly compared to system dynamics. + u.col(t) = controller.ControlOutputReference(z.col(t)); + + // save the signals + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + m_true.col(t) = controlled_system.m(); + + y_hat.col(t) = controller.sys().y(); + x_hat.col(t) = controller.sys().x(); + m_hat.col(t) = controller.sys().m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + cout << "Saving simulation data to disk.\n"; + + // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, + // xTrue, mTrue saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_hat", replace)); + + cout << "fin.\n"; + return 0; +} +``` + +_Filename: eg_glds_du_plds_ctrl.cpp_ + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md index da605224..bee4b5d6 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md @@ -1,243 +1,245 @@ ---- -title: eg_plds_ctrl.cpp - ---- - -# eg_plds_ctrl.cpp - - - -Example PLDS Control - -```cpp -//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::Matrix; -using lds::Vector; -using lds::data_t; -using std::cout; - -auto main() -> int { - cout << " ********** Example Poisson LDS Control ********** \n\n"; - - // Make SISO system sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(10.0 / dt); - - // Control variables: _reference/target output, controller gains - // n.b., Can either use Vector (arma::Col) or std::vector - Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; - Matrix k_x = - Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error - Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + - 10; // gains on integrated output err - - // Set control type bit mask, so controller knows what to do - size_t control_type = lds::kControlTypeIntY; // integral action - - // Ground-truth parameters for the controlled system - // (stand-in for physical system to be controlled) - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = 0.986; - Matrix b_true(n_x, n_u, arma::fill::zeros); - b_true[0] = 0.054; - Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); - - size_t which_m = 0; - data_t m_low = log(1 * dt) * (1 - a_true[0]); - data_t pr_lo2hi = 1e-3; - data_t m_high = log(20 * dt) * (1 - a_true[0]); - data_t pr_hi2lo = pr_lo2hi; - - Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; - // construct ground truth system to be controlled... - lds::poisson::System controlled_system(n_u, n_x, n_y, dt); - - // Assign params. - controlled_system.set_A(a_true); - controlled_system.set_B(b_true); - controlled_system.set_m(m0_true); - controlled_system.set_x0(x0_true); - // reset to initial conditions - controlled_system.Reset(); - - cout << ".....................................\n"; - cout << "controlled_system:\n"; - cout << ".....................................\n"; - controlled_system.Print(); - cout << ".....................................\n"; - - // Create the controller - lds::poisson::Controller controller; - { - // Create model used for control. - lds::poisson::System controller_system(controlled_system); - - // for this example, assume model correct, except disturbance - Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; - Vector x0_controller = arma::log(y_ref0); - controller_system.set_m(m0_controller); - controller_system.set_x0(x0_controller); - controller_system.Reset(); //reset to new init condition - - // adaptively re-estimate process disturbance (m) - controller_system.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise - // acting on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; - controller_system.set_Q_m(q_m); - - data_t u_lb = 0.0; - data_t u_ub = 5.0; - controller = std::move( - lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); - } - // set controller type - controller.set_control_type(control_type); - - // set controller gains - controller.set_Kc(k_x); - controller.set_Kc_inty(k_inty); - - // to protect against integral windup when output is consistently above - // target: - data_t tau_awu(0.1); - controller.set_tau_awu(tau_awu); - - cout << ".....................................\n"; - cout << "controller:\n"; - cout << ".....................................\n"; - controller.Print(); - cout << ".....................................\n"; - - // create Matrix to save outputs in... - Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); - y_ref.each_col() += y_ref0; - - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // simulated control signal ([=] V) - Matrix u(n_u, n_t, arma::fill::zeros); - - // outputs, states and gain/disturbance params - // *_hat indicates online estimates - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_y, n_t, arma::fill::zeros); - - // *_true indicates ground truth (system being controlled) - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_y, n_t, arma::fill::zeros); - - // set initial val - y_hat.col(0) = controller.sys().y(); - y_true.col(0) = controlled_system.y(); - - x_hat.col(0) = controller.sys().x(); - x_true.col(0) = controlled_system.x(); - - m_hat.col(0) = controller.sys().m(); - m_true.col(0) = controlled_system.m(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // simulate a stochastically switched disturbance - Vector chance = arma::randu<Vector>(1); - if (which_m == 0) // low disturbance - { - if (chance[0] < pr_lo2hi) { // switches low -> high disturbance - m0_true = std::vector<data_t>(n_x, m_high); - which_m = 1; - } - } else { // high disturbance - if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance - m0_true = std::vector<data_t>(n_x, m_low); - which_m = 0; - } - } - controlled_system.set_m(m0_true); - - // e.g., use sinusoidal reference - data_t f = 0.5; // freq [=] Hz - Vector t_vec = Vector(n_y, arma::fill::ones) * t; - y_ref.col(t) += - y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); - - // Simulate the true system. - z.col(t)=controlled_system.Simulate(u.col(t-1)); - - // This method uses a steady-state solution to control problem to calculate - // x_ref, u_ref from reference output y_ref. Notably, it does this in the - // log-linear space (i.e., log(y)). - // - // Therefore, it is only applicable to regulation problems or cases where - // reference trajectory changes slowly compared to system dynamics. - controller.set_y_ref(y_ref.col(t)); - u.col(t)=controller.ControlOutputReference(z.col(t)); - - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - m_true.col(t) = controlled_system.m(); - - y_hat.col(t) = controller.sys().y(); - x_hat.col(t) = controller.sys().x(); - m_hat.col(t) = controller.sys().m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, - // x_true, m_true saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_plds_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_plds_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_plds_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_hat", replace)); - - return 0; -} -``` - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: eg_plds_ctrl.cpp +summary: Example PLDS Control. + +--- + +# eg_plds_ctrl.cpp + + + +Example PLDS Control ```cpp + +//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout; + +auto main() -> int { + cout << " ********** Example Poisson LDS Control ********** \n\n"; + + // Make SISO system sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(10.0 / dt); + + // Control variables: _reference/target output, controller gains + // n.b., Can either use Vector (arma::Col) or std::vector + Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; + Matrix k_x = + Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error + Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + + 10; // gains on integrated output err + + // Set control type bit mask, so controller knows what to do + size_t control_type = lds::kControlTypeIntY; // integral action + + // Ground-truth parameters for the controlled system + // (stand-in for physical system to be controlled) + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = 0.986; + Matrix b_true(n_x, n_u, arma::fill::zeros); + b_true[0] = 0.054; + Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); + + size_t which_m = 0; + data_t m_low = log(1 * dt) * (1 - a_true[0]); + data_t pr_lo2hi = 1e-3; + data_t m_high = log(20 * dt) * (1 - a_true[0]); + data_t pr_hi2lo = pr_lo2hi; + + Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; + // construct ground truth system to be controlled... + lds::poisson::System controlled_system(n_u, n_x, n_y, dt); + + // Assign params. + controlled_system.set_A(a_true); + controlled_system.set_B(b_true); + controlled_system.set_m(m0_true); + controlled_system.set_x0(x0_true); + // reset to initial conditions + controlled_system.Reset(); + + cout << ".....................................\n"; + cout << "controlled_system:\n"; + cout << ".....................................\n"; + controlled_system.Print(); + cout << ".....................................\n"; + + // Create the controller + lds::poisson::Controller controller; + { + // Create model used for control. + lds::poisson::System controller_system(controlled_system); + + // for this example, assume model correct, except disturbance + Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; + Vector x0_controller = arma::log(y_ref0); + controller_system.set_m(m0_controller); + controller_system.set_x0(x0_controller); + controller_system.Reset(); //reset to new init condition + + // adaptively re-estimate process disturbance (m) + controller_system.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise + // acting on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; + controller_system.set_Q_m(q_m); + + data_t u_lb = 0.0; + data_t u_ub = 5.0; + controller = std::move( + lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); + } + // set controller type + controller.set_control_type(control_type); + + // set controller gains + controller.set_Kc(k_x); + controller.set_Kc_inty(k_inty); + + // to protect against integral windup when output is consistently above + // target: + data_t tau_awu(0.1); + controller.set_tau_awu(tau_awu); + + cout << ".....................................\n"; + cout << "controller:\n"; + cout << ".....................................\n"; + controller.Print(); + cout << ".....................................\n"; + + // create Matrix to save outputs in... + Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); + y_ref.each_col() += y_ref0; + + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // simulated control signal ([=] V) + Matrix u(n_u, n_t, arma::fill::zeros); + + // outputs, states and gain/disturbance params + // *_hat indicates online estimates + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_y, n_t, arma::fill::zeros); + + // *_true indicates ground truth (system being controlled) + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_y, n_t, arma::fill::zeros); + + // set initial val + y_hat.col(0) = controller.sys().y(); + y_true.col(0) = controlled_system.y(); + + x_hat.col(0) = controller.sys().x(); + x_true.col(0) = controlled_system.x(); + + m_hat.col(0) = controller.sys().m(); + m_true.col(0) = controlled_system.m(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // simulate a stochastically switched disturbance + Vector chance = arma::randu<Vector>(1); + if (which_m == 0) // low disturbance + { + if (chance[0] < pr_lo2hi) { // switches low -> high disturbance + m0_true = std::vector<data_t>(n_x, m_high); + which_m = 1; + } + } else { // high disturbance + if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance + m0_true = std::vector<data_t>(n_x, m_low); + which_m = 0; + } + } + controlled_system.set_m(m0_true); + + // e.g., use sinusoidal reference + data_t f = 0.5; // freq [=] Hz + Vector t_vec = Vector(n_y, arma::fill::ones) * t; + y_ref.col(t) += + y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); + + // Simulate the true system. + z.col(t)=controlled_system.Simulate(u.col(t-1)); + + // This method uses a steady-state solution to control problem to calculate + // x_ref, u_ref from reference output y_ref. Notably, it does this in the + // log-linear space (i.e., log(y)). + // + // Therefore, it is only applicable to regulation problems or cases where + // reference trajectory changes slowly compared to system dynamics. + controller.set_y_ref(y_ref.col(t)); + u.col(t)=controller.ControlOutputReference(z.col(t)); + + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + m_true.col(t) = controlled_system.m(); + + y_hat.col(t) = controller.sys().y(); + x_hat.col(t) = controller.sys().x(); + m_hat.col(t) = controller.sys().m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, + // x_true, m_true saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_plds_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_plds_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_plds_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_hat", replace)); + + return 0; +} +``` + +_Filename: eg_plds_ctrl.cpp_ + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md index 55ef8d12..a8ad636f 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md @@ -1,193 +1,195 @@ ---- -title: eg_plds_est.cpp - ---- - -# eg_plds_est.cpp - - - -Example PLDS Estimation - -```cpp -//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::Matrix; -using lds::Vector; -using lds::data_t; -using std::cout; - -// for generating random input -Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0); - -int main() { - cout << " ********** Example Poisson LDS Estimation ********** \n\n"; - - // Make SISO system sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; // no. inputs - size_t n_x = 1; // no. states - size_t n_y = 1; // no. outputs - auto n_t = static_cast<size_t>(30 / dt); // no time steps for simulation. - - // construct ground truth system... - lds::poisson::System system_true(n_u, n_x, n_y, dt); - - // Model parameters - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = exp(-dt / 0.075); - Matrix b_true = Matrix(n_x, n_u).fill(1e-2); - Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance - Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - - a_true); // initial state - - // Assign params. - system_true.set_A(a_true); - system_true.set_B(b_true); - system_true.set_x0(x0_true); - system_true.set_m(m0_true); - system_true.Reset(); - - // Construct system for estimation - // e.g., will create a model with incorrect disturbance - lds::poisson::System system_estimator(n_u, n_x, n_y, dt); - - // Can copy parameters from another system object - system_estimator = system_true; - - // wrong disturbance - Vector m0_est = m0_true * 2; - system_estimator.set_m(m0_est); - - // set new initial conditions - Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - - a_true); // initial state - system_estimator.set_x0(x0_est); - system_estimator.Reset(); // reset to initial condition. - - // turn on adaptive disturbance estimation - system_estimator.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise acting - // on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; - system_estimator.set_Q_m(q_m); - - cout << ".....................................\n"; - cout << "estimator:\n"; - cout << ".....................................\n"; - system_estimator.Print(); - cout << ".....................................\n"; - - // Set up simulation : - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // Stimulus (generate random stimulus) - Matrix q_u = - Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk - Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); - - // create matrix to save outputs in... - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix y_true(n_y, n_t, arma::fill::zeros); - - // states and disturbance params - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_x, n_t, arma::fill::zeros); - - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_x, n_t, arma::fill::zeros); - - // initial conditions - y_hat.col(0) = system_estimator.y(); - y_true.col(0) = system_true.y(); - x_hat.col(0) = system_estimator.x(); - x_true.col(0) = system_true.x(); - m_hat.col(0) = system_estimator.m(); - m_true.col(0) = system_true.m(); - - cout << "Starting " << n_t * dt << " sec simlation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // Simlate the true system. - z.col(t) = system_true.Simulate(u.col(t - 1)); - - // Filter (predict -> update) - system_estimator.Filter(u.col(t - 1), z.col(t)); - - // save signals - y_hat.col(t) = system_estimator.y(); - y_true.col(t) = system_true.y(); - - x_true.col(t) = system_true.x(); - m_true.col(t) = system_true.m(); - - x_hat.col(t) = system_estimator.x(); - m_hat.col(t) = system_estimator.m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simlation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, - // x_true, m_true saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_plds_est.h5", "dt")); - u.save(arma::hdf5_name("eg_plds_est.h5", "u", replace)); - z.save(arma::hdf5_name("eg_plds_est.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_plds_est.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_plds_est.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_plds_est.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_plds_est.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_plds_est.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_plds_est.h5", "y_hat", replace)); - - return 0; -} - -// for generating random input -Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0) { - size_t n = Q.n_rows; - - if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { - throw std::logic_error("Q must be `n` x `n`."); - } - - Matrix x(n, n_t, arma::fill::zeros); - x.col(0) = x0; - for (size_t t = 1; t < n_t; t++) { - x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); - } - - return x; -} -``` - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: eg_plds_est.cpp +summary: Example PLDS Estimation. + +--- + +# eg_plds_est.cpp + + + +Example PLDS Estimation ```cpp + +//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout; + +// for generating random input +Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0); + +int main() { + cout << " ********** Example Poisson LDS Estimation ********** \n\n"; + + // Make SISO system sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; // no. inputs + size_t n_x = 1; // no. states + size_t n_y = 1; // no. outputs + auto n_t = static_cast<size_t>(30 / dt); // no time steps for simulation. + + // construct ground truth system... + lds::poisson::System system_true(n_u, n_x, n_y, dt); + + // Model parameters + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = exp(-dt / 0.075); + Matrix b_true = Matrix(n_x, n_u).fill(1e-2); + Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance + Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - + a_true); // initial state + + // Assign params. + system_true.set_A(a_true); + system_true.set_B(b_true); + system_true.set_x0(x0_true); + system_true.set_m(m0_true); + system_true.Reset(); + + // Construct system for estimation + // e.g., will create a model with incorrect disturbance + lds::poisson::System system_estimator(n_u, n_x, n_y, dt); + + // Can copy parameters from another system object + system_estimator = system_true; + + // wrong disturbance + Vector m0_est = m0_true * 2; + system_estimator.set_m(m0_est); + + // set new initial conditions + Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - + a_true); // initial state + system_estimator.set_x0(x0_est); + system_estimator.Reset(); // reset to initial condition. + + // turn on adaptive disturbance estimation + system_estimator.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise acting + // on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; + system_estimator.set_Q_m(q_m); + + cout << ".....................................\n"; + cout << "estimator:\n"; + cout << ".....................................\n"; + system_estimator.Print(); + cout << ".....................................\n"; + + // Set up simulation : + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // Stimulus (generate random stimulus) + Matrix q_u = + Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk + Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); + + // create matrix to save outputs in... + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix y_true(n_y, n_t, arma::fill::zeros); + + // states and disturbance params + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_x, n_t, arma::fill::zeros); + + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_x, n_t, arma::fill::zeros); + + // initial conditions + y_hat.col(0) = system_estimator.y(); + y_true.col(0) = system_true.y(); + x_hat.col(0) = system_estimator.x(); + x_true.col(0) = system_true.x(); + m_hat.col(0) = system_estimator.m(); + m_true.col(0) = system_true.m(); + + cout << "Starting " << n_t * dt << " sec simlation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // Simlate the true system. + z.col(t) = system_true.Simulate(u.col(t - 1)); + + // Filter (predict -> update) + system_estimator.Filter(u.col(t - 1), z.col(t)); + + // save signals + y_hat.col(t) = system_estimator.y(); + y_true.col(t) = system_true.y(); + + x_true.col(t) = system_true.x(); + m_true.col(t) = system_true.m(); + + x_hat.col(t) = system_estimator.x(); + m_hat.col(t) = system_estimator.m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simlation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, + // x_true, m_true saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_plds_est.h5", "dt")); + u.save(arma::hdf5_name("eg_plds_est.h5", "u", replace)); + z.save(arma::hdf5_name("eg_plds_est.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_plds_est.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_plds_est.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_plds_est.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_plds_est.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_plds_est.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_plds_est.h5", "y_hat", replace)); + + return 0; +} + +// for generating random input +Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0) { + size_t n = Q.n_rows; + + if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { + throw std::logic_error("Q must be `n` x `n`."); + } + + Matrix x(n, n_t, arma::fill::zeros); + x.col(0) = x0; + for (size_t t = 1; t < n_t; t++) { + x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); + } + + return x; +} +``` + +_Filename: eg_plds_est.cpp_ + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md index e151a9dd..43596029 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md @@ -1,238 +1,240 @@ ---- -title: eg_plds_switched_ctrl.cpp - ---- - -# eg_plds_switched_ctrl.cpp - - - -Example Switched PLDS Control - -```cpp -//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::data_t; -using lds::Matrix; -using lds::Vector; -using std::cout; - -auto main() -> int { - cout << " ********** Example Switched Poisson LDS Control ********** \n\n"; - - // whether to do switched control - bool do_switch_ctrl = true; - - // Make SISO system sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(30.0 / dt); - - // for simulating switching - size_t which_mode = 1; - data_t pr_21 = 1e-3; // prob mode 1 -> 2 - data_t pr_12 = pr_21; // prob mode 2 -> 1 - - // simulated system being controlled - lds::poisson::System controlled_system(n_u, n_x, n_y, dt); - - // **Assume the system is not well characterized by one LDS, but is well - // characterized by two LDS models with different input matrices.** - data_t scale_sys_b = 2; - - Matrix a(n_x, n_x, arma::fill::eye); - a[0] = 0.985; - Matrix b1 = Matrix(n_x, n_u).fill(0.05); - Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); - - controlled_system.set_A(a); - controlled_system.set_B(b1); - controlled_system.set_d(d); - controlled_system.Reset(); // reset to initial conditions - - // reference - Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); - - // Let underlying system 1 be more sensitive than system 2 - Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); - - // create switched controller - lds::poisson::SwitchedController switched_controller; - lds::UniformMatrixList<> k_x; // feedback controller gains - { - // create switched controller sub-systems - // system 1 - lds::poisson::System sys1(controlled_system); - - // set process noise covariance - Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; - sys1.set_Q(q_controller); - - // adaptively estimate process disturbance (m) - // n.b. using arbitrary default value for process noise if enabled. - sys1.do_adapt_m = true; - - // setting initial mode to target to avoid large error at onset: - Vector x0_controller = arma::log(y_ref0) - d; - sys1.set_x0(x0_controller); - sys1.Reset(); // reset to initial conditions - - cout << ".....................................\n"; - cout << "sys1:\n"; - cout << ".....................................\n"; - sys1.Print(); - cout << ".....................................\n"; - - // system 2 - lds::poisson::System sys2 = sys1; - - // set parameters - sys2.set_B(b2); - - cout << ".....................................\n"; - cout << "sys2:\n"; - cout << ".....................................\n"; - sys2.Print(); - cout << ".....................................\n"; - - lds::UniformSystemList<lds::poisson::System> systems({sys1, sys2}); - - // controller gains for underlying system s: - Matrix k_x1(n_u, n_x, arma::fill::ones); - Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. - k_x = lds::UniformMatrixList<>({k_x1, k_x2}); - - data_t u_lb = 0.0; - data_t u_ub = 5.0; - switched_controller = std::move( - lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); - } - // Control variables - size_t control_type = 0; // no integral action, etc - switched_controller.set_control_type(control_type); - switched_controller.set_Kc(std::move(k_x)); - - switched_controller.set_y_ref(y_ref0); - - std::vector<lds::poisson::System> systems_vec(3, lds::poisson::System()); - lds::UniformSystemList<lds::poisson::System> systems(std::move(systems_vec)); - - cout << ".....................................\n"; - cout << "switched_controller:\n"; - cout << ".....................................\n"; - switched_controller.Print(); - cout << ".....................................\n"; - - // Fake measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // Will later contain control. - Matrix u(n_u, n_t, arma::fill::zeros); - - // create Matrix to save outputs in... - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); - - // modes and gain/disturbance params - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix mode(1, n_t, arma::fill::ones); - - // set initial val - y_hat.col(0) = switched_controller.sys().y(); - y_true.col(0) = controlled_system.y(); - x_hat.col(0) = switched_controller.sys().x(); - x_true.col(0) = controlled_system.x(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // Let the controlled system stochastically change gain - // Assume another algorithm decodes this mode change and signals the - // switched_controller - Vector chance(1, arma::fill::randu); - if (which_mode == 1) // mode1 - { - if (chance[0] < pr_21) { - which_mode = 2; - controlled_system.set_B(b2); - if (do_switch_ctrl) { - switched_controller.Switch(1); - } - } - } else { // mode2 - if (chance[0] < pr_12) { - which_mode = 1; - controlled_system.set_B(b1); - if (do_switch_ctrl) { - switched_controller.Switch(0); - } - } - } - - // Simulate the true system. - z.col(t) = controlled_system.Simulate(u.col(t - 1)); - - // perform control - u.col(t) = switched_controller.ControlOutputReference(z.col(t)); - - mode.col(t) = which_mode; - y_ref.col(t) = y_ref0; - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - y_hat.col(t) = switched_controller.sys().y(); - x_hat.col(t) = switched_controller.sys().x(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, - // x_true, m_true saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_true", replace)); - y_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_hat", replace)); - y_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_hat", replace)); - mode.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "mode", replace)); - - return 0; -} -``` - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: eg_plds_switched_ctrl.cpp +summary: Example Switched PLDS Control. + +--- + +# eg_plds_switched_ctrl.cpp + + + +Example Switched PLDS Control ```cpp + +//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::data_t; +using lds::Matrix; +using lds::Vector; +using std::cout; + +auto main() -> int { + cout << " ********** Example Switched Poisson LDS Control ********** \n\n"; + + // whether to do switched control + bool do_switch_ctrl = true; + + // Make SISO system sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(30.0 / dt); + + // for simulating switching + size_t which_mode = 1; + data_t pr_21 = 1e-3; // prob mode 1 -> 2 + data_t pr_12 = pr_21; // prob mode 2 -> 1 + + // simulated system being controlled + lds::poisson::System controlled_system(n_u, n_x, n_y, dt); + + // **Assume the system is not well characterized by one LDS, but is well + // characterized by two LDS models with different input matrices.** + data_t scale_sys_b = 2; + + Matrix a(n_x, n_x, arma::fill::eye); + a[0] = 0.985; + Matrix b1 = Matrix(n_x, n_u).fill(0.05); + Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); + + controlled_system.set_A(a); + controlled_system.set_B(b1); + controlled_system.set_d(d); + controlled_system.Reset(); // reset to initial conditions + + // reference + Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); + + // Let underlying system 1 be more sensitive than system 2 + Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); + + // create switched controller + lds::poisson::SwitchedController switched_controller; + lds::UniformMatrixList<> k_x; // feedback controller gains + { + // create switched controller sub-systems + // system 1 + lds::poisson::System sys1(controlled_system); + + // set process noise covariance + Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; + sys1.set_Q(q_controller); + + // adaptively estimate process disturbance (m) + // n.b. using arbitrary default value for process noise if enabled. + sys1.do_adapt_m = true; + + // setting initial mode to target to avoid large error at onset: + Vector x0_controller = arma::log(y_ref0) - d; + sys1.set_x0(x0_controller); + sys1.Reset(); // reset to initial conditions + + cout << ".....................................\n"; + cout << "sys1:\n"; + cout << ".....................................\n"; + sys1.Print(); + cout << ".....................................\n"; + + // system 2 + lds::poisson::System sys2 = sys1; + + // set parameters + sys2.set_B(b2); + + cout << ".....................................\n"; + cout << "sys2:\n"; + cout << ".....................................\n"; + sys2.Print(); + cout << ".....................................\n"; + + lds::UniformSystemList<lds::poisson::System> systems({sys1, sys2}); + + // controller gains for underlying system s: + Matrix k_x1(n_u, n_x, arma::fill::ones); + Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. + k_x = lds::UniformMatrixList<>({k_x1, k_x2}); + + data_t u_lb = 0.0; + data_t u_ub = 5.0; + switched_controller = std::move( + lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); + } + // Control variables + size_t control_type = 0; // no integral action, etc + switched_controller.set_control_type(control_type); + switched_controller.set_Kc(std::move(k_x)); + + switched_controller.set_y_ref(y_ref0); + + std::vector<lds::poisson::System> systems_vec(3, lds::poisson::System()); + lds::UniformSystemList<lds::poisson::System> systems(std::move(systems_vec)); + + cout << ".....................................\n"; + cout << "switched_controller:\n"; + cout << ".....................................\n"; + switched_controller.Print(); + cout << ".....................................\n"; + + // Fake measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // Will later contain control. + Matrix u(n_u, n_t, arma::fill::zeros); + + // create Matrix to save outputs in... + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); + + // modes and gain/disturbance params + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix mode(1, n_t, arma::fill::ones); + + // set initial val + y_hat.col(0) = switched_controller.sys().y(); + y_true.col(0) = controlled_system.y(); + x_hat.col(0) = switched_controller.sys().x(); + x_true.col(0) = controlled_system.x(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // Let the controlled system stochastically change gain + // Assume another algorithm decodes this mode change and signals the + // switched_controller + Vector chance(1, arma::fill::randu); + if (which_mode == 1) // mode1 + { + if (chance[0] < pr_21) { + which_mode = 2; + controlled_system.set_B(b2); + if (do_switch_ctrl) { + switched_controller.Switch(1); + } + } + } else { // mode2 + if (chance[0] < pr_12) { + which_mode = 1; + controlled_system.set_B(b1); + if (do_switch_ctrl) { + switched_controller.Switch(0); + } + } + } + + // Simulate the true system. + z.col(t) = controlled_system.Simulate(u.col(t - 1)); + + // perform control + u.col(t) = switched_controller.ControlOutputReference(z.col(t)); + + mode.col(t) = which_mode; + y_ref.col(t) = y_ref0; + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + y_hat.col(t) = switched_controller.sys().y(); + x_hat.col(t) = switched_controller.sys().x(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, + // x_true, m_true saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_true", replace)); + y_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_hat", replace)); + y_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_hat", replace)); + mode.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "mode", replace)); + + return 0; +} +``` + +_Filename: eg_plds_switched_ctrl.cpp_ + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/_index.md b/misc/docs-hugo/content/docs/api/Files/_index.md index 1626ee59..9a1a41bf 100644 --- a/misc/docs-hugo/content/docs/api/Files/_index.md +++ b/misc/docs-hugo/content/docs/api/Files/_index.md @@ -1,184 +1,184 @@ ---- -title: Files - ---- - -# Files - - - - - - - - - -- **[examples/eg_glds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp)** - - - -- **[examples/eg_glds_du_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp)** - - - -- **[examples/eg_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp)** - - - -- **[examples/eg_plds_est.cpp](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp)** - - - -- **[examples/eg_plds_switched_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp)** - - - - - - - - - - - - -- **[ldsCtrlEst_h/lds.h](/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h)** <br>`lds` namespace - - - -- **[ldsCtrlEst_h/lds_ctrl.h](/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h)** <br>Controller. - - - -- **[ldsCtrlEst_h/lds_fit.h](/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h)** <br>LDS base fit type. - - - -- **[ldsCtrlEst_h/lds_fit_em.h](/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h)** <br>subspace identification - - - -- **[ldsCtrlEst_h/lds_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h)** <br>subspace identification - - - -- **[ldsCtrlEst_h/lds_gaussian.h](/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h)** <br>`glds` namespace - - - -- **[ldsCtrlEst_h/lds_gaussian_ctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h)** <br>GLDS Controller. - - - -- **[ldsCtrlEst_h/lds_gaussian_fit.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h)** <br>GLDS fit type. - - - -- **[ldsCtrlEst_h/lds_gaussian_fit_em.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h)** <br>GLDS E-M fit type. - - - -- **[ldsCtrlEst_h/lds_gaussian_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h)** <br>GLDS SSID fit type. - - - -- **[ldsCtrlEst_h/lds_gaussian_sctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h)** <br>GLDS switched controller type. - - - -- **[ldsCtrlEst_h/lds_gaussian_sys.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h)** <br>GLDS base type. - - - -- **[ldsCtrlEst_h/lds_poisson.h](/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h)** <br>`plds` namespace - - - -- **[ldsCtrlEst_h/lds_poisson_ctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h)** <br>PLDS controller type. - - - -- **[ldsCtrlEst_h/lds_poisson_fit.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h)** <br>PLDS base fit type. - - - -- **[ldsCtrlEst_h/lds_poisson_fit_em.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h)** <br>PLDS E-M fit type. - - - -- **[ldsCtrlEst_h/lds_poisson_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h)** <br>PLDS SSID fit type. - - - -- **[ldsCtrlEst_h/lds_poisson_sctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h)** <br>PLDS switched controller type. - - - -- **[ldsCtrlEst_h/lds_poisson_sys.h](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h)** <br>PLDS base type. - - - -- **[ldsCtrlEst_h/lds_sctrl.h](/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h)** <br>SwitchedController type. - - - -- **[ldsCtrlEst_h/lds_sys.h](/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h)** <br>LDS base type. - - - -- **[ldsCtrlEst_h/lds_uniform_mats.h](/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h)** <br>List of uniformly sized matrices. - - - -- **[ldsCtrlEst_h/lds_uniform_systems.h](/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h)** <br>List of uniformly sized Systems. - - - -- **[ldsCtrlEst_h/lds_uniform_vecs.h](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h)** <br>List of uniformly sized vectors. - - - -- **[ldsCtrlEst_h/mex_c_util.h](/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h)** <br>arma <-> mex interoperability utilities (Matlab C API) - - - -- **[ldsCtrlEst_h/mex_cpp_util.h](/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h)** <br>arma <-> mex interoperability utilities (Matlab C++ API) - - - - - - - - - - - - -- **[src/lds.cpp](/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp)** <br>misc lds namespace functions - - - -- **[src/lds_gaussian_sys.cpp](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp)** <br>GLDS base type. - - - -- **[src/lds_poisson_sys.cpp](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp)** <br>PLDS base type. - - - -- **[src/lds_sys.cpp](/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp)** <br>LDS base type. - - - -- **[src/lds_uniform_vecs.cpp](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp)** <br>Uniformly sized vectors. - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time +--- +title: Files + +--- + +# Files + + + + + + + + + +- **[examples/eg_glds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp)** + + + +- **[examples/eg_glds_du_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp)** + + + +- **[examples/eg_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp)** + + + +- **[examples/eg_plds_est.cpp](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp)** + + + +- **[examples/eg_plds_switched_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp)** + + + + + + + + + + + + +- **[ldsCtrlEst_h/lds.h](/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h)** <br>`lds` namespace + + + +- **[ldsCtrlEst_h/lds_ctrl.h](/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h)** <br>Controller. + + + +- **[ldsCtrlEst_h/lds_fit.h](/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h)** <br>LDS base fit type. + + + +- **[ldsCtrlEst_h/lds_fit_em.h](/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h)** <br>subspace identification + + + +- **[ldsCtrlEst_h/lds_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h)** <br>subspace identification + + + +- **[ldsCtrlEst_h/lds_gaussian.h](/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h)** <br>`glds` namespace + + + +- **[ldsCtrlEst_h/lds_gaussian_ctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h)** <br>GLDS Controller. + + + +- **[ldsCtrlEst_h/lds_gaussian_fit.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h)** <br>GLDS fit type. + + + +- **[ldsCtrlEst_h/lds_gaussian_fit_em.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h)** <br>GLDS E-M fit type. + + + +- **[ldsCtrlEst_h/lds_gaussian_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h)** <br>GLDS SSID fit type. + + + +- **[ldsCtrlEst_h/lds_gaussian_sctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h)** <br>GLDS switched controller type. + + + +- **[ldsCtrlEst_h/lds_gaussian_sys.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h)** <br>GLDS base type. + + + +- **[ldsCtrlEst_h/lds_poisson.h](/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h)** <br>`plds` namespace + + + +- **[ldsCtrlEst_h/lds_poisson_ctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h)** <br>PLDS controller type. + + + +- **[ldsCtrlEst_h/lds_poisson_fit.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h)** <br>PLDS base fit type. + + + +- **[ldsCtrlEst_h/lds_poisson_fit_em.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h)** <br>PLDS E-M fit type. + + + +- **[ldsCtrlEst_h/lds_poisson_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h)** <br>PLDS SSID fit type. + + + +- **[ldsCtrlEst_h/lds_poisson_sctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h)** <br>PLDS switched controller type. + + + +- **[ldsCtrlEst_h/lds_poisson_sys.h](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h)** <br>PLDS base type. + + + +- **[ldsCtrlEst_h/lds_sctrl.h](/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h)** <br>SwitchedController type. + + + +- **[ldsCtrlEst_h/lds_sys.h](/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h)** <br>LDS base type. + + + +- **[ldsCtrlEst_h/lds_uniform_mats.h](/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h)** <br>List of uniformly sized matrices. + + + +- **[ldsCtrlEst_h/lds_uniform_systems.h](/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h)** <br>List of uniformly sized Systems. + + + +- **[ldsCtrlEst_h/lds_uniform_vecs.h](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h)** <br>List of uniformly sized vectors. + + + +- **[ldsCtrlEst_h/mex_c_util.h](/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h)** <br>arma <-> mex interoperability utilities (Matlab C API) + + + +- **[ldsCtrlEst_h/mex_cpp_util.h](/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h)** <br>arma <-> mex interoperability utilities (Matlab C++ API) + + + + + + + + + + + + +- **[src/lds.cpp](/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp)** <br>misc lds namespace functions + + + +- **[src/lds_gaussian_sys.cpp](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp)** <br>GLDS base type. + + + +- **[src/lds_poisson_sys.cpp](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp)** <br>PLDS base type. + + + +- **[src/lds_sys.cpp](/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp)** <br>LDS base type. + + + +- **[src/lds_uniform_vecs.cpp](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp)** <br>Uniformly sized vectors. + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md b/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md index f96b896a..b48621f7 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md @@ -1,49 +1,49 @@ ---- -title: ldsCtrlEst_h - ---- - -# ldsCtrlEst_h - - - -## Files - -| Name | -| -------------- | -| **[ldsCtrlEst_h/lds.h](/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h)** <br>`lds` namespace | -| **[ldsCtrlEst_h/lds_ctrl.h](/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h)** <br>Controller. | -| **[ldsCtrlEst_h/lds_fit.h](/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h)** <br>LDS base fit type. | -| **[ldsCtrlEst_h/lds_fit_em.h](/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h)** <br>subspace identification | -| **[ldsCtrlEst_h/lds_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h)** <br>subspace identification | -| **[ldsCtrlEst_h/lds_gaussian.h](/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h)** <br>`glds` namespace | -| **[ldsCtrlEst_h/lds_gaussian_ctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h)** <br>GLDS Controller. | -| **[ldsCtrlEst_h/lds_gaussian_fit.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h)** <br>GLDS fit type. | -| **[ldsCtrlEst_h/lds_gaussian_fit_em.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h)** <br>GLDS E-M fit type. | -| **[ldsCtrlEst_h/lds_gaussian_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h)** <br>GLDS SSID fit type. | -| **[ldsCtrlEst_h/lds_gaussian_sctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h)** <br>GLDS switched controller type. | -| **[ldsCtrlEst_h/lds_gaussian_sys.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h)** <br>GLDS base type. | -| **[ldsCtrlEst_h/lds_poisson.h](/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h)** <br>`plds` namespace | -| **[ldsCtrlEst_h/lds_poisson_ctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h)** <br>PLDS controller type. | -| **[ldsCtrlEst_h/lds_poisson_fit.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h)** <br>PLDS base fit type. | -| **[ldsCtrlEst_h/lds_poisson_fit_em.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h)** <br>PLDS E-M fit type. | -| **[ldsCtrlEst_h/lds_poisson_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h)** <br>PLDS SSID fit type. | -| **[ldsCtrlEst_h/lds_poisson_sctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h)** <br>PLDS switched controller type. | -| **[ldsCtrlEst_h/lds_poisson_sys.h](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h)** <br>PLDS base type. | -| **[ldsCtrlEst_h/lds_sctrl.h](/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h)** <br>SwitchedController type. | -| **[ldsCtrlEst_h/lds_sys.h](/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h)** <br>LDS base type. | -| **[ldsCtrlEst_h/lds_uniform_mats.h](/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h)** <br>List of uniformly sized matrices. | -| **[ldsCtrlEst_h/lds_uniform_systems.h](/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h)** <br>List of uniformly sized Systems. | -| **[ldsCtrlEst_h/lds_uniform_vecs.h](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h)** <br>List of uniformly sized vectors. | -| **[ldsCtrlEst_h/mex_c_util.h](/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h)** <br>arma <-> mex interoperability utilities (Matlab C API) | -| **[ldsCtrlEst_h/mex_cpp_util.h](/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h)** <br>arma <-> mex interoperability utilities (Matlab C++ API) | - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h + +--- + +# ldsCtrlEst_h + + + +## Files + +| Name | +| -------------- | +| **[ldsCtrlEst_h/lds.h](/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h)** <br>`lds` namespace | +| **[ldsCtrlEst_h/lds_ctrl.h](/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h)** <br>Controller. | +| **[ldsCtrlEst_h/lds_fit.h](/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h)** <br>LDS base fit type. | +| **[ldsCtrlEst_h/lds_fit_em.h](/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h)** <br>subspace identification | +| **[ldsCtrlEst_h/lds_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h)** <br>subspace identification | +| **[ldsCtrlEst_h/lds_gaussian.h](/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h)** <br>`glds` namespace | +| **[ldsCtrlEst_h/lds_gaussian_ctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h)** <br>GLDS Controller. | +| **[ldsCtrlEst_h/lds_gaussian_fit.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h)** <br>GLDS fit type. | +| **[ldsCtrlEst_h/lds_gaussian_fit_em.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h)** <br>GLDS E-M fit type. | +| **[ldsCtrlEst_h/lds_gaussian_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h)** <br>GLDS SSID fit type. | +| **[ldsCtrlEst_h/lds_gaussian_sctrl.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h)** <br>GLDS switched controller type. | +| **[ldsCtrlEst_h/lds_gaussian_sys.h](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h)** <br>GLDS base type. | +| **[ldsCtrlEst_h/lds_poisson.h](/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h)** <br>`plds` namespace | +| **[ldsCtrlEst_h/lds_poisson_ctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h)** <br>PLDS controller type. | +| **[ldsCtrlEst_h/lds_poisson_fit.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h)** <br>PLDS base fit type. | +| **[ldsCtrlEst_h/lds_poisson_fit_em.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h)** <br>PLDS E-M fit type. | +| **[ldsCtrlEst_h/lds_poisson_fit_ssid.h](/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h)** <br>PLDS SSID fit type. | +| **[ldsCtrlEst_h/lds_poisson_sctrl.h](/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h)** <br>PLDS switched controller type. | +| **[ldsCtrlEst_h/lds_poisson_sys.h](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h)** <br>PLDS base type. | +| **[ldsCtrlEst_h/lds_sctrl.h](/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h)** <br>SwitchedController type. | +| **[ldsCtrlEst_h/lds_sys.h](/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h)** <br>LDS base type. | +| **[ldsCtrlEst_h/lds_uniform_mats.h](/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h)** <br>List of uniformly sized matrices. | +| **[ldsCtrlEst_h/lds_uniform_systems.h](/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h)** <br>List of uniformly sized Systems. | +| **[ldsCtrlEst_h/lds_uniform_vecs.h](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h)** <br>List of uniformly sized vectors. | +| **[ldsCtrlEst_h/mex_c_util.h](/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h)** <br>arma <-> mex interoperability utilities (Matlab C API) | +| **[ldsCtrlEst_h/mex_cpp_util.h](/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h)** <br>arma <-> mex interoperability utilities (Matlab C++ API) | + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md b/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md index c97c28f4..767ff3ed 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md @@ -1,28 +1,28 @@ ---- -title: src - ---- - -# src - - - -## Files - -| Name | -| -------------- | -| **[src/lds.cpp](/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp)** <br>misc lds namespace functions | -| **[src/lds_gaussian_sys.cpp](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp)** <br>GLDS base type. | -| **[src/lds_poisson_sys.cpp](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp)** <br>PLDS base type. | -| **[src/lds_sys.cpp](/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp)** <br>LDS base type. | -| **[src/lds_uniform_vecs.cpp](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp)** <br>Uniformly sized vectors. | - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: src + +--- + +# src + + + +## Files + +| Name | +| -------------- | +| **[src/lds.cpp](/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp)** <br>misc lds namespace functions | +| **[src/lds_gaussian_sys.cpp](/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp)** <br>GLDS base type. | +| **[src/lds_poisson_sys.cpp](/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp)** <br>PLDS base type. | +| **[src/lds_sys.cpp](/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp)** <br>LDS base type. | +| **[src/lds_uniform_vecs.cpp](/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp)** <br>Uniformly sized vectors. | + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md b/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md index 8c4d9d91..3237c746 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md @@ -1,28 +1,28 @@ ---- -title: examples - ---- - -# examples - - - -## Files - -| Name | -| -------------- | -| **[examples/eg_glds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp)** | -| **[examples/eg_glds_du_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp)** | -| **[examples/eg_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp)** | -| **[examples/eg_plds_est.cpp](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp)** | -| **[examples/eg_plds_switched_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp)** | - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: examples + +--- + +# examples + + + +## Files + +| Name | +| -------------- | +| **[examples/eg_glds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp)** | +| **[examples/eg_glds_du_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp)** | +| **[examples/eg_plds_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp)** | +| **[examples/eg_plds_est.cpp](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp)** | +| **[examples/eg_plds_switched_ctrl.cpp](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp)** | + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md b/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md index afab6649..7874871d 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md @@ -1,24 +1,24 @@ ---- -title: include - ---- - -# include - - - -## Directories - -| Name | -| -------------- | -| **[ldsCtrlEst_h](/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/#dir-ldsctrlest-h)** | - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: include + +--- + +# include + + + +## Directories + +| Name | +| -------------- | +| **[ldsCtrlEst_h](/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/#dir-ldsctrlest-h)** | + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md index 26bbf313..935a0731 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md @@ -1,297 +1,334 @@ ---- -title: examples/eg_glds_ctrl.cpp - ---- - -# examples/eg_glds_ctrl.cpp - - - -## Functions - -| | Name | -| -------------- | -------------- | -| auto | **[main](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#function-main)**() | - - -## Function Details - -### main - -```cpp -auto main() -``` - - - -Going to simulate a switching disturbance (m) acting on system - - - - - -## Source code - -```cpp -//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::Matrix; -using lds::Vector; -using lds::data_t; -using std::cout; - -auto main() -> int { - cout << " ********** Example Gaussian LDS Control ********** \n\n"; - - // Make 1st-order SISO system, sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(5.0 / dt); - - // construct ground truth system to be controlled... - // initializes to random walk model with top-most n_y state observed - lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); - - // Ground-truth parameters for the controlled system - // (stand-in for physical system to be controlled) - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = exp(-dt / 0.01); - Matrix b_true = Matrix(n_x, n_u).fill(2e-4); - // control signal to model input unit conversion e.g., V -> mW/mm2: - Vector g_true = Vector(n_y).fill(10.0); - - // output noise covariance - Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; - - size_t which_m = 0; // whether low or high disturbance (0, 1) - data_t m_low = 5 * dt * (1 - a_true[0]); - data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. - data_t m_high = 20 * dt * (1 - a_true[0]); - data_t pr_hi2lo = pr_lo2hi; - - // initially let m be low - Vector m0_true = Vector(n_x).fill(m_low); - - // Assign params. - controlled_system.set_A(a_true); - controlled_system.set_B(b_true); - controlled_system.set_m(m0_true); - controlled_system.set_g(g_true); - controlled_system.set_R(r_true); - - cout << ".....................................\n"; - cout << "controlled_system:\n"; - cout << ".....................................\n"; - controlled_system.Print(); - cout << ".....................................\n"; - - // make a controller - lds::gaussian::Controller controller; - { - // Create **incorrect** model used for control. - // (e.g., imperfect model fitting) - Matrix b_controller = b_true / 2; - - // let's assume zero process disturbance initially - // (will be re-estimating) - Vector m_controller = Vector(n_x, arma::fill::zeros); - - // for this demo, just use arbitrary default R - Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; - - lds::gaussian::System controller_system(controlled_system); - controller_system.set_B(b_controller); - controller_system.set_m(m_controller); - controller_system.set_R(r_controller); - controller_system.Reset(); // reset to new m - - // going to adaptively re-estimate the disturbance - controller_system.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise - // acting on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; - controller_system.set_Q_m(q_m); - - // create controller - // lower and upper bounds on control signal (e.g., in Volts) - data_t u_lb = 0.0; // [=] V - data_t u_ub = 5.0; // [=] V - controller = std::move( - lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); - } - - // Control variables: - // if following enabled, adapts set point with re-estimated process - // disturbance n.b., should not need integral action if this is enabled as the - // adaptive estimator minimizes DC error - bool do_adaptive_set_point = false; - - // Reference/target output, controller gains - Vector y_ref0 = Vector(n_y).fill(20.0 * dt); - Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error - Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err - - // setting initial state to target to avoid error at onset: - Vector x0 = Vector(n_x).fill(y_ref0[0]); - - // set up controller type bit mask so controller knows how to proceed - size_t control_type = 0; - if (do_adaptive_set_point) { - // adapt set point with estimated disturbance - control_type = control_type | lds::kControlTypeAdaptM; - } else { - // use integral action to minimize DC error - control_type = control_type | lds::kControlTypeIntY; - } - - // set controller type - controller.set_control_type(control_type); - - // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): - Vector g_design = Vector(n_u).fill(9); - - // Set params. - // **n.b. using arbitrary defaults for Q, R in this example. Really, these - // should be set by users, as they tune characteristics of Kalman filter. - // Users can also choose not to recursively calculate the estimator gain and - // supply it (setKe) instead of covariances.** - controller.set_y_ref(y_ref0); - controller.set_Kc(k_x); - controller.set_Kc_inty(k_inty); - controller.set_g_design(g_design); - - cout << ".....................................\n"; - cout << "control system:\n"; - cout << ".....................................\n"; - controller.Print(); - cout << ".....................................\n"; - - // set up variables for simulation - // create Matrix to save outputs in... - Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; - - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // simulated control signal ([=] V) - Matrix u(n_u, n_t, arma::fill::zeros); - - // outputs, states and gain/disturbance params - // *_hat indicates online estimates - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_x, n_t, arma::fill::zeros); - - // *_true indicates ground truth (system being controlled) - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_x, n_t, arma::fill::zeros); - - // set initial val - y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); - y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); - - x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); - x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); - - m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); - m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // simulate a stochastically switched disturbance - Vector chance = arma::randu<Vector>(1); - if (which_m == 0) // low disturbance - { - if (chance[0] < pr_lo2hi) { // switches low -> high disturbance - m0_true = std::vector<data_t>(n_x, m_high); - which_m = 1; - } - } else { // high disturbance - if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance - m0_true = std::vector<data_t>(n_x, m_low); - which_m = 0; - } - } - controlled_system.set_m(m0_true); - - // input - Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); - - // Simulate the true system. - z.col(t) = controlled_system.Simulate(u_tm1); - - // This method uses a steady-state solution to control problem to calculate - // x_ref, u_ref from reference output y_ref. Therefore, it is only - // applicable to regulation problems or cases where reference trajectory - // changes slowly compared to system dynamics. - u.col(t) = controller.ControlOutputReference(z.col(t)); - - // save the signals - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - m_true.col(t) = controlled_system.m(); - - y_hat.col(t) = controller.sys().y(); - x_hat.col(t) = controller.sys().x(); - m_hat.col(t) = controller.sys().m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - cout << "Saving simulation data to disk.\n"; - - // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, - // xTrue, mTrue saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_glds_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_glds_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_glds_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_hat", replace)); - - cout << "fin.\n"; - return 0; -} -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: examples/eg_glds_ctrl.cpp + +--- + +# examples/eg_glds_ctrl.cpp + + + +## Types + +| | Name | +| -------------- | -------------- | +| using arma::Mat< data_t > | **[Matrix](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-matrix)** | +| using arma::Col< data_t > | **[Vector](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-vector)** | +| using double | **[data_t](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-data-t)** | + +## Functions + +| | Name | +| -------------- | -------------- | +| int | **[main](/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#function-main)**() | + +## Type Details + +### Matrix + +```cpp +using lds::Matrix = arma::Mat<data_t>; +``` + + + +### Vector + +```cpp +using lds::Vector = arma::Col<data_t>; +``` + + + +### data_t + +```cpp +using lds::data_t = double; +``` + + + +Type of all data in library. If need 32b, change `double` to `float`. This could be potentially useful for large scale problems where there are memory constraints. + + + +## Function Details + +### main + +```cpp +int main() +``` + + + +Going to simulate a switching disturbance (m) acting on system + + + + + +## Source code + +```cpp +//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout; + +auto main() -> int { + cout << " ********** Example Gaussian LDS Control ********** \n\n"; + + // Make 1st-order SISO system, sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(5.0 / dt); + + // construct ground truth system to be controlled... + // initializes to random walk model with top-most n_y state observed + lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); + + // Ground-truth parameters for the controlled system + // (stand-in for physical system to be controlled) + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = exp(-dt / 0.01); + Matrix b_true = Matrix(n_x, n_u).fill(2e-4); + // control signal to model input unit conversion e.g., V -> mW/mm2: + Vector g_true = Vector(n_y).fill(10.0); + + // output noise covariance + Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; + + size_t which_m = 0; // whether low or high disturbance (0, 1) + data_t m_low = 5 * dt * (1 - a_true[0]); + data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. + data_t m_high = 20 * dt * (1 - a_true[0]); + data_t pr_hi2lo = pr_lo2hi; + + // initially let m be low + Vector m0_true = Vector(n_x).fill(m_low); + + // Assign params. + controlled_system.set_A(a_true); + controlled_system.set_B(b_true); + controlled_system.set_m(m0_true); + controlled_system.set_g(g_true); + controlled_system.set_R(r_true); + + cout << ".....................................\n"; + cout << "controlled_system:\n"; + cout << ".....................................\n"; + controlled_system.Print(); + cout << ".....................................\n"; + + // make a controller + lds::gaussian::Controller controller; + { + // Create **incorrect** model used for control. + // (e.g., imperfect model fitting) + Matrix b_controller = b_true / 2; + + // let's assume zero process disturbance initially + // (will be re-estimating) + Vector m_controller = Vector(n_x, arma::fill::zeros); + + // for this demo, just use arbitrary default R + Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; + + lds::gaussian::System controller_system(controlled_system); + controller_system.set_B(b_controller); + controller_system.set_m(m_controller); + controller_system.set_R(r_controller); + controller_system.Reset(); // reset to new m + + // going to adaptively re-estimate the disturbance + controller_system.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise + // acting on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; + controller_system.set_Q_m(q_m); + + // create controller + // lower and upper bounds on control signal (e.g., in Volts) + data_t u_lb = 0.0; // [=] V + data_t u_ub = 5.0; // [=] V + controller = std::move( + lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); + } + + // Control variables: + // if following enabled, adapts set point with re-estimated process + // disturbance n.b., should not need integral action if this is enabled as the + // adaptive estimator minimizes DC error + bool do_adaptive_set_point = false; + + // Reference/target output, controller gains + Vector y_ref0 = Vector(n_y).fill(20.0 * dt); + Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error + Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err + + // setting initial state to target to avoid error at onset: + Vector x0 = Vector(n_x).fill(y_ref0[0]); + + // set up controller type bit mask so controller knows how to proceed + size_t control_type = 0; + if (do_adaptive_set_point) { + // adapt set point with estimated disturbance + control_type = control_type | lds::kControlTypeAdaptM; + } else { + // use integral action to minimize DC error + control_type = control_type | lds::kControlTypeIntY; + } + + // set controller type + controller.set_control_type(control_type); + + // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): + Vector g_design = Vector(n_u).fill(9); + + // Set params. + // **n.b. using arbitrary defaults for Q, R in this example. Really, these + // should be set by users, as they tune characteristics of Kalman filter. + // Users can also choose not to recursively calculate the estimator gain and + // supply it (setKe) instead of covariances.** + controller.set_y_ref(y_ref0); + controller.set_Kc(k_x); + controller.set_Kc_inty(k_inty); + controller.set_g_design(g_design); + + cout << ".....................................\n"; + cout << "control system:\n"; + cout << ".....................................\n"; + controller.Print(); + cout << ".....................................\n"; + + // set up variables for simulation + // create Matrix to save outputs in... + Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; + + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // simulated control signal ([=] V) + Matrix u(n_u, n_t, arma::fill::zeros); + + // outputs, states and gain/disturbance params + // *_hat indicates online estimates + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_x, n_t, arma::fill::zeros); + + // *_true indicates ground truth (system being controlled) + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_x, n_t, arma::fill::zeros); + + // set initial val + y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); + y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); + + x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); + x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); + + m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); + m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // simulate a stochastically switched disturbance + Vector chance = arma::randu<Vector>(1); + if (which_m == 0) // low disturbance + { + if (chance[0] < pr_lo2hi) { // switches low -> high disturbance + m0_true = std::vector<data_t>(n_x, m_high); + which_m = 1; + } + } else { // high disturbance + if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance + m0_true = std::vector<data_t>(n_x, m_low); + which_m = 0; + } + } + controlled_system.set_m(m0_true); + + // input + Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); + + // Simulate the true system. + z.col(t) = controlled_system.Simulate(u_tm1); + + // This method uses a steady-state solution to control problem to calculate + // x_ref, u_ref from reference output y_ref. Therefore, it is only + // applicable to regulation problems or cases where reference trajectory + // changes slowly compared to system dynamics. + u.col(t) = controller.ControlOutputReference(z.col(t)); + + // save the signals + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + m_true.col(t) = controlled_system.m(); + + y_hat.col(t) = controller.sys().y(); + x_hat.col(t) = controller.sys().x(); + m_hat.col(t) = controller.sys().m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + cout << "Saving simulation data to disk.\n"; + + // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, + // xTrue, mTrue saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_glds_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_glds_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_glds_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_glds_ctrl.h5", "y_hat", replace)); + + cout << "fin.\n"; + return 0; +} +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md index 71cef5d6..c5958668 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md @@ -1,298 +1,335 @@ ---- -title: examples/eg_glds_du_plds_ctrl.cpp - ---- - -# examples/eg_glds_du_plds_ctrl.cpp - - - -## Functions - -| | Name | -| -------------- | -------------- | -| auto | **[main](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#function-main)**() | - - -## Function Details - -### main - -```cpp -auto main() -``` - - - -Going to simulate a switching disturbance (m) acting on system - - - - - -## Source code - -```cpp -//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::data_t; -using lds::Matrix; -using lds::Vector; -using std::cout; - -auto main() -> int { - cout << " ********** Example Gaussian LDS du Control of PLDS ********** \n\n"; - - // Make 1st-order SISO system, sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(5.0 / dt); - - // construct ground truth system to be controlled... - // initializes to random walk model with top-most n_y state observed - lds::poisson::System controlled_system(n_u, n_x, n_y, dt); - - // Ground-truth parameters for the controlled system - // (stand-in for physical system to be controlled) - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = exp(-dt / 0.01); - Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); - // control signal to model input unit conversion e.g., V -> mW/mm2: - Vector g_true = Vector(n_y).fill(10.0); - - size_t which_m = 0; // whether low or high disturbance (0, 1) - data_t m_low = log(1 * dt) * (1 - a_true[0]); - data_t pr_lo2hi = - 0; // 1e-3; // probability of going from low to high disturb. - data_t m_high = log(20 * dt) * (1 - a_true[0]); - data_t pr_hi2lo = pr_lo2hi; - - // initially let m be low - Vector m0_true = Vector(n_x).fill(m_low); - Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); - - // Assign params. - controlled_system.set_A(a_true); - controlled_system.set_B(b_true); - controlled_system.set_m(m0_true); - controlled_system.set_g(g_true); - controlled_system.set_x0(x0_true); - controlled_system.Reset(); - - cout << ".....................................\n"; - cout << "controlled_system:\n"; - cout << ".....................................\n"; - controlled_system.Print(); - cout << ".....................................\n"; - - // make a controller - lds::gaussian::Controller controller; - { - // Create **incorrect** model used for control. - // (e.g., imperfect model fitting) - Matrix b_controller = b_true / 50; - - // let's assume zero process disturbance initially - // (will be re-estimating) - Vector m_controller = Vector(n_x, arma::fill::zeros); - - // process noise covariance - Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; - - // output noise covariance - Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; - - lds::gaussian::System controller_system(n_u, n_x, n_y, dt); - controller_system.set_A(a_true); - controller_system.set_B(b_controller); - controller_system.set_g(g_true); - controller_system.set_m(m_controller); - controller_system.set_Q(q_controller); - controller_system.set_R(r_controller); - controller_system.Reset(); // reset to new m - - // going to adaptively re-estimate the disturbance - controller_system.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise - // acting on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; - controller_system.set_Q_m(q_m); - - // create controller - // lower and upper bounds on control signal (e.g., in Volts) - data_t u_lb = 0.0; // [=] V - data_t u_ub = 5.0; // [=] V - controller = std::move( - lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); - } - - // Control variables: - // Reference/target output, controller gains - Vector y_ref0 = Vector(n_y).fill(20.0 * dt); - - // to design for this example, augmented state with control and made the input - // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = - // 1e-2. - Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error - Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err - Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp - - // set up controller type bit mask so controller knows how to proceed - size_t control_type = 0; - // use integral action to minimize DC error - control_type = control_type | lds::kControlTypeIntY; - // update change in control (LP filters control) - control_type = control_type | lds::kControlTypeDeltaU; - - // set controller type - controller.set_control_type(control_type); - - // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): - Vector g_design = Vector(n_u).fill(10); - - // Set params. - // **n.b. using arbitrary defaults for Q, R in this example. Really, these - // should be set by users, as they tune characteristics of Kalman filter. - // Users can also choose not to recursively calculate the estimator gain and - // supply it (setKe) instead of covariances.** - controller.set_y_ref(y_ref0); - controller.set_Kc(k_x); - controller.set_Kc_inty(k_inty); - controller.set_Kc_u(k_u); - controller.set_g_design(g_design); - - cout << ".....................................\n"; - cout << "control system:\n"; - cout << ".....................................\n"; - controller.Print(); - cout << ".....................................\n"; - - // set up variables for simulation - // create Matrix to save outputs in... - Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; - - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // simulated control signal ([=] V) - Matrix u(n_u, n_t, arma::fill::zeros); - - // outputs, states and gain/disturbance params - // *_hat indicates online estimates - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_x, n_t, arma::fill::zeros); - - // *_true indicates ground truth (system being controlled) - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_x, n_t, arma::fill::zeros); - - // get initial val - y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); - y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); - - x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); - x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); - - m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); - m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // simulate a stochastically switched disturbance - Vector chance = arma::randu<Vector>(1); - if (which_m == 0) // low disturbance - { - if (chance[0] < pr_lo2hi) { // switches low -> high disturbance - m0_true = std::vector<data_t>(n_x, m_high); - which_m = 1; - } - } else { // high disturbance - if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance - m0_true = std::vector<data_t>(n_x, m_low); - which_m = 0; - } - } - controlled_system.set_m(m0_true); - - // input - Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); - - // Simulate the true system. - z.col(t) = controlled_system.Simulate(u_tm1); - - // This method uses a steady-state solution to control problem to calculate - // x_ref, u_ref from reference output y_ref. Therefore, it is only - // applicable to regulation problems or cases where reference trajectory - // changes slowly compared to system dynamics. - u.col(t) = controller.ControlOutputReference(z.col(t)); - - // save the signals - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - m_true.col(t) = controlled_system.m(); - - y_hat.col(t) = controller.sys().y(); - x_hat.col(t) = controller.sys().x(); - m_hat.col(t) = controller.sys().m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - cout << "Saving simulation data to disk.\n"; - - // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, - // xTrue, mTrue saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_hat", replace)); - - cout << "fin.\n"; - return 0; -} -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: examples/eg_glds_du_plds_ctrl.cpp + +--- + +# examples/eg_glds_du_plds_ctrl.cpp + + + +## Types + +| | Name | +| -------------- | -------------- | +| using double | **[data_t](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-data-t)** | +| using arma::Mat< data_t > | **[Matrix](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-matrix)** | +| using arma::Col< data_t > | **[Vector](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-vector)** | + +## Functions + +| | Name | +| -------------- | -------------- | +| int | **[main](/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#function-main)**() | + +## Type Details + +### data_t + +```cpp +using lds::data_t = double; +``` + + + +Type of all data in library. If need 32b, change `double` to `float`. This could be potentially useful for large scale problems where there are memory constraints. + + +### Matrix + +```cpp +using lds::Matrix = arma::Mat<data_t>; +``` + + + +### Vector + +```cpp +using lds::Vector = arma::Col<data_t>; +``` + + + + +## Function Details + +### main + +```cpp +int main() +``` + + + +Going to simulate a switching disturbance (m) acting on system + + + + + +## Source code + +```cpp +//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::data_t; +using lds::Matrix; +using lds::Vector; +using std::cout; + +auto main() -> int { + cout << " ********** Example Gaussian LDS du Control of PLDS ********** \n\n"; + + // Make 1st-order SISO system, sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(5.0 / dt); + + // construct ground truth system to be controlled... + // initializes to random walk model with top-most n_y state observed + lds::poisson::System controlled_system(n_u, n_x, n_y, dt); + + // Ground-truth parameters for the controlled system + // (stand-in for physical system to be controlled) + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = exp(-dt / 0.01); + Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); + // control signal to model input unit conversion e.g., V -> mW/mm2: + Vector g_true = Vector(n_y).fill(10.0); + + size_t which_m = 0; // whether low or high disturbance (0, 1) + data_t m_low = log(1 * dt) * (1 - a_true[0]); + data_t pr_lo2hi = + 0; // 1e-3; // probability of going from low to high disturb. + data_t m_high = log(20 * dt) * (1 - a_true[0]); + data_t pr_hi2lo = pr_lo2hi; + + // initially let m be low + Vector m0_true = Vector(n_x).fill(m_low); + Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); + + // Assign params. + controlled_system.set_A(a_true); + controlled_system.set_B(b_true); + controlled_system.set_m(m0_true); + controlled_system.set_g(g_true); + controlled_system.set_x0(x0_true); + controlled_system.Reset(); + + cout << ".....................................\n"; + cout << "controlled_system:\n"; + cout << ".....................................\n"; + controlled_system.Print(); + cout << ".....................................\n"; + + // make a controller + lds::gaussian::Controller controller; + { + // Create **incorrect** model used for control. + // (e.g., imperfect model fitting) + Matrix b_controller = b_true / 50; + + // let's assume zero process disturbance initially + // (will be re-estimating) + Vector m_controller = Vector(n_x, arma::fill::zeros); + + // process noise covariance + Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; + + // output noise covariance + Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; + + lds::gaussian::System controller_system(n_u, n_x, n_y, dt); + controller_system.set_A(a_true); + controller_system.set_B(b_controller); + controller_system.set_g(g_true); + controller_system.set_m(m_controller); + controller_system.set_Q(q_controller); + controller_system.set_R(r_controller); + controller_system.Reset(); // reset to new m + + // going to adaptively re-estimate the disturbance + controller_system.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise + // acting on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; + controller_system.set_Q_m(q_m); + + // create controller + // lower and upper bounds on control signal (e.g., in Volts) + data_t u_lb = 0.0; // [=] V + data_t u_ub = 5.0; // [=] V + controller = std::move( + lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); + } + + // Control variables: + // Reference/target output, controller gains + Vector y_ref0 = Vector(n_y).fill(20.0 * dt); + + // to design for this example, augmented state with control and made the input + // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = + // 1e-2. + Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error + Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err + Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp + + // set up controller type bit mask so controller knows how to proceed + size_t control_type = 0; + // use integral action to minimize DC error + control_type = control_type | lds::kControlTypeIntY; + // update change in control (LP filters control) + control_type = control_type | lds::kControlTypeDeltaU; + + // set controller type + controller.set_control_type(control_type); + + // Let's say these controller gains were designed assuming g was 9 V/(mW/mm2): + Vector g_design = Vector(n_u).fill(10); + + // Set params. + // **n.b. using arbitrary defaults for Q, R in this example. Really, these + // should be set by users, as they tune characteristics of Kalman filter. + // Users can also choose not to recursively calculate the estimator gain and + // supply it (setKe) instead of covariances.** + controller.set_y_ref(y_ref0); + controller.set_Kc(k_x); + controller.set_Kc_inty(k_inty); + controller.set_Kc_u(k_u); + controller.set_g_design(g_design); + + cout << ".....................................\n"; + cout << "control system:\n"; + cout << ".....................................\n"; + controller.Print(); + cout << ".....................................\n"; + + // set up variables for simulation + // create Matrix to save outputs in... + Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; + + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // simulated control signal ([=] V) + Matrix u(n_u, n_t, arma::fill::zeros); + + // outputs, states and gain/disturbance params + // *_hat indicates online estimates + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_x, n_t, arma::fill::zeros); + + // *_true indicates ground truth (system being controlled) + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_x, n_t, arma::fill::zeros); + + // get initial val + y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); + y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); + + x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); + x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); + + m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); + m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // simulate a stochastically switched disturbance + Vector chance = arma::randu<Vector>(1); + if (which_m == 0) // low disturbance + { + if (chance[0] < pr_lo2hi) { // switches low -> high disturbance + m0_true = std::vector<data_t>(n_x, m_high); + which_m = 1; + } + } else { // high disturbance + if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance + m0_true = std::vector<data_t>(n_x, m_low); + which_m = 0; + } + } + controlled_system.set_m(m0_true); + + // input + Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); + + // Simulate the true system. + z.col(t) = controlled_system.Simulate(u_tm1); + + // This method uses a steady-state solution to control problem to calculate + // x_ref, u_ref from reference output y_ref. Therefore, it is only + // applicable to regulation problems or cases where reference trajectory + // changes slowly compared to system dynamics. + u.col(t) = controller.ControlOutputReference(z.col(t)); + + // save the signals + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + m_true.col(t) = controlled_system.m(); + + y_hat.col(t) = controller.sys().y(); + x_hat.col(t) = controller.sys().x(); + m_hat.col(t) = controller.sys().m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + cout << "Saving simulation data to disk.\n"; + + // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, + // xTrue, mTrue saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_glds_du_plds_ctrl.h5", "y_hat", replace)); + + cout << "fin.\n"; + return 0; +} +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md index 87aab564..1fc2bc79 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md @@ -1,267 +1,304 @@ ---- -title: examples/eg_plds_ctrl.cpp - ---- - -# examples/eg_plds_ctrl.cpp - - - -## Functions - -| | Name | -| -------------- | -------------- | -| auto | **[main](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#function-main)**() | - - -## Function Details - -### main - -```cpp -auto main() -``` - - - -Going to simulate a switching disturbance (m) acting on system - - - - - -## Source code - -```cpp -//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::Matrix; -using lds::Vector; -using lds::data_t; -using std::cout; - -auto main() -> int { - cout << " ********** Example Poisson LDS Control ********** \n\n"; - - // Make SISO system sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(10.0 / dt); - - // Control variables: _reference/target output, controller gains - // n.b., Can either use Vector (arma::Col) or std::vector - Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; - Matrix k_x = - Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error - Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + - 10; // gains on integrated output err - - // Set control type bit mask, so controller knows what to do - size_t control_type = lds::kControlTypeIntY; // integral action - - // Ground-truth parameters for the controlled system - // (stand-in for physical system to be controlled) - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = 0.986; - Matrix b_true(n_x, n_u, arma::fill::zeros); - b_true[0] = 0.054; - Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); - - size_t which_m = 0; - data_t m_low = log(1 * dt) * (1 - a_true[0]); - data_t pr_lo2hi = 1e-3; - data_t m_high = log(20 * dt) * (1 - a_true[0]); - data_t pr_hi2lo = pr_lo2hi; - - Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; - // construct ground truth system to be controlled... - lds::poisson::System controlled_system(n_u, n_x, n_y, dt); - - // Assign params. - controlled_system.set_A(a_true); - controlled_system.set_B(b_true); - controlled_system.set_m(m0_true); - controlled_system.set_x0(x0_true); - // reset to initial conditions - controlled_system.Reset(); - - cout << ".....................................\n"; - cout << "controlled_system:\n"; - cout << ".....................................\n"; - controlled_system.Print(); - cout << ".....................................\n"; - - // Create the controller - lds::poisson::Controller controller; - { - // Create model used for control. - lds::poisson::System controller_system(controlled_system); - - // for this example, assume model correct, except disturbance - Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; - Vector x0_controller = arma::log(y_ref0); - controller_system.set_m(m0_controller); - controller_system.set_x0(x0_controller); - controller_system.Reset(); //reset to new init condition - - // adaptively re-estimate process disturbance (m) - controller_system.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise - // acting on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; - controller_system.set_Q_m(q_m); - - data_t u_lb = 0.0; - data_t u_ub = 5.0; - controller = std::move( - lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); - } - // set controller type - controller.set_control_type(control_type); - - // set controller gains - controller.set_Kc(k_x); - controller.set_Kc_inty(k_inty); - - // to protect against integral windup when output is consistently above - // target: - data_t tau_awu(0.1); - controller.set_tau_awu(tau_awu); - - cout << ".....................................\n"; - cout << "controller:\n"; - cout << ".....................................\n"; - controller.Print(); - cout << ".....................................\n"; - - // create Matrix to save outputs in... - Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); - y_ref.each_col() += y_ref0; - - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // simulated control signal ([=] V) - Matrix u(n_u, n_t, arma::fill::zeros); - - // outputs, states and gain/disturbance params - // *_hat indicates online estimates - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_y, n_t, arma::fill::zeros); - - // *_true indicates ground truth (system being controlled) - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_y, n_t, arma::fill::zeros); - - // set initial val - y_hat.col(0) = controller.sys().y(); - y_true.col(0) = controlled_system.y(); - - x_hat.col(0) = controller.sys().x(); - x_true.col(0) = controlled_system.x(); - - m_hat.col(0) = controller.sys().m(); - m_true.col(0) = controlled_system.m(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // simulate a stochastically switched disturbance - Vector chance = arma::randu<Vector>(1); - if (which_m == 0) // low disturbance - { - if (chance[0] < pr_lo2hi) { // switches low -> high disturbance - m0_true = std::vector<data_t>(n_x, m_high); - which_m = 1; - } - } else { // high disturbance - if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance - m0_true = std::vector<data_t>(n_x, m_low); - which_m = 0; - } - } - controlled_system.set_m(m0_true); - - // e.g., use sinusoidal reference - data_t f = 0.5; // freq [=] Hz - Vector t_vec = Vector(n_y, arma::fill::ones) * t; - y_ref.col(t) += - y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); - - // Simulate the true system. - z.col(t)=controlled_system.Simulate(u.col(t-1)); - - // This method uses a steady-state solution to control problem to calculate - // x_ref, u_ref from reference output y_ref. Notably, it does this in the - // log-linear space (i.e., log(y)). - // - // Therefore, it is only applicable to regulation problems or cases where - // reference trajectory changes slowly compared to system dynamics. - controller.set_y_ref(y_ref.col(t)); - u.col(t)=controller.ControlOutputReference(z.col(t)); - - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - m_true.col(t) = controlled_system.m(); - - y_hat.col(t) = controller.sys().y(); - x_hat.col(t) = controller.sys().x(); - m_hat.col(t) = controller.sys().m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, - // x_true, m_true saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_plds_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_plds_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_plds_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_hat", replace)); - - return 0; -} -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: examples/eg_plds_ctrl.cpp + +--- + +# examples/eg_plds_ctrl.cpp + + + +## Types + +| | Name | +| -------------- | -------------- | +| using arma::Mat< data_t > | **[Matrix](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-matrix)** | +| using arma::Col< data_t > | **[Vector](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-vector)** | +| using double | **[data_t](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-data-t)** | + +## Functions + +| | Name | +| -------------- | -------------- | +| int | **[main](/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#function-main)**() | + +## Type Details + +### Matrix + +```cpp +using lds::Matrix = arma::Mat<data_t>; +``` + + + +### Vector + +```cpp +using lds::Vector = arma::Col<data_t>; +``` + + + +### data_t + +```cpp +using lds::data_t = double; +``` + + + +Type of all data in library. If need 32b, change `double` to `float`. This could be potentially useful for large scale problems where there are memory constraints. + + + +## Function Details + +### main + +```cpp +int main() +``` + + + +Going to simulate a switching disturbance (m) acting on system + + + + + +## Source code + +```cpp +//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout; + +auto main() -> int { + cout << " ********** Example Poisson LDS Control ********** \n\n"; + + // Make SISO system sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(10.0 / dt); + + // Control variables: _reference/target output, controller gains + // n.b., Can either use Vector (arma::Col) or std::vector + Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; + Matrix k_x = + Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error + Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + + 10; // gains on integrated output err + + // Set control type bit mask, so controller knows what to do + size_t control_type = lds::kControlTypeIntY; // integral action + + // Ground-truth parameters for the controlled system + // (stand-in for physical system to be controlled) + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = 0.986; + Matrix b_true(n_x, n_u, arma::fill::zeros); + b_true[0] = 0.054; + Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); + + size_t which_m = 0; + data_t m_low = log(1 * dt) * (1 - a_true[0]); + data_t pr_lo2hi = 1e-3; + data_t m_high = log(20 * dt) * (1 - a_true[0]); + data_t pr_hi2lo = pr_lo2hi; + + Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; + // construct ground truth system to be controlled... + lds::poisson::System controlled_system(n_u, n_x, n_y, dt); + + // Assign params. + controlled_system.set_A(a_true); + controlled_system.set_B(b_true); + controlled_system.set_m(m0_true); + controlled_system.set_x0(x0_true); + // reset to initial conditions + controlled_system.Reset(); + + cout << ".....................................\n"; + cout << "controlled_system:\n"; + cout << ".....................................\n"; + controlled_system.Print(); + cout << ".....................................\n"; + + // Create the controller + lds::poisson::Controller controller; + { + // Create model used for control. + lds::poisson::System controller_system(controlled_system); + + // for this example, assume model correct, except disturbance + Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; + Vector x0_controller = arma::log(y_ref0); + controller_system.set_m(m0_controller); + controller_system.set_x0(x0_controller); + controller_system.Reset(); //reset to new init condition + + // adaptively re-estimate process disturbance (m) + controller_system.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise + // acting on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; + controller_system.set_Q_m(q_m); + + data_t u_lb = 0.0; + data_t u_ub = 5.0; + controller = std::move( + lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); + } + // set controller type + controller.set_control_type(control_type); + + // set controller gains + controller.set_Kc(k_x); + controller.set_Kc_inty(k_inty); + + // to protect against integral windup when output is consistently above + // target: + data_t tau_awu(0.1); + controller.set_tau_awu(tau_awu); + + cout << ".....................................\n"; + cout << "controller:\n"; + cout << ".....................................\n"; + controller.Print(); + cout << ".....................................\n"; + + // create Matrix to save outputs in... + Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); + y_ref.each_col() += y_ref0; + + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // simulated control signal ([=] V) + Matrix u(n_u, n_t, arma::fill::zeros); + + // outputs, states and gain/disturbance params + // *_hat indicates online estimates + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_y, n_t, arma::fill::zeros); + + // *_true indicates ground truth (system being controlled) + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_y, n_t, arma::fill::zeros); + + // set initial val + y_hat.col(0) = controller.sys().y(); + y_true.col(0) = controlled_system.y(); + + x_hat.col(0) = controller.sys().x(); + x_true.col(0) = controlled_system.x(); + + m_hat.col(0) = controller.sys().m(); + m_true.col(0) = controlled_system.m(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // simulate a stochastically switched disturbance + Vector chance = arma::randu<Vector>(1); + if (which_m == 0) // low disturbance + { + if (chance[0] < pr_lo2hi) { // switches low -> high disturbance + m0_true = std::vector<data_t>(n_x, m_high); + which_m = 1; + } + } else { // high disturbance + if (chance[0] < pr_hi2lo) { // swithces high -> low disturbance + m0_true = std::vector<data_t>(n_x, m_low); + which_m = 0; + } + } + controlled_system.set_m(m0_true); + + // e.g., use sinusoidal reference + data_t f = 0.5; // freq [=] Hz + Vector t_vec = Vector(n_y, arma::fill::ones) * t; + y_ref.col(t) += + y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); + + // Simulate the true system. + z.col(t)=controlled_system.Simulate(u.col(t-1)); + + // This method uses a steady-state solution to control problem to calculate + // x_ref, u_ref from reference output y_ref. Notably, it does this in the + // log-linear space (i.e., log(y)). + // + // Therefore, it is only applicable to regulation problems or cases where + // reference trajectory changes slowly compared to system dynamics. + controller.set_y_ref(y_ref.col(t)); + u.col(t)=controller.ControlOutputReference(z.col(t)); + + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + m_true.col(t) = controlled_system.m(); + + y_hat.col(t) = controller.sys().y(); + x_hat.col(t) = controller.sys().x(); + m_hat.col(t) = controller.sys().m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, + // x_true, m_true saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_plds_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_plds_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_plds_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_plds_ctrl.h5", "y_hat", replace)); + + return 0; +} +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md index fda45792..8114f4e8 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md @@ -1,227 +1,264 @@ ---- -title: examples/eg_plds_est.cpp - ---- - -# examples/eg_plds_est.cpp - - - -## Functions - -| | Name | -| -------------- | -------------- | -| Matrix | **[random_walk](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-random-walk)**(size_t n_t, const Matrix & Q, const Vector & x0) | -| int | **[main](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-main)**() | - - -## Function Details - -### random_walk - -```cpp -Matrix random_walk( - size_t n_t, - const Matrix & Q, - const Vector & x0 -) -``` - - - -### main - -```cpp -int main() -``` - - - - - - -## Source code - -```cpp -//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::Matrix; -using lds::Vector; -using lds::data_t; -using std::cout; - -// for generating random input -Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0); - -int main() { - cout << " ********** Example Poisson LDS Estimation ********** \n\n"; - - // Make SISO system sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; // no. inputs - size_t n_x = 1; // no. states - size_t n_y = 1; // no. outputs - auto n_t = static_cast<size_t>(30 / dt); // no time steps for simulation. - - // construct ground truth system... - lds::poisson::System system_true(n_u, n_x, n_y, dt); - - // Model parameters - Matrix a_true(n_x, n_x, arma::fill::eye); - a_true[0] = exp(-dt / 0.075); - Matrix b_true = Matrix(n_x, n_u).fill(1e-2); - Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance - Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - - a_true); // initial state - - // Assign params. - system_true.set_A(a_true); - system_true.set_B(b_true); - system_true.set_x0(x0_true); - system_true.set_m(m0_true); - system_true.Reset(); - - // Construct system for estimation - // e.g., will create a model with incorrect disturbance - lds::poisson::System system_estimator(n_u, n_x, n_y, dt); - - // Can copy parameters from another system object - system_estimator = system_true; - - // wrong disturbance - Vector m0_est = m0_true * 2; - system_estimator.set_m(m0_est); - - // set new initial conditions - Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - - a_true); // initial state - system_estimator.set_x0(x0_est); - system_estimator.Reset(); // reset to initial condition. - - // turn on adaptive disturbance estimation - system_estimator.do_adapt_m = true; - - // set adaptation rate by changing covariance of assumed process noise acting - // on random-walk evolution of m - Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; - system_estimator.set_Q_m(q_m); - - cout << ".....................................\n"; - cout << "estimator:\n"; - cout << ".....................................\n"; - system_estimator.Print(); - cout << ".....................................\n"; - - // Set up simulation : - // Simulated measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // Stimulus (generate random stimulus) - Matrix q_u = - Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk - Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); - - // create matrix to save outputs in... - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix y_true(n_y, n_t, arma::fill::zeros); - - // states and disturbance params - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix m_hat(n_x, n_t, arma::fill::zeros); - - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix m_true(n_x, n_t, arma::fill::zeros); - - // initial conditions - y_hat.col(0) = system_estimator.y(); - y_true.col(0) = system_true.y(); - x_hat.col(0) = system_estimator.x(); - x_true.col(0) = system_true.x(); - m_hat.col(0) = system_estimator.m(); - m_true.col(0) = system_true.m(); - - cout << "Starting " << n_t * dt << " sec simlation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // Simlate the true system. - z.col(t) = system_true.Simulate(u.col(t - 1)); - - // Filter (predict -> update) - system_estimator.Filter(u.col(t - 1), z.col(t)); - - // save signals - y_hat.col(t) = system_estimator.y(); - y_true.col(t) = system_true.y(); - - x_true.col(t) = system_true.x(); - m_true.col(t) = system_true.m(); - - x_hat.col(t) = system_estimator.x(); - m_hat.col(t) = system_estimator.m(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simlation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, - // x_true, m_true saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_plds_est.h5", "dt")); - u.save(arma::hdf5_name("eg_plds_est.h5", "u", replace)); - z.save(arma::hdf5_name("eg_plds_est.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_plds_est.h5", "x_true", replace)); - m_true.save(arma::hdf5_name("eg_plds_est.h5", "m_true", replace)); - y_true.save(arma::hdf5_name("eg_plds_est.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_plds_est.h5", "x_hat", replace)); - m_hat.save(arma::hdf5_name("eg_plds_est.h5", "m_hat", replace)); - y_hat.save(arma::hdf5_name("eg_plds_est.h5", "y_hat", replace)); - - return 0; -} - -// for generating random input -Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0) { - size_t n = Q.n_rows; - - if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { - throw std::logic_error("Q must be `n` x `n`."); - } - - Matrix x(n, n_t, arma::fill::zeros); - x.col(0) = x0; - for (size_t t = 1; t < n_t; t++) { - x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); - } - - return x; -} -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: examples/eg_plds_est.cpp + +--- + +# examples/eg_plds_est.cpp + + + +## Types + +| | Name | +| -------------- | -------------- | +| using arma::Mat< data_t > | **[Matrix](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-matrix)** | +| using arma::Col< data_t > | **[Vector](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-vector)** | +| using double | **[data_t](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-data-t)** | + +## Functions + +| | Name | +| -------------- | -------------- | +| Matrix | **[random_walk](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-random-walk)**(size_t n_t, const Matrix & Q, const Vector & x0) | +| int | **[main](/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-main)**() | + +## Type Details + +### Matrix + +```cpp +using lds::Matrix = arma::Mat<data_t>; +``` + + + +### Vector + +```cpp +using lds::Vector = arma::Col<data_t>; +``` + + + +### data_t + +```cpp +using lds::data_t = double; +``` + + + +Type of all data in library. If need 32b, change `double` to `float`. This could be potentially useful for large scale problems where there are memory constraints. + + + +## Function Details + +### random_walk + +```cpp +Matrix random_walk( + size_t n_t, + const Matrix & Q, + const Vector & x0 +) +``` + + + +### main + +```cpp +int main() +``` + + + + + + +## Source code + +```cpp +//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout; + +// for generating random input +Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0); + +int main() { + cout << " ********** Example Poisson LDS Estimation ********** \n\n"; + + // Make SISO system sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; // no. inputs + size_t n_x = 1; // no. states + size_t n_y = 1; // no. outputs + auto n_t = static_cast<size_t>(30 / dt); // no time steps for simulation. + + // construct ground truth system... + lds::poisson::System system_true(n_u, n_x, n_y, dt); + + // Model parameters + Matrix a_true(n_x, n_x, arma::fill::eye); + a_true[0] = exp(-dt / 0.075); + Matrix b_true = Matrix(n_x, n_u).fill(1e-2); + Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance + Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - + a_true); // initial state + + // Assign params. + system_true.set_A(a_true); + system_true.set_B(b_true); + system_true.set_x0(x0_true); + system_true.set_m(m0_true); + system_true.Reset(); + + // Construct system for estimation + // e.g., will create a model with incorrect disturbance + lds::poisson::System system_estimator(n_u, n_x, n_y, dt); + + // Can copy parameters from another system object + system_estimator = system_true; + + // wrong disturbance + Vector m0_est = m0_true * 2; + system_estimator.set_m(m0_est); + + // set new initial conditions + Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - + a_true); // initial state + system_estimator.set_x0(x0_est); + system_estimator.Reset(); // reset to initial condition. + + // turn on adaptive disturbance estimation + system_estimator.do_adapt_m = true; + + // set adaptation rate by changing covariance of assumed process noise acting + // on random-walk evolution of m + Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; + system_estimator.set_Q_m(q_m); + + cout << ".....................................\n"; + cout << "estimator:\n"; + cout << ".....................................\n"; + system_estimator.Print(); + cout << ".....................................\n"; + + // Set up simulation : + // Simulated measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // Stimulus (generate random stimulus) + Matrix q_u = + Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk + Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); + + // create matrix to save outputs in... + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix y_true(n_y, n_t, arma::fill::zeros); + + // states and disturbance params + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix m_hat(n_x, n_t, arma::fill::zeros); + + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix m_true(n_x, n_t, arma::fill::zeros); + + // initial conditions + y_hat.col(0) = system_estimator.y(); + y_true.col(0) = system_true.y(); + x_hat.col(0) = system_estimator.x(); + x_true.col(0) = system_true.x(); + m_hat.col(0) = system_estimator.m(); + m_true.col(0) = system_true.m(); + + cout << "Starting " << n_t * dt << " sec simlation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // Simlate the true system. + z.col(t) = system_true.Simulate(u.col(t - 1)); + + // Filter (predict -> update) + system_estimator.Filter(u.col(t - 1), z.col(t)); + + // save signals + y_hat.col(t) = system_estimator.y(); + y_true.col(t) = system_true.y(); + + x_true.col(t) = system_true.x(); + m_true.col(t) = system_true.m(); + + x_hat.col(t) = system_estimator.x(); + m_hat.col(t) = system_estimator.m(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simlation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, + // x_true, m_true saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_plds_est.h5", "dt")); + u.save(arma::hdf5_name("eg_plds_est.h5", "u", replace)); + z.save(arma::hdf5_name("eg_plds_est.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_plds_est.h5", "x_true", replace)); + m_true.save(arma::hdf5_name("eg_plds_est.h5", "m_true", replace)); + y_true.save(arma::hdf5_name("eg_plds_est.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_plds_est.h5", "x_hat", replace)); + m_hat.save(arma::hdf5_name("eg_plds_est.h5", "m_hat", replace)); + y_hat.save(arma::hdf5_name("eg_plds_est.h5", "y_hat", replace)); + + return 0; +} + +// for generating random input +Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0) { + size_t n = Q.n_rows; + + if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { + throw std::logic_error("Q must be `n` x `n`."); + } + + Matrix x(n, n_t, arma::fill::zeros); + x.col(0) = x0; + for (size_t t = 1; t < n_t; t++) { + x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); + } + + return x; +} +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md index 361148d0..540db8ea 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md @@ -1,259 +1,296 @@ ---- -title: examples/eg_plds_switched_ctrl.cpp - ---- - -# examples/eg_plds_switched_ctrl.cpp - - - -## Functions - -| | Name | -| -------------- | -------------- | -| auto | **[main](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#function-main)**() | - - -## Function Details - -### main - -```cpp -auto main() -``` - - - - - - -## Source code - -```cpp -//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst> - -using lds::data_t; -using lds::Matrix; -using lds::Vector; -using std::cout; - -auto main() -> int { - cout << " ********** Example Switched Poisson LDS Control ********** \n\n"; - - // whether to do switched control - bool do_switch_ctrl = true; - - // Make SISO system sampled at 1kHz - data_t dt = 1e-3; - size_t n_u = 1; - size_t n_x = 1; - size_t n_y = 1; - - // no time steps for simulation. - auto n_t = static_cast<size_t>(30.0 / dt); - - // for simulating switching - size_t which_mode = 1; - data_t pr_21 = 1e-3; // prob mode 1 -> 2 - data_t pr_12 = pr_21; // prob mode 2 -> 1 - - // simulated system being controlled - lds::poisson::System controlled_system(n_u, n_x, n_y, dt); - - // **Assume the system is not well characterized by one LDS, but is well - // characterized by two LDS models with different input matrices.** - data_t scale_sys_b = 2; - - Matrix a(n_x, n_x, arma::fill::eye); - a[0] = 0.985; - Matrix b1 = Matrix(n_x, n_u).fill(0.05); - Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); - - controlled_system.set_A(a); - controlled_system.set_B(b1); - controlled_system.set_d(d); - controlled_system.Reset(); // reset to initial conditions - - // reference - Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); - - // Let underlying system 1 be more sensitive than system 2 - Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); - - // create switched controller - lds::poisson::SwitchedController switched_controller; - lds::UniformMatrixList<> k_x; // feedback controller gains - { - // create switched controller sub-systems - // system 1 - lds::poisson::System sys1(controlled_system); - - // set process noise covariance - Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; - sys1.set_Q(q_controller); - - // adaptively estimate process disturbance (m) - // n.b. using arbitrary default value for process noise if enabled. - sys1.do_adapt_m = true; - - // setting initial mode to target to avoid large error at onset: - Vector x0_controller = arma::log(y_ref0) - d; - sys1.set_x0(x0_controller); - sys1.Reset(); // reset to initial conditions - - cout << ".....................................\n"; - cout << "sys1:\n"; - cout << ".....................................\n"; - sys1.Print(); - cout << ".....................................\n"; - - // system 2 - lds::poisson::System sys2 = sys1; - - // set parameters - sys2.set_B(b2); - - cout << ".....................................\n"; - cout << "sys2:\n"; - cout << ".....................................\n"; - sys2.Print(); - cout << ".....................................\n"; - - lds::UniformSystemList<lds::poisson::System> systems({sys1, sys2}); - - // controller gains for underlying system s: - Matrix k_x1(n_u, n_x, arma::fill::ones); - Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. - k_x = lds::UniformMatrixList<>({k_x1, k_x2}); - - data_t u_lb = 0.0; - data_t u_ub = 5.0; - switched_controller = std::move( - lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); - } - // Control variables - size_t control_type = 0; // no integral action, etc - switched_controller.set_control_type(control_type); - switched_controller.set_Kc(std::move(k_x)); - - switched_controller.set_y_ref(y_ref0); - - std::vector<lds::poisson::System> systems_vec(3, lds::poisson::System()); - lds::UniformSystemList<lds::poisson::System> systems(std::move(systems_vec)); - - cout << ".....................................\n"; - cout << "switched_controller:\n"; - cout << ".....................................\n"; - switched_controller.Print(); - cout << ".....................................\n"; - - // Fake measurements - Matrix z(n_y, n_t, arma::fill::zeros); - - // Will later contain control. - Matrix u(n_u, n_t, arma::fill::zeros); - - // create Matrix to save outputs in... - Matrix y_hat(n_y, n_t, arma::fill::zeros); - Matrix y_true(n_y, n_t, arma::fill::zeros); - Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); - - // modes and gain/disturbance params - Matrix x_hat(n_x, n_t, arma::fill::zeros); - Matrix x_true(n_x, n_t, arma::fill::zeros); - Matrix mode(1, n_t, arma::fill::ones); - - // set initial val - y_hat.col(0) = switched_controller.sys().y(); - y_true.col(0) = controlled_system.y(); - x_hat.col(0) = switched_controller.sys().x(); - x_true.col(0) = controlled_system.x(); - - cout << "Starting " << n_t * dt << " sec simulation ... \n"; - auto start = std::chrono::high_resolution_clock::now(); - for (size_t t = 1; t < n_t; t++) { - // Let the controlled system stochastically change gain - // Assume another algorithm decodes this mode change and signals the - // switched_controller - Vector chance(1, arma::fill::randu); - if (which_mode == 1) // mode1 - { - if (chance[0] < pr_21) { - which_mode = 2; - controlled_system.set_B(b2); - if (do_switch_ctrl) { - switched_controller.Switch(1); - } - } - } else { // mode2 - if (chance[0] < pr_12) { - which_mode = 1; - controlled_system.set_B(b1); - if (do_switch_ctrl) { - switched_controller.Switch(0); - } - } - } - - // Simulate the true system. - z.col(t) = controlled_system.Simulate(u.col(t - 1)); - - // perform control - u.col(t) = switched_controller.ControlOutputReference(z.col(t)); - - mode.col(t) = which_mode; - y_ref.col(t) = y_ref0; - y_true.col(t) = controlled_system.y(); - x_true.col(t) = controlled_system.x(); - y_hat.col(t) = switched_controller.sys().y(); - x_hat.col(t) = switched_controller.sys().x(); - } - - auto finish = std::chrono::high_resolution_clock::now(); - std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; - cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; - cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; - - // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, - // x_true, m_true saving with hdf5 via armadillo - arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; - - auto dt_vec = Vector(1).fill(dt); - dt_vec.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "dt")); - y_ref.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_ref", replace)); - u.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "u", replace)); - z.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "z", replace)); - x_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_true", replace)); - y_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_true", replace)); - x_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_hat", replace)); - y_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_hat", replace)); - mode.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "mode", replace)); - - return 0; -} -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: examples/eg_plds_switched_ctrl.cpp + +--- + +# examples/eg_plds_switched_ctrl.cpp + + + +## Types + +| | Name | +| -------------- | -------------- | +| using double | **[data_t](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-data-t)** | +| using arma::Mat< data_t > | **[Matrix](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-matrix)** | +| using arma::Col< data_t > | **[Vector](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-vector)** | + +## Functions + +| | Name | +| -------------- | -------------- | +| int | **[main](/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#function-main)**() | + +## Type Details + +### data_t + +```cpp +using lds::data_t = double; +``` + + + +Type of all data in library. If need 32b, change `double` to `float`. This could be potentially useful for large scale problems where there are memory constraints. + + +### Matrix + +```cpp +using lds::Matrix = arma::Mat<data_t>; +``` + + + +### Vector + +```cpp +using lds::Vector = arma::Col<data_t>; +``` + + + + +## Function Details + +### main + +```cpp +int main() +``` + + + + + + +## Source code + +```cpp +//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst> + +using lds::data_t; +using lds::Matrix; +using lds::Vector; +using std::cout; + +auto main() -> int { + cout << " ********** Example Switched Poisson LDS Control ********** \n\n"; + + // whether to do switched control + bool do_switch_ctrl = true; + + // Make SISO system sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast<size_t>(30.0 / dt); + + // for simulating switching + size_t which_mode = 1; + data_t pr_21 = 1e-3; // prob mode 1 -> 2 + data_t pr_12 = pr_21; // prob mode 2 -> 1 + + // simulated system being controlled + lds::poisson::System controlled_system(n_u, n_x, n_y, dt); + + // **Assume the system is not well characterized by one LDS, but is well + // characterized by two LDS models with different input matrices.** + data_t scale_sys_b = 2; + + Matrix a(n_x, n_x, arma::fill::eye); + a[0] = 0.985; + Matrix b1 = Matrix(n_x, n_u).fill(0.05); + Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); + + controlled_system.set_A(a); + controlled_system.set_B(b1); + controlled_system.set_d(d); + controlled_system.Reset(); // reset to initial conditions + + // reference + Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); + + // Let underlying system 1 be more sensitive than system 2 + Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); + + // create switched controller + lds::poisson::SwitchedController switched_controller; + lds::UniformMatrixList<> k_x; // feedback controller gains + { + // create switched controller sub-systems + // system 1 + lds::poisson::System sys1(controlled_system); + + // set process noise covariance + Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; + sys1.set_Q(q_controller); + + // adaptively estimate process disturbance (m) + // n.b. using arbitrary default value for process noise if enabled. + sys1.do_adapt_m = true; + + // setting initial mode to target to avoid large error at onset: + Vector x0_controller = arma::log(y_ref0) - d; + sys1.set_x0(x0_controller); + sys1.Reset(); // reset to initial conditions + + cout << ".....................................\n"; + cout << "sys1:\n"; + cout << ".....................................\n"; + sys1.Print(); + cout << ".....................................\n"; + + // system 2 + lds::poisson::System sys2 = sys1; + + // set parameters + sys2.set_B(b2); + + cout << ".....................................\n"; + cout << "sys2:\n"; + cout << ".....................................\n"; + sys2.Print(); + cout << ".....................................\n"; + + lds::UniformSystemList<lds::poisson::System> systems({sys1, sys2}); + + // controller gains for underlying system s: + Matrix k_x1(n_u, n_x, arma::fill::ones); + Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. + k_x = lds::UniformMatrixList<>({k_x1, k_x2}); + + data_t u_lb = 0.0; + data_t u_ub = 5.0; + switched_controller = std::move( + lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); + } + // Control variables + size_t control_type = 0; // no integral action, etc + switched_controller.set_control_type(control_type); + switched_controller.set_Kc(std::move(k_x)); + + switched_controller.set_y_ref(y_ref0); + + std::vector<lds::poisson::System> systems_vec(3, lds::poisson::System()); + lds::UniformSystemList<lds::poisson::System> systems(std::move(systems_vec)); + + cout << ".....................................\n"; + cout << "switched_controller:\n"; + cout << ".....................................\n"; + switched_controller.Print(); + cout << ".....................................\n"; + + // Fake measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // Will later contain control. + Matrix u(n_u, n_t, arma::fill::zeros); + + // create Matrix to save outputs in... + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); + + // modes and gain/disturbance params + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix mode(1, n_t, arma::fill::ones); + + // set initial val + y_hat.col(0) = switched_controller.sys().y(); + y_true.col(0) = controlled_system.y(); + x_hat.col(0) = switched_controller.sys().x(); + x_true.col(0) = controlled_system.x(); + + cout << "Starting " << n_t * dt << " sec simulation ... \n"; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t < n_t; t++) { + // Let the controlled system stochastically change gain + // Assume another algorithm decodes this mode change and signals the + // switched_controller + Vector chance(1, arma::fill::randu); + if (which_mode == 1) // mode1 + { + if (chance[0] < pr_21) { + which_mode = 2; + controlled_system.set_B(b2); + if (do_switch_ctrl) { + switched_controller.Switch(1); + } + } + } else { // mode2 + if (chance[0] < pr_12) { + which_mode = 1; + controlled_system.set_B(b1); + if (do_switch_ctrl) { + switched_controller.Switch(0); + } + } + } + + // Simulate the true system. + z.col(t) = controlled_system.Simulate(u.col(t - 1)); + + // perform control + u.col(t) = switched_controller.ControlOutputReference(z.col(t)); + + mode.col(t) = which_mode; + y_ref.col(t) = y_ref0; + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + y_hat.col(t) = switched_controller.sys().y(); + x_hat.col(t) = switched_controller.sys().x(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration<data_t, std::milli> sim_time_ms = finish - start; + cout << "Finished simulation in " << sim_time_ms.count() << " ms.\n"; + cout << "(app. " << (sim_time_ms.count() / n_t) * 1e3 << " us/time-step)\n"; + + // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, + // x_true, m_true saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "dt")); + y_ref.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_ref", replace)); + u.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "u", replace)); + z.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "z", replace)); + x_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_true", replace)); + y_true.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_true", replace)); + x_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "x_hat", replace)); + y_hat.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "y_hat", replace)); + mode.save(arma::hdf5_name("eg_plds_switched_ctrl.h5", "mode", replace)); + + return 0; +} +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md index a08e813f..52875193 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md @@ -1,162 +1,162 @@ ---- -title: src/lds.cpp -summary: misc lds namespace functions - ---- - -# src/lds.cpp - -misc lds namespace functions [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Detailed Description - - - -This file implements miscellaneous lds namespace functions not bound to a class. - - - - - -## Source code - -```cpp -//===-- lds.cpp - LDS -----------------------------------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst_h/lds.h> - -// insert any necessary function definitions here. -namespace lds { - -void ForceSymPD(Matrix& X) { - if (X.is_sympd() || !X.is_square()) { - return; - } - - // make symmetric - X = (X + X.t()) / 2; - - // for eigenval decomp - bool did_succeed(true); - Vector d; - Matrix u; - - // see first method (which may not be ideal): - // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ - size_t k(1); - bool is_sympd = X.is_sympd(); - Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); - - while (!is_sympd) { - if (k > 100) { - did_succeed = arma::eig_sym(d, u, X, "std"); - data_t min_eig = arma::min(d); - std::cerr << "After multiple iterations, min eigen val = " << min_eig - << ".\n"; - throw std::runtime_error( - "Failed to make matrix symmetric positive definite."); - return; - } - - // Limit(d, arma::eps(0), kInf); // force to be positive... - // Matrix d_diag = arma::diagmat(d); - // X = u * d_diag * u.t(); - - did_succeed = arma::eig_sym(d, u, X, "std"); - if (!did_succeed) { - throw std::runtime_error("ForceSymPD failed."); - } - - data_t min_eig = arma::min(d); - X += id * abs(min_eig) + arma::datum::eps; - - // make sure symm: - X = (X + X.t()) / 2; - // double check eigenvals positive after symmetrizing: - arma::eig_sym(d, u, X, "std"); - min_eig = arma::min(d); - is_sympd = min_eig > 0; - k++; - } -} - -void ForceSymMinEig(Matrix& X, data_t eig_min) { - if (!X.is_square()) { - return; - } - - // make symmetric - X = (X + X.t()) / 2; - - bool did_succeed(true); - Vector d; - Matrix u; - did_succeed = arma::eig_sym(d, u, X, "std"); - if (!did_succeed) { - throw std::runtime_error("ForceSymMinEig failed."); - } - Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound - Matrix d_diag = arma::diagmat(d); - X = u * d_diag * u.t(); - - // double check symmetric - X = (X + X.t()) / 2; -} - -void lq(Matrix& L, Matrix& Qt, const Matrix& X) { - bool did_succeed(true); - did_succeed = arma::qr_econ(Qt, L, X.t()); - if (!did_succeed) { - throw std::runtime_error("LQ decomposition failed."); - } - arma::inplace_trans(L); - arma::inplace_trans(Qt); -} - -Matrix calcCov(const Matrix& A, const Matrix& B) { - // subtract out mean - auto m_a = arma::mean(A, 1); - Matrix a0 = A; - a0.each_col() -= m_a; - - auto m_b = arma::mean(B, 1); - Matrix b0 = B; - b0.each_col() -= m_b; - - Matrix cov = a0 * b0.t() / a0.n_cols; - return cov; -} - -} // namespace lds -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: src/lds.cpp +summary: misc lds namespace functions + +--- + +# src/lds.cpp + +misc lds namespace functions [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Detailed Description + + + +This file implements miscellaneous lds namespace functions not bound to a class. + + + + + +## Source code + +```cpp +//===-- lds.cpp - LDS -----------------------------------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst_h/lds.h> + +// insert any necessary function definitions here. +namespace lds { + +void ForceSymPD(Matrix& X) { + if (X.is_sympd() || !X.is_square()) { + return; + } + + // make symmetric + X = (X + X.t()) / 2; + + // for eigenval decomp + bool did_succeed(true); + Vector d; + Matrix u; + + // see first method (which may not be ideal): + // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ + size_t k(1); + bool is_sympd = X.is_sympd(); + Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); + + while (!is_sympd) { + if (k > 100) { + did_succeed = arma::eig_sym(d, u, X, "std"); + data_t min_eig = arma::min(d); + std::cerr << "After multiple iterations, min eigen val = " << min_eig + << ".\n"; + throw std::runtime_error( + "Failed to make matrix symmetric positive definite."); + return; + } + + // Limit(d, arma::eps(0), kInf); // force to be positive... + // Matrix d_diag = arma::diagmat(d); + // X = u * d_diag * u.t(); + + did_succeed = arma::eig_sym(d, u, X, "std"); + if (!did_succeed) { + throw std::runtime_error("ForceSymPD failed."); + } + + data_t min_eig = arma::min(d); + X += id * abs(min_eig) + arma::datum::eps; + + // make sure symm: + X = (X + X.t()) / 2; + // double check eigenvals positive after symmetrizing: + arma::eig_sym(d, u, X, "std"); + min_eig = arma::min(d); + is_sympd = min_eig > 0; + k++; + } +} + +void ForceSymMinEig(Matrix& X, data_t eig_min) { + if (!X.is_square()) { + return; + } + + // make symmetric + X = (X + X.t()) / 2; + + bool did_succeed(true); + Vector d; + Matrix u; + did_succeed = arma::eig_sym(d, u, X, "std"); + if (!did_succeed) { + throw std::runtime_error("ForceSymMinEig failed."); + } + Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound + Matrix d_diag = arma::diagmat(d); + X = u * d_diag * u.t(); + + // double check symmetric + X = (X + X.t()) / 2; +} + +void lq(Matrix& L, Matrix& Qt, const Matrix& X) { + bool did_succeed(true); + did_succeed = arma::qr_econ(Qt, L, X.t()); + if (!did_succeed) { + throw std::runtime_error("LQ decomposition failed."); + } + arma::inplace_trans(L); + arma::inplace_trans(Qt); +} + +Matrix calcCov(const Matrix& A, const Matrix& B) { + // subtract out mean + auto m_a = arma::mean(A, 1); + Matrix a0 = A; + a0.each_col() -= m_a; + + auto m_b = arma::mean(B, 1); + Matrix b0 = B; + b0.each_col() -= m_b; + + Matrix cov = a0 * b0.t() / a0.n_cols; + return cov; +} + +} // namespace lds +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds_8h.md b/misc/docs-hugo/content/docs/api/Files/lds_8h.md index 87b7121a..6d054aee 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds_8h.md @@ -1,184 +1,184 @@ ---- -title: ldsCtrlEst_h/lds.h -summary: lds namespace - ---- - -# ldsCtrlEst_h/lds.h - -`lds` namespace [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Detailed Description - - - -This file defines the `lds` namespace, which will be an umbrella for linear dynamical systems with Gaussian (`[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)`) or Poisson (`[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)`) observations. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_H -#define LDSCTRLEST_LDS_H - -// #ifndef LDSCTRLEST -// #include <ldsCtrlEst> -// #endif - -#include <armadillo> - -namespace lds { -using data_t = double; // may change to float (but breaks mex functions) -using Vector = arma::Col<data_t>; -using Matrix = arma::Mat<data_t>; -using Cube = arma::Cube<data_t>; -using View = arma::subview<data_t>; - -namespace fill = arma::fill; - - -static const std::size_t kControlTypeDeltaU = 0x1; - -static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1; - -static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2; - -static const data_t kInf = std::numeric_limits<data_t>::infinity(); -static const data_t kPi = arma::datum::pi; - -static const data_t kDefaultP0 = 1e-6; -static const data_t kDefaultQ0 = 1e-6; -static const data_t kDefaultR0 = 1e-2; - -enum SSIDWt { - kSSIDNone, - kSSIDMOESP, - kSSIDCVA -}; - -enum MatrixListFreeDim { - kMatFreeDimNone, - kMatFreeDim1, - kMatFreeDim2 -}; - -// TODO(mfbolus): for SwitchedController, may want systems to have differing -// numbers of states. Use this enum as template parameter? -// enum SystemListFreeDim { -// kSysFreeDimNone, -// kSysFreeDimX ///< allow state dim (x) of systems in list to be hetero -// }; - -// place hard limits on contents of vecors/mats -void Limit(std::vector<data_t>& x, data_t lb, data_t ub); -void Limit(Vector& x, data_t lb, data_t ub); -void Limit(Matrix& x, data_t lb, data_t ub); - -// in-place assign that errs if there are dimension mismatches: -void Reassign(Vector& some, const Vector& other, - const std::string& parenthetical = "Reassign"); -void Reassign(Matrix& some, const Matrix& other, - const std::string& parenthetical = "Reassign"); - -// TODO(mfbolus): this is a fudge, but for some reason, cov mats often going -// numerically asymm. - -void ForceSymPD(Matrix& X); - -void ForceSymMinEig(Matrix& X, data_t eig_min = 0); - -void lq(Matrix& L, Matrix& Qt, const Matrix& X); - -Matrix calcCov(const Matrix& A, const Matrix& B); - -inline void Limit(std::vector<data_t>& x, data_t lb, data_t ub) { - for (data_t& el : x) { - el = el < lb ? lb : el; - el = el > ub ? ub : el; - } -} -inline void Limit(Vector& x, data_t lb, data_t ub) { - for (data_t& el : x) { - el = el < lb ? lb : el; - el = el > ub ? ub : el; - } -} -inline void Limit(Matrix& x, data_t lb, data_t ub) { - for (data_t& el : x) { - el = el < lb ? lb : el; - el = el > ub ? ub : el; - } -} - -inline void Reassign(Vector& some, const Vector& other, - const std::string& parenthetical) { - // check dimensions - if (other.n_elem != some.n_elem) { - std::ostringstream ss; - ss << "cannot reassign vector of size " << some.n_elem - << " with vector of size " << other.n_elem << "(" << parenthetical - << ")"; - throw std::runtime_error(ss.str()); - } - - for (size_t k = 0; k < some.n_elem; k++) { - some[k] = other[k]; - } -} - -inline void Reassign(Matrix& some, const Matrix& other, - const std::string& parenthetical) { - // check dimensions - if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { - std::ostringstream ss; - ss << "cannot reassign matrix of size " << some.n_rows << "x" << some.n_cols - << " with matrix of size " << other.n_rows << "x" << other.n_cols << "(" - << parenthetical << ")"; - throw std::runtime_error(ss.str()); - } - - for (size_t k = 0; k < some.n_elem; k++) { - some[k] = other[k]; - } -} - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds.h +summary: lds namespace + +--- + +# ldsCtrlEst_h/lds.h + +`lds` namespace [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Detailed Description + + + +This file defines the `lds` namespace, which will be an umbrella for linear dynamical systems with Gaussian (`[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)`) or Poisson (`[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)`) observations. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_H +#define LDSCTRLEST_LDS_H + +// #ifndef LDSCTRLEST +// #include <ldsCtrlEst> +// #endif + +#include <armadillo> + +namespace lds { +using data_t = double; // may change to float (but breaks mex functions) +using Vector = arma::Col<data_t>; +using Matrix = arma::Mat<data_t>; +using Cube = arma::Cube<data_t>; +using View = arma::subview<data_t>; + +namespace fill = arma::fill; + + +static const std::size_t kControlTypeDeltaU = 0x1; + +static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1; + +static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2; + +static const data_t kInf = std::numeric_limits<data_t>::infinity(); +static const data_t kPi = arma::datum::pi; + +static const data_t kDefaultP0 = 1e-6; +static const data_t kDefaultQ0 = 1e-6; +static const data_t kDefaultR0 = 1e-2; + +enum SSIDWt { + kSSIDNone, + kSSIDMOESP, + kSSIDCVA +}; + +enum MatrixListFreeDim { + kMatFreeDimNone, + kMatFreeDim1, + kMatFreeDim2 +}; + +// TODO(mfbolus): for SwitchedController, may want systems to have differing +// numbers of states. Use this enum as template parameter? +// enum SystemListFreeDim { +// kSysFreeDimNone, +// kSysFreeDimX ///< allow state dim (x) of systems in list to be hetero +// }; + +// place hard limits on contents of vecors/mats +void Limit(std::vector<data_t>& x, data_t lb, data_t ub); +void Limit(Vector& x, data_t lb, data_t ub); +void Limit(Matrix& x, data_t lb, data_t ub); + +// in-place assign that errs if there are dimension mismatches: +void Reassign(Vector& some, const Vector& other, + const std::string& parenthetical = "Reassign"); +void Reassign(Matrix& some, const Matrix& other, + const std::string& parenthetical = "Reassign"); + +// TODO(mfbolus): this is a fudge, but for some reason, cov mats often going +// numerically asymm. + +void ForceSymPD(Matrix& X); + +void ForceSymMinEig(Matrix& X, data_t eig_min = 0); + +void lq(Matrix& L, Matrix& Qt, const Matrix& X); + +Matrix calcCov(const Matrix& A, const Matrix& B); + +inline void Limit(std::vector<data_t>& x, data_t lb, data_t ub) { + for (data_t& el : x) { + el = el < lb ? lb : el; + el = el > ub ? ub : el; + } +} +inline void Limit(Vector& x, data_t lb, data_t ub) { + for (data_t& el : x) { + el = el < lb ? lb : el; + el = el > ub ? ub : el; + } +} +inline void Limit(Matrix& x, data_t lb, data_t ub) { + for (data_t& el : x) { + el = el < lb ? lb : el; + el = el > ub ? ub : el; + } +} + +inline void Reassign(Vector& some, const Vector& other, + const std::string& parenthetical) { + // check dimensions + if (other.n_elem != some.n_elem) { + std::ostringstream ss; + ss << "cannot reassign vector of size " << some.n_elem + << " with vector of size " << other.n_elem << "(" << parenthetical + << ")"; + throw std::runtime_error(ss.str()); + } + + for (size_t k = 0; k < some.n_elem; k++) { + some[k] = other[k]; + } +} + +inline void Reassign(Matrix& some, const Matrix& other, + const std::string& parenthetical) { + // check dimensions + if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { + std::ostringstream ss; + ss << "cannot reassign matrix of size " << some.n_rows << "x" << some.n_cols + << " with matrix of size " << other.n_rows << "x" << other.n_cols << "(" + << parenthetical << ")"; + throw std::runtime_error(ss.str()); + } + + for (size_t k = 0; k < some.n_elem; k++) { + some[k] = other[k]; + } +} + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md index e40d81f0..ac9d3a01 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md @@ -1,551 +1,551 @@ ---- -title: ldsCtrlEst_h/lds_ctrl.h -summary: Controller. - ---- - -# ldsCtrlEst_h/lds_ctrl.h - -Controller. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** | - -## Detailed Description - - - -This file declares the type for control of a linear dynamical system ([lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_CTRL_H -#define LDSCTRLEST_LDS_CTRL_H - -// namespace -#include "lds.h" -// system type -#include "lds_sys.h" - -namespace lds { - -template <typename System> -class Controller { - static_assert(std::is_base_of<lds::System, System>::value, - "System must be derived from lds::System type."); - - public: - Controller() = default; - - Controller(const System& sys, data_t u_lb, data_t u_ub, - size_t control_type = 0); - - Controller(System&& sys, data_t u_lb, data_t u_ub, size_t control_type = 0); - - const Vector& Control(const Vector& z, bool do_control = true, - bool do_lock_control = false, - data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, - bool do_reset_at_control_onset = true); - - const Vector& ControlOutputReference(const Vector& z, bool do_control = true, - bool do_estimation = true, - bool do_lock_control = false, - data_t sigma_soft_start = 0, - data_t sigma_u_noise = 0, - bool do_reset_at_control_onset = true); - - // get methods: - const System& sys() const { return sys_; }; - const Matrix& Kc() const { return Kc_; }; - const Matrix& Kc_inty() const { return Kc_inty_; }; - const Matrix& Kc_u() const { return Kc_u_; }; - const Vector& g_design() const { return g_design_; }; - const Vector& u_ref() const { return u_ref_; }; - const Vector& x_ref() const { return x_ref_; }; - const Vector& y_ref() const { return y_ref_; }; - size_t control_type() const { return control_type_; }; - data_t tau_awu() const { return tau_awu_; }; - data_t u_lb() const { return u_lb_; }; - data_t u_ub() const { return u_ub_; }; - - // set methods - void set_sys(const System& sys) { - bool does_match = sys_.n_u() == sys.n_u(); - does_match = does_match && (sys_.n_x() == sys.n_x()); - does_match = does_match && (sys_.n_y() == sys.n_y()); - if (does_match) { - sys_ = sys; - } else { - throw std::runtime_error( - "new system argument to `set_sys` does not match dimensionality of " - "existing system"); - } - }; - void set_g_design(const Vector& g_design) { Reassign(g_design_, g_design); }; - void set_u_ref(const Vector& u_ref) { Reassign(u_ref_, u_ref); }; - void set_x_ref(const Vector& x_ref) { - Reassign(x_ref_, x_ref); - cx_ref_ = sys_.C() * x_ref_; - }; - - // y_ref needs to be handled differently depending on output fn. - // (need to populate cx_ref_ too, which depends on output fn) - virtual void set_y_ref(const Vector& y_ref) { Reassign(y_ref_, y_ref); }; - - void set_Kc(const Matrix& Kc) { Reassign(Kc_, Kc); }; - void set_Kc_inty(const Matrix& Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; - void set_Kc_u(const Matrix& Kc_u) { Reassign(Kc_u_, Kc_u); }; - void set_tau_awu(data_t tau) { - tau_awu_ = tau; - k_awu_ = sys_.dt() / tau_awu_; - }; - - void set_control_type(size_t control_type); - - // There is no reason u_lb/ub should not be public, but making set methods - // anyway. - void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; - - void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; - - void Reset() { - sys_.Reset(); - u_ref_.zeros(); - u_ref_prev_.zeros(); - int_e_.zeros(); - int_e_awu_adjust_.zeros(); - u_sat_.zeros(); - u_saturated_ = false; - t_since_control_onset_ = 0.0; - }; - - void Print() { - sys_.Print(); - std::cout << "g_design : " << g_design_ << "\n"; - std::cout << "u_lb : " << u_lb_ << "\n"; - std::cout << "u_ub : " << u_ub_ << "\n"; - }; - - protected: - System sys_; - - Vector u_; - Vector u_return_; - - Vector g_design_; - - // reference signals - Vector u_ref_; - // create no set method for this: - Vector u_ref_prev_; - Vector x_ref_; - Vector y_ref_; - Vector cx_ref_; - - // Controller gains - Matrix Kc_; - Matrix - Kc_u_; - Matrix Kc_inty_; - - // control after g inversion - // do not need set methods for these. - Vector du_ref_; - Vector dv_ref_; - Vector v_ref_; - Vector dv_; - Vector v_; - - // integral error - // do not need set method for this - Vector int_e_; - Vector int_e_awu_adjust_; - Vector u_sat_; - - bool do_control_prev_ = false; - bool do_lock_control_prev_ = false; - - // whether the g of system has become inverted from what you think it is - // (gain_ref) - bool u_saturated_ = - false; - - // should be safe to have references here bc nothing needs to be done - // (like reset vars) when it changes... - data_t u_lb_{}; - data_t u_ub_{}; - - data_t tau_awu_{}; - data_t k_awu_ = 0; - - data_t t_since_control_onset_ = 0; - size_t control_type_{}; - - private: - void CalcControl(bool do_control = true, bool do_estimation = true, - bool do_lock_control = false, data_t sigma_soft_start = 0, - data_t sigma_u_noise = 0, - bool do_reset_at_control_onset = true); - - void CalcSteadyStateSetPoint(); - - void AntiWindup(); - - void InitVars(size_t control_type); -}; - -// Implement the above: - -template <typename System> -inline Controller<System>::Controller(const System& sys, data_t u_lb, - data_t u_ub, size_t control_type) - : sys_(sys), - u_lb_(u_lb), - u_ub_(u_ub), - tau_awu_(lds::kInf) { - InitVars(control_type); -} - -template <typename System> -inline Controller<System>::Controller(System&& sys, data_t u_lb, data_t u_ub, - size_t control_type) - : sys_(std::move(sys)), - u_lb_(u_lb), - u_ub_(u_ub), - tau_awu_(lds::kInf) { - InitVars(control_type); -} - -template <typename System> -inline void Controller<System>::set_control_type(size_t control_type) { - if (control_type_ == control_type) { - return; - } - - // creating a blank slate... - control_type_ = 0; - Kc_inty_.zeros(0, 0); - Kc_u_.zeros(0, 0); - int_e_.zeros(0, 0); - int_e_awu_adjust_.zeros(0, 0); - - // controller was designed to minimize integral error - if (control_type & kControlTypeIntY) { - Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); - int_e_.zeros(sys_.n_y()); - int_e_awu_adjust_.zeros(sys_.n_u()); - control_type_ = control_type_ | kControlTypeIntY; - } - - // controller was designed to minimize deltaU - // (i.e. state augmented with u) - if (control_type & kControlTypeDeltaU) { - Kc_u_.zeros(sys_.n_u(), sys_.n_u()); - control_type_ = control_type_ | kControlTypeDeltaU; - } - - // whether to adapt set point calculate with (re-estimated) process - // disturbance (m) - if (control_type & kControlTypeAdaptM) { - if (sys_.do_adapt_m) // only if adapting m... - { - control_type_ = control_type_ | kControlTypeAdaptM; - } - } -} // set_control_type - -template <typename System> -inline const Vector& Controller<System>::Control( - const Vector& z, bool do_control, bool do_lock_control, - data_t sigma_soft_start, data_t sigma_u_noise, - bool do_reset_at_control_onset) { - // update state estimates, given latest measurement - sys_.Filter(u_, z); - - bool do_estimation = true; // always have estimator on in this case - - // calculate control signal - CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, - sigma_u_noise, do_reset_at_control_onset); - - return u_return_; -} - -template <typename System> -inline const Vector& Controller<System>::ControlOutputReference( - const Vector& z, bool do_control, bool do_estimation, bool do_lock_control, - data_t sigma_soft_start, data_t sigma_u_noise, - bool do_reset_at_control_onset) { - // update state estimates, given latest measurement - if (do_estimation) { - sys_.Filter(u_, z); - } else { - sys_.f(u_); - } - - // calculate the set point - // solves for u_ref and x_ref when output is at y_ref at steady state. - if (do_control) { - CalcSteadyStateSetPoint(); - } - - // calculate control signal - CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, - sigma_u_noise, do_reset_at_control_onset); - - return u_return_; -} - -template <typename System> -inline void Controller<System>::CalcControl(bool do_control, bool do_estimation, - bool do_lock_control, - data_t sigma_soft_start, - data_t sigma_u_noise, - bool do_reset_at_control_onset) { - if (do_control && do_estimation) { - if (!do_control_prev_) { - if (do_reset_at_control_onset) { - Reset(); - } - t_since_control_onset_ = 0.0; - } else { - t_since_control_onset_ += sys_.dt(); - } - - // enforce softstart on control vars. - if (sigma_soft_start > 0) { - // half-Gaussian soft-start scaling factor - data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / - (2 * pow(sigma_soft_start, 2))); - u_ref_ *= soft_start_sf; - // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too - // x_ref_ *= soft_start_sf; - // cx_ref_ *= soft_start_sf; - // y_ref_ *= soft_start_sf; - } - - if (!do_lock_control) { - // first do u -> v change of vars. (v = g.*u) - // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver - // control voltage u[=]V) - v_ref_ = g_design_ % u_ref_; - - // Given FB, calc. the change in control - if (control_type_ & kControlTypeDeltaU) { - // if control designed to minimize not u but deltaU (i.e. state aug with - // u): - - // TODO(mfbolus): Commented out for now. See note below. - // du_ref_ = u_ref_ - u_ref_prev_; - // dv_ref_ = g_design_ % du_ref_; - - // TODO(mfbolus): Assuming users want *smooth* control signals if using - // kControlTypeDeltaU, it should be the case that dv_ref_ is --> 0. May - // want to revisit, but I am going to force it to be zero unless a - // situation arises that argues for keeping the above. - dv_ref_.zeros(); - - dv_ = dv_ref_; // nominally-optimal. - dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error - dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) - - if (control_type_ & kControlTypeIntY) { - // TODO(mfbolus): one approach to protection against integral windup - // would be to not integrate error when control signal saturated: - - // if(!uSaturated) - int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error - dv_ -= Kc_inty_ * int_e_; // control for integrated error - } - - // update the control - v_ += dv_; - } else { - v_ = v_ref_; // nominally-optimal. - v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error - - if (control_type_ & kControlTypeIntY) { - // TODO(mfbolus): one approach to protection against integral windup - // would be to not integrate error when control signal saturated: - - // if (!uSaturated) - int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error - v_ -= Kc_inty_ * int_e_; // control for integrated error - } - } - - // convert back to control voltage u[=]V - u_ = v_ / sys_.g(); - } // else do nothing until lock is low - } else { // if not control - // feed through u_ref in open loop - u_ = u_ref_ % g_design_ / sys_.g(); - v_ = sys_.g() % u_; - u_ref_.zeros(); - int_e_.zeros(); - int_e_awu_adjust_.zeros(); - u_sat_.zeros(); - } // ends do_control - - // enforce box constraints (and antiwindup) - AntiWindup(); - - // add noise to input? - // The value for u that is *returned* to user after addition of any noise, - // while keeping controller/estimator blind to this addition. - u_return_ = u_; - if ((sigma_u_noise > 0.0) && (do_control && !do_lock_control)) { - u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); - Limit(u_return_, u_lb_, u_ub_); - }; - - // For next time step: - u_ref_prev_ = u_ref_; - do_control_prev_ = do_control; - do_lock_control_prev_ = do_lock_control; -} // CalcControl - -template <typename System> -inline void Controller<System>::CalcSteadyStateSetPoint() { - // Linearly-constrained least squares (ls). - // - // _reference: - // Boyd & Vandenberghe (2018) Introduction to Applied Linear Algebra - // - Matrix a_ls = - join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); - Vector b_ls = cx_ref_; - Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), - sys_.B() * arma::diagmat(sys_.g())); - Vector d_ls = -sys_.m0(); - if (control_type_ & kControlTypeAdaptM) { - d_ls = -sys_.m(); // adapt setpoint calc with disturbance? - } - - Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg - // fault if I do not do this. - Matrix phi_ls = - join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), - join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); - // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: - Matrix inv_phi = pinv(phi_ls); - Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); - x_ref_ = xulam.subvec(0, sys_.n_x() - 1); - u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); - cx_ref_ = sys_.C() * x_ref_; -} // CalcSteadyStateSetPoint - -template <typename System> -void Controller<System>::AntiWindup() { - u_saturated_ = false; - u_sat_ = u_; - - // limit u and flag whether saturated - for (size_t k = 0; k < u_.n_elem; k++) { - if (u_[k] < u_lb_) { - u_sat_[k] = u_lb_; - u_saturated_ = true; - } - - if (u_[k] > u_ub_) { - u_sat_[k] = u_ub_; - u_saturated_ = true; - } - } - - if ((control_type_ & kControlTypeIntY) && (tau_awu_ < lds::kInf)) { - // one-step back-calculation (calculate intE for u=u_sat) - // (Astroem, Rundqwist 1989 warn against using this...) - // int_e_awu_adjust_ = - // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); - - // gradual: see Astroem, Rundqwist 1989 - // this is a fudge for doing MIMO gradual - // n.b., went ahead and multiplied 1/T by dt so don't have to do that here. - int_e_awu_adjust_ = - k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); - // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); - - int_e_ += int_e_awu_adjust_; - } - - // set u to saturated version - u_ = u_sat_; -} - -template <typename System> -void Controller<System>::InitVars(size_t control_type) { - // initialize to default values - u_ref_ = Vector(sys_.n_u(), fill::zeros); - u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); - x_ref_ = Vector(sys_.n_x(), fill::zeros); - y_ref_ = Vector(sys_.n_y(), fill::zeros); - cx_ref_ = Vector(sys_.n_y(), fill::zeros); - - u_ = Vector(sys_.n_u(), fill::zeros); - u_return_ = Vector(sys_.n_u(), fill::zeros); - u_sat_ = Vector(sys_.n_u(), fill::zeros); - - // Might not need all these, so zero elements until later. - Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); - Kc_u_ = Matrix(0, 0, fill::zeros); - Kc_inty_ = Matrix(0, 0, fill::zeros); - - g_design_ = sys_.g(); // by default, same as model - dv_ = Vector(sys_.n_u(), fill::zeros); - v_ = Vector(sys_.n_u(), fill::zeros); - du_ref_ = Vector(sys_.n_u(), fill::zeros); - dv_ref_ = Vector(sys_.n_u(), fill::zeros); - v_ref_ = Vector(sys_.n_u(), fill::zeros); - - int_e_ = Vector(0, fill::zeros); - int_e_awu_adjust_ = Vector(0, fill::zeros); - - set_control_type(control_type); -} - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_ctrl.h +summary: Controller. + +--- + +# ldsCtrlEst_h/lds_ctrl.h + +Controller. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** | + +## Detailed Description + + + +This file declares the type for control of a linear dynamical system ([lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_CTRL_H +#define LDSCTRLEST_LDS_CTRL_H + +// namespace +#include "lds.h" +// system type +#include "lds_sys.h" + +namespace lds { + +template <typename System> +class Controller { + static_assert(std::is_base_of<lds::System, System>::value, + "System must be derived from lds::System type."); + + public: + Controller() = default; + + Controller(const System& sys, data_t u_lb, data_t u_ub, + size_t control_type = 0); + + Controller(System&& sys, data_t u_lb, data_t u_ub, size_t control_type = 0); + + const Vector& Control(const Vector& z, bool do_control = true, + bool do_lock_control = false, + data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, + bool do_reset_at_control_onset = true); + + const Vector& ControlOutputReference(const Vector& z, bool do_control = true, + bool do_estimation = true, + bool do_lock_control = false, + data_t sigma_soft_start = 0, + data_t sigma_u_noise = 0, + bool do_reset_at_control_onset = true); + + // get methods: + const System& sys() const { return sys_; }; + const Matrix& Kc() const { return Kc_; }; + const Matrix& Kc_inty() const { return Kc_inty_; }; + const Matrix& Kc_u() const { return Kc_u_; }; + const Vector& g_design() const { return g_design_; }; + const Vector& u_ref() const { return u_ref_; }; + const Vector& x_ref() const { return x_ref_; }; + const Vector& y_ref() const { return y_ref_; }; + size_t control_type() const { return control_type_; }; + data_t tau_awu() const { return tau_awu_; }; + data_t u_lb() const { return u_lb_; }; + data_t u_ub() const { return u_ub_; }; + + // set methods + void set_sys(const System& sys) { + bool does_match = sys_.n_u() == sys.n_u(); + does_match = does_match && (sys_.n_x() == sys.n_x()); + does_match = does_match && (sys_.n_y() == sys.n_y()); + if (does_match) { + sys_ = sys; + } else { + throw std::runtime_error( + "new system argument to `set_sys` does not match dimensionality of " + "existing system"); + } + }; + void set_g_design(const Vector& g_design) { Reassign(g_design_, g_design); }; + void set_u_ref(const Vector& u_ref) { Reassign(u_ref_, u_ref); }; + void set_x_ref(const Vector& x_ref) { + Reassign(x_ref_, x_ref); + cx_ref_ = sys_.C() * x_ref_; + }; + + // y_ref needs to be handled differently depending on output fn. + // (need to populate cx_ref_ too, which depends on output fn) + virtual void set_y_ref(const Vector& y_ref) { Reassign(y_ref_, y_ref); }; + + void set_Kc(const Matrix& Kc) { Reassign(Kc_, Kc); }; + void set_Kc_inty(const Matrix& Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; + void set_Kc_u(const Matrix& Kc_u) { Reassign(Kc_u_, Kc_u); }; + void set_tau_awu(data_t tau) { + tau_awu_ = tau; + k_awu_ = sys_.dt() / tau_awu_; + }; + + void set_control_type(size_t control_type); + + // There is no reason u_lb/ub should not be public, but making set methods + // anyway. + void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; + + void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; + + void Reset() { + sys_.Reset(); + u_ref_.zeros(); + u_ref_prev_.zeros(); + int_e_.zeros(); + int_e_awu_adjust_.zeros(); + u_sat_.zeros(); + u_saturated_ = false; + t_since_control_onset_ = 0.0; + }; + + void Print() { + sys_.Print(); + std::cout << "g_design : " << g_design_ << "\n"; + std::cout << "u_lb : " << u_lb_ << "\n"; + std::cout << "u_ub : " << u_ub_ << "\n"; + }; + + protected: + System sys_; + + Vector u_; + Vector u_return_; + + Vector g_design_; + + // reference signals + Vector u_ref_; + // create no set method for this: + Vector u_ref_prev_; + Vector x_ref_; + Vector y_ref_; + Vector cx_ref_; + + // Controller gains + Matrix Kc_; + Matrix + Kc_u_; + Matrix Kc_inty_; + + // control after g inversion + // do not need set methods for these. + Vector du_ref_; + Vector dv_ref_; + Vector v_ref_; + Vector dv_; + Vector v_; + + // integral error + // do not need set method for this + Vector int_e_; + Vector int_e_awu_adjust_; + Vector u_sat_; + + bool do_control_prev_ = false; + bool do_lock_control_prev_ = false; + + // whether the g of system has become inverted from what you think it is + // (gain_ref) + bool u_saturated_ = + false; + + // should be safe to have references here bc nothing needs to be done + // (like reset vars) when it changes... + data_t u_lb_{}; + data_t u_ub_{}; + + data_t tau_awu_{}; + data_t k_awu_ = 0; + + data_t t_since_control_onset_ = 0; + size_t control_type_{}; + + private: + void CalcControl(bool do_control = true, bool do_estimation = true, + bool do_lock_control = false, data_t sigma_soft_start = 0, + data_t sigma_u_noise = 0, + bool do_reset_at_control_onset = true); + + void CalcSteadyStateSetPoint(); + + void AntiWindup(); + + void InitVars(size_t control_type); +}; + +// Implement the above: + +template <typename System> +inline Controller<System>::Controller(const System& sys, data_t u_lb, + data_t u_ub, size_t control_type) + : sys_(sys), + u_lb_(u_lb), + u_ub_(u_ub), + tau_awu_(lds::kInf) { + InitVars(control_type); +} + +template <typename System> +inline Controller<System>::Controller(System&& sys, data_t u_lb, data_t u_ub, + size_t control_type) + : sys_(std::move(sys)), + u_lb_(u_lb), + u_ub_(u_ub), + tau_awu_(lds::kInf) { + InitVars(control_type); +} + +template <typename System> +inline void Controller<System>::set_control_type(size_t control_type) { + if (control_type_ == control_type) { + return; + } + + // creating a blank slate... + control_type_ = 0; + Kc_inty_.zeros(0, 0); + Kc_u_.zeros(0, 0); + int_e_.zeros(0, 0); + int_e_awu_adjust_.zeros(0, 0); + + // controller was designed to minimize integral error + if (control_type & kControlTypeIntY) { + Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); + int_e_.zeros(sys_.n_y()); + int_e_awu_adjust_.zeros(sys_.n_u()); + control_type_ = control_type_ | kControlTypeIntY; + } + + // controller was designed to minimize deltaU + // (i.e. state augmented with u) + if (control_type & kControlTypeDeltaU) { + Kc_u_.zeros(sys_.n_u(), sys_.n_u()); + control_type_ = control_type_ | kControlTypeDeltaU; + } + + // whether to adapt set point calculate with (re-estimated) process + // disturbance (m) + if (control_type & kControlTypeAdaptM) { + if (sys_.do_adapt_m) // only if adapting m... + { + control_type_ = control_type_ | kControlTypeAdaptM; + } + } +} // set_control_type + +template <typename System> +inline const Vector& Controller<System>::Control( + const Vector& z, bool do_control, bool do_lock_control, + data_t sigma_soft_start, data_t sigma_u_noise, + bool do_reset_at_control_onset) { + // update state estimates, given latest measurement + sys_.Filter(u_, z); + + bool do_estimation = true; // always have estimator on in this case + + // calculate control signal + CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, + sigma_u_noise, do_reset_at_control_onset); + + return u_return_; +} + +template <typename System> +inline const Vector& Controller<System>::ControlOutputReference( + const Vector& z, bool do_control, bool do_estimation, bool do_lock_control, + data_t sigma_soft_start, data_t sigma_u_noise, + bool do_reset_at_control_onset) { + // update state estimates, given latest measurement + if (do_estimation) { + sys_.Filter(u_, z); + } else { + sys_.f(u_); + } + + // calculate the set point + // solves for u_ref and x_ref when output is at y_ref at steady state. + if (do_control) { + CalcSteadyStateSetPoint(); + } + + // calculate control signal + CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, + sigma_u_noise, do_reset_at_control_onset); + + return u_return_; +} + +template <typename System> +inline void Controller<System>::CalcControl(bool do_control, bool do_estimation, + bool do_lock_control, + data_t sigma_soft_start, + data_t sigma_u_noise, + bool do_reset_at_control_onset) { + if (do_control && do_estimation) { + if (!do_control_prev_) { + if (do_reset_at_control_onset) { + Reset(); + } + t_since_control_onset_ = 0.0; + } else { + t_since_control_onset_ += sys_.dt(); + } + + // enforce softstart on control vars. + if (sigma_soft_start > 0) { + // half-Gaussian soft-start scaling factor + data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / + (2 * pow(sigma_soft_start, 2))); + u_ref_ *= soft_start_sf; + // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too + // x_ref_ *= soft_start_sf; + // cx_ref_ *= soft_start_sf; + // y_ref_ *= soft_start_sf; + } + + if (!do_lock_control) { + // first do u -> v change of vars. (v = g.*u) + // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver + // control voltage u[=]V) + v_ref_ = g_design_ % u_ref_; + + // Given FB, calc. the change in control + if (control_type_ & kControlTypeDeltaU) { + // if control designed to minimize not u but deltaU (i.e. state aug with + // u): + + // TODO(mfbolus): Commented out for now. See note below. + // du_ref_ = u_ref_ - u_ref_prev_; + // dv_ref_ = g_design_ % du_ref_; + + // TODO(mfbolus): Assuming users want *smooth* control signals if using + // kControlTypeDeltaU, it should be the case that dv_ref_ is --> 0. May + // want to revisit, but I am going to force it to be zero unless a + // situation arises that argues for keeping the above. + dv_ref_.zeros(); + + dv_ = dv_ref_; // nominally-optimal. + dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error + dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) + + if (control_type_ & kControlTypeIntY) { + // TODO(mfbolus): one approach to protection against integral windup + // would be to not integrate error when control signal saturated: + + // if(!uSaturated) + int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error + dv_ -= Kc_inty_ * int_e_; // control for integrated error + } + + // update the control + v_ += dv_; + } else { + v_ = v_ref_; // nominally-optimal. + v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error + + if (control_type_ & kControlTypeIntY) { + // TODO(mfbolus): one approach to protection against integral windup + // would be to not integrate error when control signal saturated: + + // if (!uSaturated) + int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error + v_ -= Kc_inty_ * int_e_; // control for integrated error + } + } + + // convert back to control voltage u[=]V + u_ = v_ / sys_.g(); + } // else do nothing until lock is low + } else { // if not control + // feed through u_ref in open loop + u_ = u_ref_ % g_design_ / sys_.g(); + v_ = sys_.g() % u_; + u_ref_.zeros(); + int_e_.zeros(); + int_e_awu_adjust_.zeros(); + u_sat_.zeros(); + } // ends do_control + + // enforce box constraints (and antiwindup) + AntiWindup(); + + // add noise to input? + // The value for u that is *returned* to user after addition of any noise, + // while keeping controller/estimator blind to this addition. + u_return_ = u_; + if ((sigma_u_noise > 0.0) && (do_control && !do_lock_control)) { + u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); + Limit(u_return_, u_lb_, u_ub_); + }; + + // For next time step: + u_ref_prev_ = u_ref_; + do_control_prev_ = do_control; + do_lock_control_prev_ = do_lock_control; +} // CalcControl + +template <typename System> +inline void Controller<System>::CalcSteadyStateSetPoint() { + // Linearly-constrained least squares (ls). + // + // _reference: + // Boyd & Vandenberghe (2018) Introduction to Applied Linear Algebra + // + Matrix a_ls = + join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); + Vector b_ls = cx_ref_; + Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), + sys_.B() * arma::diagmat(sys_.g())); + Vector d_ls = -sys_.m0(); + if (control_type_ & kControlTypeAdaptM) { + d_ls = -sys_.m(); // adapt setpoint calc with disturbance? + } + + Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg + // fault if I do not do this. + Matrix phi_ls = + join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), + join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); + // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: + Matrix inv_phi = pinv(phi_ls); + Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); + x_ref_ = xulam.subvec(0, sys_.n_x() - 1); + u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); + cx_ref_ = sys_.C() * x_ref_; +} // CalcSteadyStateSetPoint + +template <typename System> +void Controller<System>::AntiWindup() { + u_saturated_ = false; + u_sat_ = u_; + + // limit u and flag whether saturated + for (size_t k = 0; k < u_.n_elem; k++) { + if (u_[k] < u_lb_) { + u_sat_[k] = u_lb_; + u_saturated_ = true; + } + + if (u_[k] > u_ub_) { + u_sat_[k] = u_ub_; + u_saturated_ = true; + } + } + + if ((control_type_ & kControlTypeIntY) && (tau_awu_ < lds::kInf)) { + // one-step back-calculation (calculate intE for u=u_sat) + // (Astroem, Rundqwist 1989 warn against using this...) + // int_e_awu_adjust_ = + // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); + + // gradual: see Astroem, Rundqwist 1989 + // this is a fudge for doing MIMO gradual + // n.b., went ahead and multiplied 1/T by dt so don't have to do that here. + int_e_awu_adjust_ = + k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); + // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); + + int_e_ += int_e_awu_adjust_; + } + + // set u to saturated version + u_ = u_sat_; +} + +template <typename System> +void Controller<System>::InitVars(size_t control_type) { + // initialize to default values + u_ref_ = Vector(sys_.n_u(), fill::zeros); + u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); + x_ref_ = Vector(sys_.n_x(), fill::zeros); + y_ref_ = Vector(sys_.n_y(), fill::zeros); + cx_ref_ = Vector(sys_.n_y(), fill::zeros); + + u_ = Vector(sys_.n_u(), fill::zeros); + u_return_ = Vector(sys_.n_u(), fill::zeros); + u_sat_ = Vector(sys_.n_u(), fill::zeros); + + // Might not need all these, so zero elements until later. + Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); + Kc_u_ = Matrix(0, 0, fill::zeros); + Kc_inty_ = Matrix(0, 0, fill::zeros); + + g_design_ = sys_.g(); // by default, same as model + dv_ = Vector(sys_.n_u(), fill::zeros); + v_ = Vector(sys_.n_u(), fill::zeros); + du_ref_ = Vector(sys_.n_u(), fill::zeros); + dv_ref_ = Vector(sys_.n_u(), fill::zeros); + v_ref_ = Vector(sys_.n_u(), fill::zeros); + + int_e_ = Vector(0, fill::zeros); + int_e_awu_adjust_ = Vector(0, fill::zeros); + + set_control_type(control_type); +} + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md index f1642701..8a0cbf0d 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md @@ -1,151 +1,151 @@ ---- -title: ldsCtrlEst_h/lds_fit.h -summary: LDS base fit type. - ---- - -# ldsCtrlEst_h/lds_fit.h - -LDS base fit type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/)** <br>LDS [Fit]() Type. | - -## Detailed Description - - - -This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDS_FIT_HPP -#define LDS_FIT_HPP - -// namespace -#include "lds.h" -#include "lds_uniform_mats.h" - -namespace lds { -class Fit { - public: - Fit() = default; - Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); - - virtual ~Fit() = default; - - // get methods - size_t n_u() const { return n_u_; }; - size_t n_x() const { return n_x_; }; - size_t n_y() const { return n_y_; }; - data_t dt() const { return dt_; }; - const Matrix& A() const { return A_; }; - const Matrix& B() const { return B_; }; - const Vector& g() const { return g_; }; - const Vector& m() const { return m_; }; - const Matrix& Q() const { return Q_; }; - const Vector& x0() const { return x0_; }; - const Matrix& P0() const { return P0_; }; - const Matrix& C() const { return C_; }; - const Vector& d() const { return d_; }; - // gets measurement noise - virtual const Matrix& R() const = 0; - - // set methods (e.g., seeding initial fit values) - void set_A(const Matrix& A) { Reassign(A_, A); }; - void set_B(const Matrix& B) { Reassign(B_, B); }; - void set_g(const Vector& g) { Reassign(g_, g); }; - void set_m(const Vector& m) { Reassign(m_, m); }; - void set_Q(const Matrix& Q) { - Reassign(Q_, Q); - ForceSymPD(Q_); - }; - virtual void set_R(const Matrix& R) = 0; - void set_x0(const Vector& x0) { Reassign(x0_, x0); }; - void set_P0(const Matrix& P0) { - Reassign(P0_, P0); - ForceSymPD(P0_); - }; - void set_C(const Matrix& C) { Reassign(C_, C); }; - void set_d(const Vector& d) { Reassign(d_, d); }; - - View f(Matrix& x, const Matrix& u, size_t t) { - x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; - return x.col(t); - }; - - View f(Matrix& x_pre, const Matrix& x_post, const Matrix& u, size_t t) { - x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; - return x_pre.col(t); - }; - - virtual View h(Matrix& y, const Matrix& x, size_t t) = 0; - - protected: - data_t dt_{}; - - // Dynamics - Matrix A_; - Matrix B_; - Vector g_; - Vector m_; - Matrix Q_; - - // Output - Matrix C_; - Vector d_; - Matrix R_; - - // initial conditions - Vector x0_; - Matrix P0_; - - size_t n_u_{}; - size_t n_x_{}; - size_t n_y_{}; -}; - -} // namespace lds -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_fit.h +summary: LDS base fit type. + +--- + +# ldsCtrlEst_h/lds_fit.h + +LDS base fit type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/)** <br>LDS [Fit]() Type. | + +## Detailed Description + + + +This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDS_FIT_HPP +#define LDS_FIT_HPP + +// namespace +#include "lds.h" +#include "lds_uniform_mats.h" + +namespace lds { +class Fit { + public: + Fit() = default; + Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); + + virtual ~Fit() = default; + + // get methods + size_t n_u() const { return n_u_; }; + size_t n_x() const { return n_x_; }; + size_t n_y() const { return n_y_; }; + data_t dt() const { return dt_; }; + const Matrix& A() const { return A_; }; + const Matrix& B() const { return B_; }; + const Vector& g() const { return g_; }; + const Vector& m() const { return m_; }; + const Matrix& Q() const { return Q_; }; + const Vector& x0() const { return x0_; }; + const Matrix& P0() const { return P0_; }; + const Matrix& C() const { return C_; }; + const Vector& d() const { return d_; }; + // gets measurement noise + virtual const Matrix& R() const = 0; + + // set methods (e.g., seeding initial fit values) + void set_A(const Matrix& A) { Reassign(A_, A); }; + void set_B(const Matrix& B) { Reassign(B_, B); }; + void set_g(const Vector& g) { Reassign(g_, g); }; + void set_m(const Vector& m) { Reassign(m_, m); }; + void set_Q(const Matrix& Q) { + Reassign(Q_, Q); + ForceSymPD(Q_); + }; + virtual void set_R(const Matrix& R) = 0; + void set_x0(const Vector& x0) { Reassign(x0_, x0); }; + void set_P0(const Matrix& P0) { + Reassign(P0_, P0); + ForceSymPD(P0_); + }; + void set_C(const Matrix& C) { Reassign(C_, C); }; + void set_d(const Vector& d) { Reassign(d_, d); }; + + View f(Matrix& x, const Matrix& u, size_t t) { + x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; + return x.col(t); + }; + + View f(Matrix& x_pre, const Matrix& x_post, const Matrix& u, size_t t) { + x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; + return x_pre.col(t); + }; + + virtual View h(Matrix& y, const Matrix& x, size_t t) = 0; + + protected: + data_t dt_{}; + + // Dynamics + Matrix A_; + Matrix B_; + Vector g_; + Vector m_; + Matrix Q_; + + // Output + Matrix C_; + Vector d_; + Matrix R_; + + // initial conditions + Vector x0_; + Matrix P0_; + + size_t n_u_{}; + size_t n_x_{}; + size_t n_y_{}; +}; + +} // namespace lds +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md index ae2dbecb..4b678e48 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md @@ -1,634 +1,634 @@ ---- -title: ldsCtrlEst_h/lds_fit_em.h -summary: subspace identification - ---- - -# ldsCtrlEst_h/lds_fit_em.h - -subspace identification [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** | - -## Detailed Description - - - -This file declares the type for fitting a linear dynamical system by expectation-maximization ([lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_EMAX_H -#define LDSCTRLEST_LDS_EMAX_H - -#include "lds_fit.h" - -namespace lds { - -template <typename Fit> -class EM { - static_assert(std::is_base_of<lds::Fit, Fit>::value, - "Fit must be derived from lds::Fit type."); - - public: - EM() = default; - - EM(size_t n_x, data_t dt, UniformMatrixList<kMatFreeDim2>&& u_train, - UniformMatrixList<kMatFreeDim2>&& z_train); - - EM(const Fit& fit0, UniformMatrixList<kMatFreeDim2>&& u_train, - UniformMatrixList<kMatFreeDim2>&& z_train); - - virtual ~EM() = default; - - const Fit& Run(bool calc_dynamics = true, bool calc_Q = true, - bool calc_init = true, bool calc_output = true, - bool calc_measurement = true, size_t max_iter = 100, - data_t tol = 1e-2); - - std::tuple<UniformMatrixList<kMatFreeDim2>, UniformMatrixList<kMatFreeDim2>> - ReturnData() { - auto tuple = std::make_tuple(std::move(u_), std::move(z_)); - // auto tuple = std::make_tuple(u_, z_); - u_ = UniformMatrixList<kMatFreeDim2>(); - z_ = UniformMatrixList<kMatFreeDim2>(); - return tuple; - } - - const std::vector<Matrix>& x() const { return x_; }; - const std::vector<Matrix>& y() const { return y_; }; - - const Matrix& sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; - const Matrix& sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; - const Matrix& sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; - size_t n_t_tot() { return n_t_tot_; } - - const Vector& theta() const { return theta_; }; - - protected: - void Expectation(bool force_common_initial = false); - - void Maximization(bool calc_dynamics = true, bool calc_Q = true, - bool calc_init = false, bool calc_output = false, - bool calc_measurement = false); - - void MaximizeDynamics(); - void MaximizeQ(); - void MaximizeInitial(); - virtual void MaximizeOutput() = 0; - virtual void MaximizeMeasurement() = 0; - - void Smooth(bool force_common_initial); - - virtual void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) = 0; - - void Reset(); - - void InitVars(); - - Vector UpdateTheta(); - - // input/output training data - UniformMatrixList<kMatFreeDim2> u_; - UniformMatrixList<kMatFreeDim2> z_; - std::vector<Matrix> x_; - std::vector<Cube> P_; - std::vector<Cube> P_t_tm1_; - std::vector<Matrix> y_; - Matrix diag_y_; - - // expectations calculated in E-step - Matrix sum_E_x_t_x_t_; - Matrix sum_E_xu_tm1_xu_tm1_; - Matrix sum_E_xu_t_xu_tm1_; - - Fit fit_; - Vector theta_; - - data_t dt_{}; - size_t n_u_{}; - size_t n_x_{}; - size_t n_y_{}; - size_t n_trials_{}; - std::vector<size_t> n_t_; - size_t n_t_tot_{}; -}; - -template <typename Fit> -EM<Fit>::EM(size_t n_x, data_t dt, UniformMatrixList<kMatFreeDim2>&& u_train, - UniformMatrixList<kMatFreeDim2>&& z_train) { - n_u_ = u_train.at(0).n_rows; - n_y_ = z_train.at(0).n_rows; - fit_ = Fit(n_u_, n_x, n_y_, dt); - u_ = std::move(u_train); - z_ = std::move(z_train); - InitVars(); -} - -template <typename Fit> -EM<Fit>::EM(const Fit& fit0, UniformMatrixList<kMatFreeDim2>&& u_train, - UniformMatrixList<kMatFreeDim2>&& z_train) { - // make sure fit dims match I/O data - if (fit0.n_u() != u_train.at(0).n_rows) { - throw std::runtime_error( - "Initial fit and input training data have inconsistent dimensions"); - } - if (fit0.n_y() != z_train.at(0).n_rows) { - throw std::runtime_error( - "Initial fit and output training data have inconsistent dimensions"); - } - - fit_ = fit0; - u_ = std::move(u_train); - z_ = std::move(z_train); - - InitVars(); -} - -template <typename Fit> -void EM<Fit>::InitVars() { - // check input/output data dimensions are consistent - if (z_.size() != u_.size()) { - throw std::runtime_error( - "I/O training data have different number of trials."); - } - n_trials_ = u_.size(); - - n_t_tot_ = 0; - n_t_ = std::vector<size_t>(n_trials_); - for (size_t trial = 0; trial < n_trials_; trial++) { - if (z_.at(trial).n_cols != u_.at(trial).n_cols) { - throw std::runtime_error( - "I/O training data have different number of time steps."); - } - n_t_[trial] = u_.at(trial).n_cols; - n_t_tot_ += n_t_[trial]; - } - - n_u_ = fit_.n_u(); - n_x_ = fit_.n_x(); - n_y_ = fit_.n_y(); - dt_ = fit_.dt(); - - x_ = std::vector<Matrix>(n_trials_); - P_ = std::vector<Cube>(n_trials_); - P_t_tm1_ = std::vector<Cube>(n_trials_); - y_ = std::vector<Matrix>(n_trials_); - for (size_t trial = 0; trial < n_trials_; trial++) { - x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); - P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); - P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); - y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); - } - - diag_y_ = Matrix(n_y_, n_y_, fill::zeros); - - // covariances in expectation step - sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); - sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); - sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); -} - -template <typename Fit> -const Fit& EM<Fit>::Run(bool calc_dynamics, bool calc_Q, bool calc_init, - bool calc_output, bool calc_measurement, - size_t max_iter, data_t tol) { - Reset(); // to initial conditions - - size_t n_params = - 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; - Vector theta(n_params); - Vector theta_new(n_params); - data_t max_dtheta = 1; - - // if solving for initial conditions, allow them be varied. - // otherwise, freeze at provided values. - bool force_common_initial = !calc_init; - - // go until parameter convergence - for (size_t l = 0; l < max_iter; l++) { - theta_ = UpdateTheta(); - - std::cout << "Iteration " << l + 1 << "/" << max_iter << " ...\n"; - - Expectation(force_common_initial); - Maximization(calc_dynamics, calc_Q, calc_init, calc_output, - calc_measurement); - - // check convergence - theta_new = UpdateTheta(); - - Vector dtheta = abs(theta_new - theta_) / abs(theta_); - // some parameters could be zero... - arma::uvec ubi_finite = find_finite(dtheta); - - max_dtheta = max(dtheta.elem(ubi_finite)); - std::cout << "max dtheta: " << max_dtheta << "\n"; - if (max_dtheta < tol) { - std::cout << "Converged.\n"; - break; - } - - std::cout << "\n"; - } - - return fit_; -} - -template <typename Fit> -void EM<Fit>::Smooth(bool force_common_initial) { - Matrix k_e(n_x_, n_y_); // estimator gain - Cube k_backfilt; // back-filtering gains - - // TODO(mfbolus): this loop could be made parallel - for (size_t trial = 0; trial < z_.size(); trial++) { - Matrix x_pre(n_x_, n_t_[trial], fill::zeros); - Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); - Matrix x_post(n_x_, n_t_[trial], fill::zeros); - Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); - - if (force_common_initial) // forces all trials to have same initial - // conditions. - { - x_[trial].col(0) = fit_.x0(); - P_[trial].slice(0) = fit_.P0(); - } - y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); - - // This *should not* be necessary but make sure P is symmetric. - ForceSymPD(P_[trial].slice(0)); - - x_pre.col(0) = x_[trial].col(0); - p_pre.slice(0) = P_[trial].slice(0); - - x_post.col(0) = x_[trial].col(0); - p_post.slice(0) = P_[trial].slice(0); - - // filter - for (size_t t = 1; t < n_t_[trial]; t++) { - // predict - fit_.f(x_pre, x_post, u_.at(trial), t); - fit_.h(y_[trial], x_pre, t); - diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel - - // update --> posterior estimation - RecurseKe(k_e, p_pre, p_post, t); - x_post.col(t) = - x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); - y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); - } - - // backfilter -> Smoothed estimate - // Reference: - // Shumway et Stoffer (1982) - ForceSymPD(p_post.slice(n_t_[trial] - 1)); - k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); - x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); - P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); - for (size_t t = (n_t_[trial] - 1); t > 0; t--) { - // TODO(mfmbolus): should not be necessary to force symm positive def - ForceSymPD(p_pre.slice(t)); - ForceSymPD(p_post.slice(t - 1)); - ForceSymPD(P_[trial].slice(t)); - k_backfilt.slice(t - 1) = - p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); - x_[trial].col(t - 1) = - x_post.col(t - 1) + - k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); - P_[trial].slice(t - 1) = - p_post.slice(t - 1) + k_backfilt.slice(t - 1) * - (P_[trial].slice(t) - p_pre.slice(t)) * - k_backfilt.slice(t - 1).t(); - } - - // do the same for P_t_tm1 - Matrix id(n_x_, n_x_, fill::eye); - P_t_tm1_[trial].slice(n_t_[trial] - 1) = - (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); - for (size_t t = (n_t_[trial] - 1); t > 1; t--) { - P_t_tm1_[trial].slice(t - 1) = - p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + - k_backfilt.slice(t - 1) * - (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * - k_backfilt.slice(t - 2).t(); - } - - // finally, get smoothed estimate of output - for (size_t t = 0; t < n_t_[trial]; t++) { - fit_.h(y_[trial], x_[trial], t); - } // samps loop - } // trial loop -} // Smooth - -// template <typename Fit> -// void EM<Fit>::RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) { -// // predict covar -// P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); - -// // update Ke -// Ke = P_pre.slice(t) * fit_.C().t() * -// inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); - -// // update cov -// // Reference: Ghahramani et Hinton (1996) -// P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); - -// // // n.b. for poisson : -// // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + -// fit_.Q(); -// // // update cov -// // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * -// // fit_.C()); -// // // update Ke -// // Ke = P_post.slice(t) * fit_.C(); -// } - -template <typename Fit> -void EM<Fit>::Expectation(bool force_common_initial) { - // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] - Smooth(force_common_initial); - - // now get the various forms of sum(E[xx']) needed - // n.b. Going to start at t=1 rather than 0 bc most max terms need that. - // so really "n_t_tot_" is (n_t_tot_-1) - n_t_tot_ = 0; - sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); - sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); - sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); - - Vector xu_tm1(n_x_ + n_u_, fill::zeros); - Vector xu_t(n_x_ + n_u_, fill::zeros); - - for (size_t trial = 0; trial < z_.size(); trial++) { - for (size_t t = 1; t < n_t_[trial]; t++) { - // ------------ sum_E_x_t_x_t ------------ - sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); - sum_E_x_t_x_t_ += P_[trial].slice(t); - - // ------------ sum_E_xu_tm1_xu_tm1 ------------ - xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); - sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); - sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += - P_[trial].slice(t - 1); - - // ------------ sum_E_xu_t_xu_tm1 ------------ - xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); - sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); - sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += - P_t_tm1_[trial].slice(t); - - n_t_tot_ += 1; - } // time - } // trial -} // Expectation - -template <typename Fit> -void EM<Fit>::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, - bool calc_output, bool calc_measurement) { - if (calc_output) { - MaximizeOutput(); - } - - if (calc_measurement) { - MaximizeMeasurement(); - } - - if (calc_dynamics) { - MaximizeDynamics(); - } - - if (calc_Q) { - MaximizeQ(); - } - - if (calc_init) { - MaximizeInitial(); - } -} // Maximization - -template <typename Fit> -void EM<Fit>::MaximizeDynamics() { - // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) - Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * - inv_sympd(sum_E_xu_tm1_xu_tm1_); - fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); - fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); - std::cout << "A_new[0]: " << fit_.A()[0] << "\n"; - std::cout << "B_new[0]: " << fit_.B()[0] << "\n"; -} - -template <typename Fit> -void EM<Fit>::MaximizeQ() { - // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) - // View sum_e_x_t_xu_tm1 = - // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); - // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * - // inv_sympd(sum_E_xu_tm1_xu_tm1_) * - // sum_e_x_t_xu_tm1.t(); - // q /= n_t_tot_; - - // this way is same as above iff dynamics were just updated: - // View sum_e_x_t_xu_tm1 = - // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); - // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); - // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); - // q /= n_t_tot_; - - // From scratch method: - // Q is covariance of the error between state and dynamics-predicted state - // (aka process noise) - // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})'] - // t-1 terms: - View sum_e_x_tm1_x_tm1 = - sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); - View sum_e_u_tm1_u_tm1 = - sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); - View sum_e_x_tm1_u_tm1 = - sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); - - // t, t-1 terms: - View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); - View sum_e_x_t_u_tm1 = - sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); - - Matrix q = sum_E_x_t_x_t_; - q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); - q -= sum_e_x_t_x_tm1 * fit_.A().t(); - q -= fit_.A() * sum_e_x_t_x_tm1.t(); - // input-related terms: - q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); - q -= sum_e_x_t_u_tm1 * fit_.B().t(); - q -= fit_.B() * sum_e_x_t_u_tm1.t(); - q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); - q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); - q /= n_t_tot_; - - fit_.set_Q(q); - std::cout << "Q_new[0]: " << fit_.Q()[0] << "\n"; - // std::cout << "Q_new: \n" << fit_.Q() << "\n"; -} - -template <typename Fit> -void EM<Fit>::MaximizeInitial() { - Vector x0 = fit_.x0(); - x0.zeros(); - for (size_t trial = 0; trial < z_.size(); trial++) { - x0 += x_[trial].col(0); - } - x0 /= z_.size(); - std::cout << "x0_new[0]: " << x0[0] << "\n"; - - // always recalc P0 even if the initial state is fixed (at zero, for - // example) - Matrix e_var(n_x_, n_x_, fill::zeros); - for (size_t trial = 0; trial < z_.size(); trial++) { - e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); - } - e_var /= z_.size(); - - // go ahead and subtract x0*x0' so don't have to below. - e_var -= x0 * x0.t(); - - // To get P0, going to get initial P_ per trial and average. - // (which might be wrong, but need a single number) - Matrix p0 = fit_.P0(); - p0.zeros(); - for (size_t trial = 0; trial < z_.size(); trial++) { - p0 += - (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; - } - p0 /= z_.size(); - - fit_.set_P0(p0); - std::cout << "P0_new[0]: " << fit_.P0()[0] << "\n"; -} - -template <typename Fit> -void EM<Fit>::MaximizeOutput() { - // solve for C+d: - Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); - Vector x1(n_x_ + 1, fill::zeros); - x1[n_x_] = 1.0; // augment with one to solve for bias - Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); - for (size_t trial = 0; trial < z_.size(); trial++) { - for (size_t t = 1; t < n_t_[trial]; t++) { - x1.subvec(0, n_x_ - 1) = x_[trial].col(t); - sum_zx += z_.at(trial).col(t) * x1.t(); - sum_e_x1_x1 += x1 * x1.t(); - sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); - } - } - Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); - fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); - fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); - std::cout << "C_new[0]: " << fit_.C()[0] << "\n"; - std::cout << "d_new[0]: " << fit_.d()[0] << "\n"; -} - -template <typename Fit> -void EM<Fit>::MaximizeMeasurement() { - // Solve for measurement noise covar - size_t n_t_tot = 0; - // Ghahgramani, Hinton 1996: - Matrix sum_zz(n_y_, n_y_, fill::zeros); - Matrix sum_yz(n_y_, n_y_, fill::zeros); - for (size_t trial = 0; trial < z_.size(); trial++) { - for (size_t t = 1; t < n_t_[trial]; t++) { - sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); - // Use Cnew: - sum_yz += - (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); - n_t_tot += 1; - } - } - fit_.set_R((sum_zz - sum_yz) / n_t_tot); - std::cout << "R_new[0]: " << fit_.R()[0] << "\n"; -} - -template <typename Fit> -void EM<Fit>::Reset() { - // reset to initial conditions - for (size_t trial = 0; trial < n_trials_; trial++) { - x_[trial].col(0) = fit_.x0(); - P_[trial].slice(0) = fit_.P0(); - y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); - } -} - -template <typename Fit> -Vector EM<Fit>::UpdateTheta() { - // TODO(mfbolus): This should include n_y_ more params for d. - size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; - if (fit_.R().n_elem > 0) { - n_params += n_y_ * n_y_; - } - Vector theta(n_params); - - size_t idx_start = 0; - theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); - idx_start += n_x_ * n_x_; - theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); - idx_start += n_x_ * n_u_; - theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); - idx_start += n_x_ * n_x_; - theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); - idx_start += n_x_; - theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); - idx_start += n_x_ * n_x_; - theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); - idx_start += n_y_ * n_x_; - theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); - idx_start += n_y_; - if (fit_.R().n_elem > 0) { - theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); - } - - return theta; -} - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_fit_em.h +summary: subspace identification + +--- + +# ldsCtrlEst_h/lds_fit_em.h + +subspace identification [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** | + +## Detailed Description + + + +This file declares the type for fitting a linear dynamical system by expectation-maximization ([lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_EMAX_H +#define LDSCTRLEST_LDS_EMAX_H + +#include "lds_fit.h" + +namespace lds { + +template <typename Fit> +class EM { + static_assert(std::is_base_of<lds::Fit, Fit>::value, + "Fit must be derived from lds::Fit type."); + + public: + EM() = default; + + EM(size_t n_x, data_t dt, UniformMatrixList<kMatFreeDim2>&& u_train, + UniformMatrixList<kMatFreeDim2>&& z_train); + + EM(const Fit& fit0, UniformMatrixList<kMatFreeDim2>&& u_train, + UniformMatrixList<kMatFreeDim2>&& z_train); + + virtual ~EM() = default; + + const Fit& Run(bool calc_dynamics = true, bool calc_Q = true, + bool calc_init = true, bool calc_output = true, + bool calc_measurement = true, size_t max_iter = 100, + data_t tol = 1e-2); + + std::tuple<UniformMatrixList<kMatFreeDim2>, UniformMatrixList<kMatFreeDim2>> + ReturnData() { + auto tuple = std::make_tuple(std::move(u_), std::move(z_)); + // auto tuple = std::make_tuple(u_, z_); + u_ = UniformMatrixList<kMatFreeDim2>(); + z_ = UniformMatrixList<kMatFreeDim2>(); + return tuple; + } + + const std::vector<Matrix>& x() const { return x_; }; + const std::vector<Matrix>& y() const { return y_; }; + + const Matrix& sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; + const Matrix& sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; + const Matrix& sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; + size_t n_t_tot() { return n_t_tot_; } + + const Vector& theta() const { return theta_; }; + + protected: + void Expectation(bool force_common_initial = false); + + void Maximization(bool calc_dynamics = true, bool calc_Q = true, + bool calc_init = false, bool calc_output = false, + bool calc_measurement = false); + + void MaximizeDynamics(); + void MaximizeQ(); + void MaximizeInitial(); + virtual void MaximizeOutput() = 0; + virtual void MaximizeMeasurement() = 0; + + void Smooth(bool force_common_initial); + + virtual void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) = 0; + + void Reset(); + + void InitVars(); + + Vector UpdateTheta(); + + // input/output training data + UniformMatrixList<kMatFreeDim2> u_; + UniformMatrixList<kMatFreeDim2> z_; + std::vector<Matrix> x_; + std::vector<Cube> P_; + std::vector<Cube> P_t_tm1_; + std::vector<Matrix> y_; + Matrix diag_y_; + + // expectations calculated in E-step + Matrix sum_E_x_t_x_t_; + Matrix sum_E_xu_tm1_xu_tm1_; + Matrix sum_E_xu_t_xu_tm1_; + + Fit fit_; + Vector theta_; + + data_t dt_{}; + size_t n_u_{}; + size_t n_x_{}; + size_t n_y_{}; + size_t n_trials_{}; + std::vector<size_t> n_t_; + size_t n_t_tot_{}; +}; + +template <typename Fit> +EM<Fit>::EM(size_t n_x, data_t dt, UniformMatrixList<kMatFreeDim2>&& u_train, + UniformMatrixList<kMatFreeDim2>&& z_train) { + n_u_ = u_train.at(0).n_rows; + n_y_ = z_train.at(0).n_rows; + fit_ = Fit(n_u_, n_x, n_y_, dt); + u_ = std::move(u_train); + z_ = std::move(z_train); + InitVars(); +} + +template <typename Fit> +EM<Fit>::EM(const Fit& fit0, UniformMatrixList<kMatFreeDim2>&& u_train, + UniformMatrixList<kMatFreeDim2>&& z_train) { + // make sure fit dims match I/O data + if (fit0.n_u() != u_train.at(0).n_rows) { + throw std::runtime_error( + "Initial fit and input training data have inconsistent dimensions"); + } + if (fit0.n_y() != z_train.at(0).n_rows) { + throw std::runtime_error( + "Initial fit and output training data have inconsistent dimensions"); + } + + fit_ = fit0; + u_ = std::move(u_train); + z_ = std::move(z_train); + + InitVars(); +} + +template <typename Fit> +void EM<Fit>::InitVars() { + // check input/output data dimensions are consistent + if (z_.size() != u_.size()) { + throw std::runtime_error( + "I/O training data have different number of trials."); + } + n_trials_ = u_.size(); + + n_t_tot_ = 0; + n_t_ = std::vector<size_t>(n_trials_); + for (size_t trial = 0; trial < n_trials_; trial++) { + if (z_.at(trial).n_cols != u_.at(trial).n_cols) { + throw std::runtime_error( + "I/O training data have different number of time steps."); + } + n_t_[trial] = u_.at(trial).n_cols; + n_t_tot_ += n_t_[trial]; + } + + n_u_ = fit_.n_u(); + n_x_ = fit_.n_x(); + n_y_ = fit_.n_y(); + dt_ = fit_.dt(); + + x_ = std::vector<Matrix>(n_trials_); + P_ = std::vector<Cube>(n_trials_); + P_t_tm1_ = std::vector<Cube>(n_trials_); + y_ = std::vector<Matrix>(n_trials_); + for (size_t trial = 0; trial < n_trials_; trial++) { + x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); + P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); + P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); + y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); + } + + diag_y_ = Matrix(n_y_, n_y_, fill::zeros); + + // covariances in expectation step + sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); + sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); + sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); +} + +template <typename Fit> +const Fit& EM<Fit>::Run(bool calc_dynamics, bool calc_Q, bool calc_init, + bool calc_output, bool calc_measurement, + size_t max_iter, data_t tol) { + Reset(); // to initial conditions + + size_t n_params = + 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; + Vector theta(n_params); + Vector theta_new(n_params); + data_t max_dtheta = 1; + + // if solving for initial conditions, allow them be varied. + // otherwise, freeze at provided values. + bool force_common_initial = !calc_init; + + // go until parameter convergence + for (size_t l = 0; l < max_iter; l++) { + theta_ = UpdateTheta(); + + std::cout << "Iteration " << l + 1 << "/" << max_iter << " ...\n"; + + Expectation(force_common_initial); + Maximization(calc_dynamics, calc_Q, calc_init, calc_output, + calc_measurement); + + // check convergence + theta_new = UpdateTheta(); + + Vector dtheta = abs(theta_new - theta_) / abs(theta_); + // some parameters could be zero... + arma::uvec ubi_finite = find_finite(dtheta); + + max_dtheta = max(dtheta.elem(ubi_finite)); + std::cout << "max dtheta: " << max_dtheta << "\n"; + if (max_dtheta < tol) { + std::cout << "Converged.\n"; + break; + } + + std::cout << "\n"; + } + + return fit_; +} + +template <typename Fit> +void EM<Fit>::Smooth(bool force_common_initial) { + Matrix k_e(n_x_, n_y_); // estimator gain + Cube k_backfilt; // back-filtering gains + + // TODO(mfbolus): this loop could be made parallel + for (size_t trial = 0; trial < z_.size(); trial++) { + Matrix x_pre(n_x_, n_t_[trial], fill::zeros); + Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); + Matrix x_post(n_x_, n_t_[trial], fill::zeros); + Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); + + if (force_common_initial) // forces all trials to have same initial + // conditions. + { + x_[trial].col(0) = fit_.x0(); + P_[trial].slice(0) = fit_.P0(); + } + y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); + + // This *should not* be necessary but make sure P is symmetric. + ForceSymPD(P_[trial].slice(0)); + + x_pre.col(0) = x_[trial].col(0); + p_pre.slice(0) = P_[trial].slice(0); + + x_post.col(0) = x_[trial].col(0); + p_post.slice(0) = P_[trial].slice(0); + + // filter + for (size_t t = 1; t < n_t_[trial]; t++) { + // predict + fit_.f(x_pre, x_post, u_.at(trial), t); + fit_.h(y_[trial], x_pre, t); + diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel + + // update --> posterior estimation + RecurseKe(k_e, p_pre, p_post, t); + x_post.col(t) = + x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); + y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); + } + + // backfilter -> Smoothed estimate + // Reference: + // Shumway et Stoffer (1982) + ForceSymPD(p_post.slice(n_t_[trial] - 1)); + k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); + x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); + P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); + for (size_t t = (n_t_[trial] - 1); t > 0; t--) { + // TODO(mfmbolus): should not be necessary to force symm positive def + ForceSymPD(p_pre.slice(t)); + ForceSymPD(p_post.slice(t - 1)); + ForceSymPD(P_[trial].slice(t)); + k_backfilt.slice(t - 1) = + p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); + x_[trial].col(t - 1) = + x_post.col(t - 1) + + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); + P_[trial].slice(t - 1) = + p_post.slice(t - 1) + k_backfilt.slice(t - 1) * + (P_[trial].slice(t) - p_pre.slice(t)) * + k_backfilt.slice(t - 1).t(); + } + + // do the same for P_t_tm1 + Matrix id(n_x_, n_x_, fill::eye); + P_t_tm1_[trial].slice(n_t_[trial] - 1) = + (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); + for (size_t t = (n_t_[trial] - 1); t > 1; t--) { + P_t_tm1_[trial].slice(t - 1) = + p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + + k_backfilt.slice(t - 1) * + (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * + k_backfilt.slice(t - 2).t(); + } + + // finally, get smoothed estimate of output + for (size_t t = 0; t < n_t_[trial]; t++) { + fit_.h(y_[trial], x_[trial], t); + } // samps loop + } // trial loop +} // Smooth + +// template <typename Fit> +// void EM<Fit>::RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) { +// // predict covar +// P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); + +// // update Ke +// Ke = P_pre.slice(t) * fit_.C().t() * +// inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); + +// // update cov +// // Reference: Ghahramani et Hinton (1996) +// P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); + +// // // n.b. for poisson : +// // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + +// fit_.Q(); +// // // update cov +// // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * +// // fit_.C()); +// // // update Ke +// // Ke = P_post.slice(t) * fit_.C(); +// } + +template <typename Fit> +void EM<Fit>::Expectation(bool force_common_initial) { + // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] + Smooth(force_common_initial); + + // now get the various forms of sum(E[xx']) needed + // n.b. Going to start at t=1 rather than 0 bc most max terms need that. + // so really "n_t_tot_" is (n_t_tot_-1) + n_t_tot_ = 0; + sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); + sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); + sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); + + Vector xu_tm1(n_x_ + n_u_, fill::zeros); + Vector xu_t(n_x_ + n_u_, fill::zeros); + + for (size_t trial = 0; trial < z_.size(); trial++) { + for (size_t t = 1; t < n_t_[trial]; t++) { + // ------------ sum_E_x_t_x_t ------------ + sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); + sum_E_x_t_x_t_ += P_[trial].slice(t); + + // ------------ sum_E_xu_tm1_xu_tm1 ------------ + xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); + sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); + sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += + P_[trial].slice(t - 1); + + // ------------ sum_E_xu_t_xu_tm1 ------------ + xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); + sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); + sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += + P_t_tm1_[trial].slice(t); + + n_t_tot_ += 1; + } // time + } // trial +} // Expectation + +template <typename Fit> +void EM<Fit>::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, + bool calc_output, bool calc_measurement) { + if (calc_output) { + MaximizeOutput(); + } + + if (calc_measurement) { + MaximizeMeasurement(); + } + + if (calc_dynamics) { + MaximizeDynamics(); + } + + if (calc_Q) { + MaximizeQ(); + } + + if (calc_init) { + MaximizeInitial(); + } +} // Maximization + +template <typename Fit> +void EM<Fit>::MaximizeDynamics() { + // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) + Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * + inv_sympd(sum_E_xu_tm1_xu_tm1_); + fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); + fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); + std::cout << "A_new[0]: " << fit_.A()[0] << "\n"; + std::cout << "B_new[0]: " << fit_.B()[0] << "\n"; +} + +template <typename Fit> +void EM<Fit>::MaximizeQ() { + // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) + // View sum_e_x_t_xu_tm1 = + // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); + // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * + // inv_sympd(sum_E_xu_tm1_xu_tm1_) * + // sum_e_x_t_xu_tm1.t(); + // q /= n_t_tot_; + + // this way is same as above iff dynamics were just updated: + // View sum_e_x_t_xu_tm1 = + // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); + // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); + // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); + // q /= n_t_tot_; + + // From scratch method: + // Q is covariance of the error between state and dynamics-predicted state + // (aka process noise) + // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})'] + // t-1 terms: + View sum_e_x_tm1_x_tm1 = + sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); + View sum_e_u_tm1_u_tm1 = + sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); + View sum_e_x_tm1_u_tm1 = + sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); + + // t, t-1 terms: + View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); + View sum_e_x_t_u_tm1 = + sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); + + Matrix q = sum_E_x_t_x_t_; + q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); + q -= sum_e_x_t_x_tm1 * fit_.A().t(); + q -= fit_.A() * sum_e_x_t_x_tm1.t(); + // input-related terms: + q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); + q -= sum_e_x_t_u_tm1 * fit_.B().t(); + q -= fit_.B() * sum_e_x_t_u_tm1.t(); + q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); + q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); + q /= n_t_tot_; + + fit_.set_Q(q); + std::cout << "Q_new[0]: " << fit_.Q()[0] << "\n"; + // std::cout << "Q_new: \n" << fit_.Q() << "\n"; +} + +template <typename Fit> +void EM<Fit>::MaximizeInitial() { + Vector x0 = fit_.x0(); + x0.zeros(); + for (size_t trial = 0; trial < z_.size(); trial++) { + x0 += x_[trial].col(0); + } + x0 /= z_.size(); + std::cout << "x0_new[0]: " << x0[0] << "\n"; + + // always recalc P0 even if the initial state is fixed (at zero, for + // example) + Matrix e_var(n_x_, n_x_, fill::zeros); + for (size_t trial = 0; trial < z_.size(); trial++) { + e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); + } + e_var /= z_.size(); + + // go ahead and subtract x0*x0' so don't have to below. + e_var -= x0 * x0.t(); + + // To get P0, going to get initial P_ per trial and average. + // (which might be wrong, but need a single number) + Matrix p0 = fit_.P0(); + p0.zeros(); + for (size_t trial = 0; trial < z_.size(); trial++) { + p0 += + (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; + } + p0 /= z_.size(); + + fit_.set_P0(p0); + std::cout << "P0_new[0]: " << fit_.P0()[0] << "\n"; +} + +template <typename Fit> +void EM<Fit>::MaximizeOutput() { + // solve for C+d: + Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); + Vector x1(n_x_ + 1, fill::zeros); + x1[n_x_] = 1.0; // augment with one to solve for bias + Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); + for (size_t trial = 0; trial < z_.size(); trial++) { + for (size_t t = 1; t < n_t_[trial]; t++) { + x1.subvec(0, n_x_ - 1) = x_[trial].col(t); + sum_zx += z_.at(trial).col(t) * x1.t(); + sum_e_x1_x1 += x1 * x1.t(); + sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); + } + } + Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); + fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); + fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); + std::cout << "C_new[0]: " << fit_.C()[0] << "\n"; + std::cout << "d_new[0]: " << fit_.d()[0] << "\n"; +} + +template <typename Fit> +void EM<Fit>::MaximizeMeasurement() { + // Solve for measurement noise covar + size_t n_t_tot = 0; + // Ghahgramani, Hinton 1996: + Matrix sum_zz(n_y_, n_y_, fill::zeros); + Matrix sum_yz(n_y_, n_y_, fill::zeros); + for (size_t trial = 0; trial < z_.size(); trial++) { + for (size_t t = 1; t < n_t_[trial]; t++) { + sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); + // Use Cnew: + sum_yz += + (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); + n_t_tot += 1; + } + } + fit_.set_R((sum_zz - sum_yz) / n_t_tot); + std::cout << "R_new[0]: " << fit_.R()[0] << "\n"; +} + +template <typename Fit> +void EM<Fit>::Reset() { + // reset to initial conditions + for (size_t trial = 0; trial < n_trials_; trial++) { + x_[trial].col(0) = fit_.x0(); + P_[trial].slice(0) = fit_.P0(); + y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); + } +} + +template <typename Fit> +Vector EM<Fit>::UpdateTheta() { + // TODO(mfbolus): This should include n_y_ more params for d. + size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; + if (fit_.R().n_elem > 0) { + n_params += n_y_ * n_y_; + } + Vector theta(n_params); + + size_t idx_start = 0; + theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); + idx_start += n_x_ * n_x_; + theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); + idx_start += n_x_ * n_u_; + theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); + idx_start += n_x_ * n_x_; + theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); + idx_start += n_x_; + theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); + idx_start += n_x_ * n_x_; + theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); + idx_start += n_y_ * n_x_; + theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); + idx_start += n_y_; + if (fit_.R().n_elem > 0) { + theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); + } + + return theta; +} + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md index c0fd6b5d..05d7bdd1 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md @@ -1,581 +1,581 @@ ---- -title: ldsCtrlEst_h/lds_fit_ssid.h -summary: subspace identification - ---- - -# ldsCtrlEst_h/lds_fit_ssid.h - -subspace identification [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** | - -## Detailed Description - - - -This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm (`[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)<Fit>`). - -References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_FIT_SSID_H -#define LDSCTRLEST_LDS_FIT_SSID_H - -#include "lds_fit.h" - -namespace lds { - -template <typename Fit> -class SSID { - static_assert(std::is_base_of<lds::Fit, Fit>::value, - "Fit must be derived from lds::Fit type."); - - public: - SSID() = default; - - SSID(size_t n_x, size_t n_h, data_t dt, - UniformMatrixList<kMatFreeDim2>&& u_train, - UniformMatrixList<kMatFreeDim2>&& z_train, - const Vector& d = Vector(1).fill(-kInf)); - - std::tuple<Fit, Vector> Run(SSIDWt ssid_wt); - - std::tuple<UniformMatrixList<kMatFreeDim2>, UniformMatrixList<kMatFreeDim2>> - ReturnData() { - auto tuple = std::make_tuple(std::move(u_), std::move(z_)); - u_ = UniformMatrixList<kMatFreeDim2>(); - z_ = UniformMatrixList<kMatFreeDim2>(); - return tuple; - } - - protected: - void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); - - void CreateHankelDataMat(); - - virtual void DecomposeData() = 0; - - void CalcSVD(SSIDWt wt); - - void Solve(data_t wt_dc); - - void RecomputeExtObs(); - - // input/output training data - UniformMatrixList<kMatFreeDim2> u_; - UniformMatrixList<kMatFreeDim2> z_; - Matrix D_; - - Fit fit_; - Matrix g_dc_; - - data_t dt_{}; - size_t n_u_{}; - size_t n_x_{}; - size_t n_y_{}; - size_t n_h_{}; - size_t n_trials_{}; - std::vector<size_t> n_t_; - size_t n_t_tot_{}; - - Matrix L_; - Vector s_; - Matrix ext_obs_t_; -}; - -template <typename Fit> -SSID<Fit>::SSID(size_t n_x, size_t n_h, data_t dt, - UniformMatrixList<kMatFreeDim2>&& u_train, - UniformMatrixList<kMatFreeDim2>&& z_train, const Vector& d) { - // check input/output data dimensions are consistent - if (z_train.size() != u_train.size()) { - throw std::runtime_error( - "I/O training data have different number of trials."); - } - n_trials_ = u_train.size(); - - n_t_tot_ = 0; - n_t_ = std::vector<size_t>(n_trials_); - for (size_t trial = 0; trial < n_trials_; trial++) { - if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { - throw std::runtime_error( - "I/O training data have different number of time steps."); - } - n_t_[trial] = u_train.at(trial).n_cols; - n_t_tot_ += n_t_[trial]; - } - - dt_ = dt; - n_x_ = n_x; - n_u_ = u_train.at(0).n_rows; - n_y_ = z_train.at(0).n_rows; - n_h_ = n_h; - - // dimensionality check for eventual block-hankel data matrix - size_t len = n_t_tot_ - 2 * n_h_ + 1; - if (len < (2 * n_h_ * (n_u_ + n_y_))) { - std::ostringstream ss; - ss << "Dataset problem! More rows than columns in block-hankel data " - "matrix: 2*(n_u+n_y)*n_h > data-length! Need higher data-length or " - "lower n_h."; - throw std::runtime_error(ss.str()); - } - - fit_ = Fit(n_u_, n_x_, n_y_, dt_); - - u_ = std::move(u_train); - z_ = std::move(z_train); - - if (!d.is_finite() || (d.n_rows != n_y_)) { - // TODO(mfbolus): implement least-square solution for impulse response with - // a second input of ones. Data-driven way of accounting for offset *not* - // driven by an input. - // - // For now, calculate output bias (d) as the - // output wherever the stimulus has not been on for some amount of time. - // convolve u with rectangle and take all samples. This is a reasonable - // approach, since often when autonomous systems are fit (i.e., systems with - // no input), they will subtract off the mean of the output. This - // essentially amounts to setting output bias to the mean of the output when - // there is no stimulation. - data_t t_silence = 0.1; - data_t thresh_silence = 0.001; - CalcD(t_silence, thresh_silence); - } else { - fit_.set_d(d); - } -} - -template <typename Fit> -std::tuple<Fit, Vector> SSID<Fit>::Run(SSIDWt ssid_wt) { - // the weight on minimizing dc I/O gain only works for gaussian, - // and hopefully not necessary with appropriate dataset. - data_t wt_dc = 0; - // std::cout << "creating hankel mat\n"; - CreateHankelDataMat(); - // std::cout << "decomposing data\n"; - DecomposeData(); - // std::cout << "calculating svd\n"; - CalcSVD(ssid_wt); - // std::cout << "solving for params\n"; - Solve(wt_dc); - // std::cout << "fin\n"; - return std::make_tuple(fit_, s_); -} - -template <typename Fit> -void SSID<Fit>::CalcD(data_t t_silence, data_t thresh_silence) { - Vector d(z_.at(0).n_rows, fill::zeros); - Vector win(static_cast<size_t>(t_silence / dt_), fill::ones); - Vector sum_z_silence(n_y_, fill::zeros); - size_t n_silence(0); - for (size_t trial = 0; trial < u_.size(); trial++) { - // find silent samples - // start by convolving with - Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); - Vector u_conv = conv(sum_u, win, "same"); - - // get only the samples that are silent... - arma::uvec ubi_silence = find(u_conv <= thresh_silence); - if (ubi_silence.n_elem > 0) { - sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); - n_silence += ubi_silence.n_elem; - } - } - if (n_silence > 0) { - d = sum_z_silence / n_silence; - } - fit_.set_d(d); -} - -template <typename Fit> -void SSID<Fit>::CreateHankelDataMat() { - // temporary copy of data - Matrix z(n_y_, n_t_tot_, fill::zeros); - Matrix u(n_u_, n_t_tot_, fill::zeros); - size_t so_far(0); - - for (size_t trial = 0; trial < z_.size(); trial++) { - z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); - u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); - so_far += n_t_.at(trial); - } - - // remove output bias - z.each_col() -= fit_.d(); - - // calculate I/O gain @ DC while data in convenient form - g_dc_ = z * pinv(u); - // std::cout << "G0_data = " << g_dc_ << "\n"; - - // create hankel data matrix - size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat - - // block-hankel data matrix - D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); - // past input - auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); - // future input - auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); - // past output - auto y_p = - D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); - // future output - auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, - 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); - - size_t idx = 0; - for (size_t k = 0; k < len; k++) { - idx = 0; - for (size_t kk = k; kk < (n_h_ + k); kk++) { - u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); - idx += n_u_; - } - - idx = 0; - for (size_t kk = (n_h_ + k); kk < (2 * n_h_ + k); kk++) { - u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); - idx += n_u_; - } - - idx = 0; - for (size_t kk = k; kk < (n_h_ + k); kk++) { - y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); - idx += n_y_; - } - - idx = 0; - for (size_t kk = (n_h_ + k); kk < (2 * n_h_ + k); kk++) { - y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); - idx += n_y_; - } - } - - D_ /= sqrt(static_cast<data_t>(len)); -} - -// template <typename Fit> -// void SSID<Fit>::DecomposeData() { -// // do LQ decomp instead of calculating covariance expensive way -// // Note that "R" in van Overschee is lower-triangular (L), not "R" in QR -// // decomp. Very confusing. -// Matrix q_t; -// lq(L_, q_t, D_); -// // van Overschee zeros out the other elements. -// L_ = trimatl(L_); -// } - -template <typename Fit> -void SSID<Fit>::CalcSVD(SSIDWt wt) { - // submats that will be needed: - auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, - n_h_ * (2 * n_u_ + n_y_) - 1); - auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); - auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); - auto R_23_13 = - L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); - auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, - n_h_ * (2 * n_u_ + n_y_) - 1); - auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, - n_h_ * (2 * n_u_) - 1); - auto R_44 = - L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, - n_h_ * (2 * n_u_ + n_y_) - 1); - auto R_56_14 = - L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, - n_h_ * (2 * n_u_ + n_y_) - 1); - - Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); - auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); - auto Luf = - Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); - auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, - n_h_ * (2 * n_u_ + n_y_) - 1); - - // aka: R_f - Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, - 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); - // from van Overschee subid.m: - // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs - - Matrix U; - Matrix V; - switch (wt) { - case kSSIDNone: { - // No weighting. (what van Overschee calls "N4SID") - Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; - arma::svd(U, s_, V, O_k_sans_Qt, "std"); - } break; - case kSSIDMOESP: { - // MOESP weighting - // This is what they use in the "robust" algorithm van Overschee, de Moor - // 1996 - Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; - Matrix O_k_ortho_Uf_sans_Qt = - join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); - svd(U, s_, V, O_k_ortho_Uf_sans_Qt, "std"); - } break; - case kSSIDCVA: { - // CVA weighting - // See van Overschee's matlab code (subid.m): - // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems - Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; - Matrix O_k_ortho_Uf_sans_Qt = - join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); - - Matrix inv_w1; - Matrix qt1; - lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) - inv_w1 = trimatl(inv_w1); - inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); - Matrix w_o_w = arma::solve( - inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively - // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt - svd(U, s_, V, w_o_w, "std"); - - U = inv_w1 * U; - break; - } - } - - // Truncate to model order (heart of ssid method) - auto s_hat = s_.subvec(0, n_x_ - 1); - Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); - auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); - - // get extended observability and controllability mats - ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix -} - -template <typename Fit> -void SSID<Fit>::Solve(data_t wt_dc) { - // required submats - auto R_56_14 = - L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, - n_h_ * (2 * n_u_ + n_y_) - 1); - auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, - n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); - auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, - 2 * n_h_ * (n_u_ + n_y_) - 1, - n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); - auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, - n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, - n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); - auto R_56_15 = - L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, - n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); - - // Solve for params using appropriate algorithm: - // robust deterministic/stochastic algorithm in van Overschee 1996 - // algorithm that the authors say "works" in practice. - auto ext_obs_tm1 = ext_obs_t_.submat( - 0, 0, ext_obs_t_.n_rows - 1 - n_y_, - ext_obs_t_.n_cols - 1); // extended observability matrix - - // This is what textbook (1996) says: - // - // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); - // - // HOWEVER, do not know why but have to fill the last place with zeros like - // authors' matlab implementation (see `subid.m`) - // Otherwise, get ridiculous covariances (although A,C estimates are close to - // same...) - Matrix Tr = join_vert( - join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), - R_23_15); - Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); - Matrix S = Tl * pinv(Tr); - - // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure - // stability. - fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); - Matrix ext_obs_t_p1 = join_vert( - ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), - Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); - fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); - - // At this point, van Overschee & de Moor suggest re-calculating ext_obs_t_, - // ext_obs_tm1 from (A, C) because it was just an approximation. This is - RecomputeExtObs(); - ext_obs_tm1 = ext_obs_t_.submat( - 0, 0, ext_obs_t_.n_rows - 1 - n_y_, - ext_obs_t_.n_cols - 1); // extended observability matrix - Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); - Tr = join_vert( - join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), - R_23_15); - S = Tl * pinv(Tr); - - Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); - Matrix Mcurly = pinv(ext_obs_tm1); - Matrix Pcurly = Tl - Lcurly * R_56_15; - Vector Pvec = vectorise(Pcurly); - Matrix Qcurly = R_23_15; - - // Identify [D; B], assuming D=0 and ensuring DC gain is correct - Matrix sum_QcurlyT_kron_Ncurly( - (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), - fill::zeros); - - Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, - fill::eye); - eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, - eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; - - // van Overschee (1996) p. 126 - Matrix N1_Tl = -Lcurly; - N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += - join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); - N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += - Matrix(n_y_, n_y_, fill::eye); - - Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); - Matrix N_k; - for (size_t k = 0; k < n_h_; k++) { - auto Qcurly_k = - Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); - - Nk_Tl.zeros(); - Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = - N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); - N_k = Nk_Tl * eye_ext_obs_tm1; - - sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); - } - - Matrix err_vec; - if (wt_dc > 0) { - // Constraints enforced by weighted least squares - // - // Reference: - // - // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly - // Excited Industrial Systems. Conference in Decision and Control. - - // constraint 1: assume D=0 --> remove the components for Dvec (this is - // actually a hard constraint in that it ignores D) - Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; - sum_QcurlyT_kron_Ncurly = - Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); - - size_t kkk = 0; - for (size_t k = 1; k < (n_u_ + 1); k++) { - size_t start_idx = k * (n_y_ + n_x_) - n_x_; - for (size_t kk = 0; kk < n_x_; kk++) { - sum_QcurlyT_kron_Ncurly.col(kkk) = - sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); - kkk++; - } - } - - // constraint 2: Make sure DC I/O gain is correct - Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); - Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); - Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); - Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = - join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); - - // WEIGHTED LS - // Important in practice because I care a lot about at least getting the DC - // gain correct. Put x weight on minimizing error at DC, relative to others - Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, - sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); - // Make weight on minimizing DC error immense so at least that - // should be nailed. - size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; - size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; - size_t stop_row = w.n_rows - 1; - size_t stop_col = w.n_cols - 1; - // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale - // weight with data length? - w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; - Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * - sum_QcurlyT_kron_Ncurly_b_to_g0) * - sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; - - fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); - - // Calculate residuals and their cov. - // Because I've added constraints, I need to re-calculate the right term - // with b_vec instead of how van Overschee do in final algorithm. - err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; - } else { - // default way: *no* constraint on G0 or D=0 - Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; - // TODO(mfbolus) n.b., this gets thrown away... - // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); - fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); - err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; - } - // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); - - // TODO(mfbolus): Something is wrong with the error calculation above. - // Use the way van overschee does it in `subid.m` - // WARNING: this ignores any above constraints, so Q, R will be approximate... - Matrix err = Tl - S * Tr; - Matrix cov_err = err * err.t(); - fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); - fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); -} - -template <typename Fit> -void SSID<Fit>::RecomputeExtObs() { - ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); - for (size_t k = 2; k < (n_h_ + 1); k++) { - ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = - ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, - ext_obs_t_.n_cols - 1) * - fit_.A(); - } -} - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_fit_ssid.h +summary: subspace identification + +--- + +# ldsCtrlEst_h/lds_fit_ssid.h + +subspace identification [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** | + +## Detailed Description + + + +This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm (`[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)<Fit>`). + +References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_FIT_SSID_H +#define LDSCTRLEST_LDS_FIT_SSID_H + +#include "lds_fit.h" + +namespace lds { + +template <typename Fit> +class SSID { + static_assert(std::is_base_of<lds::Fit, Fit>::value, + "Fit must be derived from lds::Fit type."); + + public: + SSID() = default; + + SSID(size_t n_x, size_t n_h, data_t dt, + UniformMatrixList<kMatFreeDim2>&& u_train, + UniformMatrixList<kMatFreeDim2>&& z_train, + const Vector& d = Vector(1).fill(-kInf)); + + std::tuple<Fit, Vector> Run(SSIDWt ssid_wt); + + std::tuple<UniformMatrixList<kMatFreeDim2>, UniformMatrixList<kMatFreeDim2>> + ReturnData() { + auto tuple = std::make_tuple(std::move(u_), std::move(z_)); + u_ = UniformMatrixList<kMatFreeDim2>(); + z_ = UniformMatrixList<kMatFreeDim2>(); + return tuple; + } + + protected: + void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); + + void CreateHankelDataMat(); + + virtual void DecomposeData() = 0; + + void CalcSVD(SSIDWt wt); + + void Solve(data_t wt_dc); + + void RecomputeExtObs(); + + // input/output training data + UniformMatrixList<kMatFreeDim2> u_; + UniformMatrixList<kMatFreeDim2> z_; + Matrix D_; + + Fit fit_; + Matrix g_dc_; + + data_t dt_{}; + size_t n_u_{}; + size_t n_x_{}; + size_t n_y_{}; + size_t n_h_{}; + size_t n_trials_{}; + std::vector<size_t> n_t_; + size_t n_t_tot_{}; + + Matrix L_; + Vector s_; + Matrix ext_obs_t_; +}; + +template <typename Fit> +SSID<Fit>::SSID(size_t n_x, size_t n_h, data_t dt, + UniformMatrixList<kMatFreeDim2>&& u_train, + UniformMatrixList<kMatFreeDim2>&& z_train, const Vector& d) { + // check input/output data dimensions are consistent + if (z_train.size() != u_train.size()) { + throw std::runtime_error( + "I/O training data have different number of trials."); + } + n_trials_ = u_train.size(); + + n_t_tot_ = 0; + n_t_ = std::vector<size_t>(n_trials_); + for (size_t trial = 0; trial < n_trials_; trial++) { + if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { + throw std::runtime_error( + "I/O training data have different number of time steps."); + } + n_t_[trial] = u_train.at(trial).n_cols; + n_t_tot_ += n_t_[trial]; + } + + dt_ = dt; + n_x_ = n_x; + n_u_ = u_train.at(0).n_rows; + n_y_ = z_train.at(0).n_rows; + n_h_ = n_h; + + // dimensionality check for eventual block-hankel data matrix + size_t len = n_t_tot_ - 2 * n_h_ + 1; + if (len < (2 * n_h_ * (n_u_ + n_y_))) { + std::ostringstream ss; + ss << "Dataset problem! More rows than columns in block-hankel data " + "matrix: 2*(n_u+n_y)*n_h > data-length! Need higher data-length or " + "lower n_h."; + throw std::runtime_error(ss.str()); + } + + fit_ = Fit(n_u_, n_x_, n_y_, dt_); + + u_ = std::move(u_train); + z_ = std::move(z_train); + + if (!d.is_finite() || (d.n_rows != n_y_)) { + // TODO(mfbolus): implement least-square solution for impulse response with + // a second input of ones. Data-driven way of accounting for offset *not* + // driven by an input. + // + // For now, calculate output bias (d) as the + // output wherever the stimulus has not been on for some amount of time. + // convolve u with rectangle and take all samples. This is a reasonable + // approach, since often when autonomous systems are fit (i.e., systems with + // no input), they will subtract off the mean of the output. This + // essentially amounts to setting output bias to the mean of the output when + // there is no stimulation. + data_t t_silence = 0.1; + data_t thresh_silence = 0.001; + CalcD(t_silence, thresh_silence); + } else { + fit_.set_d(d); + } +} + +template <typename Fit> +std::tuple<Fit, Vector> SSID<Fit>::Run(SSIDWt ssid_wt) { + // the weight on minimizing dc I/O gain only works for gaussian, + // and hopefully not necessary with appropriate dataset. + data_t wt_dc = 0; + // std::cout << "creating hankel mat\n"; + CreateHankelDataMat(); + // std::cout << "decomposing data\n"; + DecomposeData(); + // std::cout << "calculating svd\n"; + CalcSVD(ssid_wt); + // std::cout << "solving for params\n"; + Solve(wt_dc); + // std::cout << "fin\n"; + return std::make_tuple(fit_, s_); +} + +template <typename Fit> +void SSID<Fit>::CalcD(data_t t_silence, data_t thresh_silence) { + Vector d(z_.at(0).n_rows, fill::zeros); + Vector win(static_cast<size_t>(t_silence / dt_), fill::ones); + Vector sum_z_silence(n_y_, fill::zeros); + size_t n_silence(0); + for (size_t trial = 0; trial < u_.size(); trial++) { + // find silent samples + // start by convolving with + Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); + Vector u_conv = conv(sum_u, win, "same"); + + // get only the samples that are silent... + arma::uvec ubi_silence = find(u_conv <= thresh_silence); + if (ubi_silence.n_elem > 0) { + sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); + n_silence += ubi_silence.n_elem; + } + } + if (n_silence > 0) { + d = sum_z_silence / n_silence; + } + fit_.set_d(d); +} + +template <typename Fit> +void SSID<Fit>::CreateHankelDataMat() { + // temporary copy of data + Matrix z(n_y_, n_t_tot_, fill::zeros); + Matrix u(n_u_, n_t_tot_, fill::zeros); + size_t so_far(0); + + for (size_t trial = 0; trial < z_.size(); trial++) { + z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); + u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); + so_far += n_t_.at(trial); + } + + // remove output bias + z.each_col() -= fit_.d(); + + // calculate I/O gain @ DC while data in convenient form + g_dc_ = z * pinv(u); + // std::cout << "G0_data = " << g_dc_ << "\n"; + + // create hankel data matrix + size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat + + // block-hankel data matrix + D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); + // past input + auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); + // future input + auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); + // past output + auto y_p = + D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); + // future output + auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, + 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); + + size_t idx = 0; + for (size_t k = 0; k < len; k++) { + idx = 0; + for (size_t kk = k; kk < (n_h_ + k); kk++) { + u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); + idx += n_u_; + } + + idx = 0; + for (size_t kk = (n_h_ + k); kk < (2 * n_h_ + k); kk++) { + u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); + idx += n_u_; + } + + idx = 0; + for (size_t kk = k; kk < (n_h_ + k); kk++) { + y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); + idx += n_y_; + } + + idx = 0; + for (size_t kk = (n_h_ + k); kk < (2 * n_h_ + k); kk++) { + y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); + idx += n_y_; + } + } + + D_ /= sqrt(static_cast<data_t>(len)); +} + +// template <typename Fit> +// void SSID<Fit>::DecomposeData() { +// // do LQ decomp instead of calculating covariance expensive way +// // Note that "R" in van Overschee is lower-triangular (L), not "R" in QR +// // decomp. Very confusing. +// Matrix q_t; +// lq(L_, q_t, D_); +// // van Overschee zeros out the other elements. +// L_ = trimatl(L_); +// } + +template <typename Fit> +void SSID<Fit>::CalcSVD(SSIDWt wt) { + // submats that will be needed: + auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, + n_h_ * (2 * n_u_ + n_y_) - 1); + auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); + auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); + auto R_23_13 = + L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); + auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, + n_h_ * (2 * n_u_ + n_y_) - 1); + auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, + n_h_ * (2 * n_u_) - 1); + auto R_44 = + L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, + n_h_ * (2 * n_u_ + n_y_) - 1); + auto R_56_14 = + L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, + n_h_ * (2 * n_u_ + n_y_) - 1); + + Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); + auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); + auto Luf = + Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); + auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, + n_h_ * (2 * n_u_ + n_y_) - 1); + + // aka: R_f + Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, + 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); + // from van Overschee subid.m: + // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs + + Matrix U; + Matrix V; + switch (wt) { + case kSSIDNone: { + // No weighting. (what van Overschee calls "N4SID") + Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; + arma::svd(U, s_, V, O_k_sans_Qt, "std"); + } break; + case kSSIDMOESP: { + // MOESP weighting + // This is what they use in the "robust" algorithm van Overschee, de Moor + // 1996 + Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - + R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; + Matrix O_k_ortho_Uf_sans_Qt = + join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); + svd(U, s_, V, O_k_ortho_Uf_sans_Qt, "std"); + } break; + case kSSIDCVA: { + // CVA weighting + // See van Overschee's matlab code (subid.m): + // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems + Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - + R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; + Matrix O_k_ortho_Uf_sans_Qt = + join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); + + Matrix inv_w1; + Matrix qt1; + lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) + inv_w1 = trimatl(inv_w1); + inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); + Matrix w_o_w = arma::solve( + inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively + // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt + svd(U, s_, V, w_o_w, "std"); + + U = inv_w1 * U; + break; + } + } + + // Truncate to model order (heart of ssid method) + auto s_hat = s_.subvec(0, n_x_ - 1); + Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); + auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); + + // get extended observability and controllability mats + ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix +} + +template <typename Fit> +void SSID<Fit>::Solve(data_t wt_dc) { + // required submats + auto R_56_14 = + L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, + n_h_ * (2 * n_u_ + n_y_) - 1); + auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, + n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); + auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, + 2 * n_h_ * (n_u_ + n_y_) - 1, + n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); + auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, + n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, + n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); + auto R_56_15 = + L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, + n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); + + // Solve for params using appropriate algorithm: + // robust deterministic/stochastic algorithm in van Overschee 1996 + // algorithm that the authors say "works" in practice. + auto ext_obs_tm1 = ext_obs_t_.submat( + 0, 0, ext_obs_t_.n_rows - 1 - n_y_, + ext_obs_t_.n_cols - 1); // extended observability matrix + + // This is what textbook (1996) says: + // + // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); + // + // HOWEVER, do not know why but have to fill the last place with zeros like + // authors' matlab implementation (see `subid.m`) + // Otherwise, get ridiculous covariances (although A,C estimates are close to + // same...) + Matrix Tr = join_vert( + join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), + R_23_15); + Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); + Matrix S = Tl * pinv(Tr); + + // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure + // stability. + fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); + Matrix ext_obs_t_p1 = join_vert( + ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), + Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); + fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); + + // At this point, van Overschee & de Moor suggest re-calculating ext_obs_t_, + // ext_obs_tm1 from (A, C) because it was just an approximation. This is + RecomputeExtObs(); + ext_obs_tm1 = ext_obs_t_.submat( + 0, 0, ext_obs_t_.n_rows - 1 - n_y_, + ext_obs_t_.n_cols - 1); // extended observability matrix + Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); + Tr = join_vert( + join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), + R_23_15); + S = Tl * pinv(Tr); + + Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); + Matrix Mcurly = pinv(ext_obs_tm1); + Matrix Pcurly = Tl - Lcurly * R_56_15; + Vector Pvec = vectorise(Pcurly); + Matrix Qcurly = R_23_15; + + // Identify [D; B], assuming D=0 and ensuring DC gain is correct + Matrix sum_QcurlyT_kron_Ncurly( + (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), + fill::zeros); + + Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, + fill::eye); + eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, + eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; + + // van Overschee (1996) p. 126 + Matrix N1_Tl = -Lcurly; + N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += + join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); + N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += + Matrix(n_y_, n_y_, fill::eye); + + Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); + Matrix N_k; + for (size_t k = 0; k < n_h_; k++) { + auto Qcurly_k = + Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); + + Nk_Tl.zeros(); + Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = + N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); + N_k = Nk_Tl * eye_ext_obs_tm1; + + sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); + } + + Matrix err_vec; + if (wt_dc > 0) { + // Constraints enforced by weighted least squares + // + // Reference: + // + // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly + // Excited Industrial Systems. Conference in Decision and Control. + + // constraint 1: assume D=0 --> remove the components for Dvec (this is + // actually a hard constraint in that it ignores D) + Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; + sum_QcurlyT_kron_Ncurly = + Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); + + size_t kkk = 0; + for (size_t k = 1; k < (n_u_ + 1); k++) { + size_t start_idx = k * (n_y_ + n_x_) - n_x_; + for (size_t kk = 0; kk < n_x_; kk++) { + sum_QcurlyT_kron_Ncurly.col(kkk) = + sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); + kkk++; + } + } + + // constraint 2: Make sure DC I/O gain is correct + Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); + Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); + Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); + Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = + join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); + + // WEIGHTED LS + // Important in practice because I care a lot about at least getting the DC + // gain correct. Put x weight on minimizing error at DC, relative to others + Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, + sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); + // Make weight on minimizing DC error immense so at least that + // should be nailed. + size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; + size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; + size_t stop_row = w.n_rows - 1; + size_t stop_col = w.n_cols - 1; + // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale + // weight with data length? + w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; + Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * + sum_QcurlyT_kron_Ncurly_b_to_g0) * + sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; + + fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); + + // Calculate residuals and their cov. + // Because I've added constraints, I need to re-calculate the right term + // with b_vec instead of how van Overschee do in final algorithm. + err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; + } else { + // default way: *no* constraint on G0 or D=0 + Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; + // TODO(mfbolus) n.b., this gets thrown away... + // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); + fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); + err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; + } + // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); + + // TODO(mfbolus): Something is wrong with the error calculation above. + // Use the way van overschee does it in `subid.m` + // WARNING: this ignores any above constraints, so Q, R will be approximate... + Matrix err = Tl - S * Tr; + Matrix cov_err = err * err.t(); + fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); + fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); +} + +template <typename Fit> +void SSID<Fit>::RecomputeExtObs() { + ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); + for (size_t k = 2; k < (n_h_ + 1); k++) { + ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = + ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, + ext_obs_t_.n_cols - 1) * + fit_.A(); + } +} + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md index 51a85fdd..2f5b8380 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md @@ -1,71 +1,71 @@ ---- -title: ldsCtrlEst_h/lds_gaussian.h -summary: glds namespace - ---- - -# ldsCtrlEst_h/lds_gaussian.h - -`glds` namespace [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | - -## Detailed Description - - - -This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations (`[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)`). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_GAUSSIAN_H -#define LDSCTRLEST_LDS_GAUSSIAN_H - -// namespace -#include "lds.h" - -namespace lds { -namespace gaussian { -// insert any Gaussian-specific things here... -} // namespace gaussian -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_gaussian.h +summary: glds namespace + +--- + +# ldsCtrlEst_h/lds_gaussian.h + +`glds` namespace [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | + +## Detailed Description + + + +This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations (`[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)`). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_GAUSSIAN_H +#define LDSCTRLEST_LDS_GAUSSIAN_H + +// namespace +#include "lds.h" + +namespace lds { +namespace gaussian { +// insert any Gaussian-specific things here... +} // namespace gaussian +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md index fafc590a..201a48df 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md @@ -1,116 +1,116 @@ ---- -title: ldsCtrlEst_h/lds_gaussian_ctrl.h -summary: GLDS Controller. - ---- - -# ldsCtrlEst_h/lds_gaussian_ctrl.h - -GLDS Controller. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/)** <br>Gaussian-observation [Controller]() Type. | - -## Detailed Description - - - -This file declares and partially defines the type for control of a gaussian-observation linear dynamical system ([lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/)). It inherits functionality from the underlying GLDS model type ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)), including state estimation. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H -#define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H - -// namespace -#include "lds_gaussian.h" -// system -#include "lds_gaussian_sys.h" -// controller -#include "lds_ctrl.h" - -namespace lds { -namespace gaussian { -class Controller : public lds::Controller<System> { - public: - void set_y_ref(const Vector& y_ref) override { - Reassign(y_ref_,y_ref); - cx_ref_ = y_ref - sys_.d(); - }; - - // make sure base class template methods available - using lds::Controller<System>::Controller; - using lds::Controller<System>::Control; - using lds::Controller<System>::ControlOutputReference; - - using lds::Controller<System>::sys; - using lds::Controller<System>::Kc; - using lds::Controller<System>::Kc_inty; - using lds::Controller<System>::Kc_u; - using lds::Controller<System>::g_design; - using lds::Controller<System>::u_ref; - using lds::Controller<System>::x_ref; - using lds::Controller<System>::y_ref; - using lds::Controller<System>::control_type; - - using lds::Controller<System>::set_sys; - using lds::Controller<System>::set_g_design; - using lds::Controller<System>::set_u_ref; - using lds::Controller<System>::set_x_ref; - using lds::Controller<System>::set_y_ref; - using lds::Controller<System>::set_Kc; - using lds::Controller<System>::set_Kc_inty; - using lds::Controller<System>::set_Kc_u; - using lds::Controller<System>::set_tau_awu; - using lds::Controller<System>::set_control_type; - - using lds::Controller<System>::Reset; - using lds::Controller<System>::Print; -}; -} // namespace gaussian -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_gaussian_ctrl.h +summary: GLDS Controller. + +--- + +# ldsCtrlEst_h/lds_gaussian_ctrl.h + +GLDS Controller. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/)** <br>Gaussian-observation [Controller]() Type. | + +## Detailed Description + + + +This file declares and partially defines the type for control of a gaussian-observation linear dynamical system ([lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/)). It inherits functionality from the underlying GLDS model type ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)), including state estimation. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H +#define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H + +// namespace +#include "lds_gaussian.h" +// system +#include "lds_gaussian_sys.h" +// controller +#include "lds_ctrl.h" + +namespace lds { +namespace gaussian { +class Controller : public lds::Controller<System> { + public: + void set_y_ref(const Vector& y_ref) override { + Reassign(y_ref_,y_ref); + cx_ref_ = y_ref - sys_.d(); + }; + + // make sure base class template methods available + using lds::Controller<System>::Controller; + using lds::Controller<System>::Control; + using lds::Controller<System>::ControlOutputReference; + + using lds::Controller<System>::sys; + using lds::Controller<System>::Kc; + using lds::Controller<System>::Kc_inty; + using lds::Controller<System>::Kc_u; + using lds::Controller<System>::g_design; + using lds::Controller<System>::u_ref; + using lds::Controller<System>::x_ref; + using lds::Controller<System>::y_ref; + using lds::Controller<System>::control_type; + + using lds::Controller<System>::set_sys; + using lds::Controller<System>::set_g_design; + using lds::Controller<System>::set_u_ref; + using lds::Controller<System>::set_x_ref; + using lds::Controller<System>::set_y_ref; + using lds::Controller<System>::set_Kc; + using lds::Controller<System>::set_Kc_inty; + using lds::Controller<System>::set_Kc_u; + using lds::Controller<System>::set_tau_awu; + using lds::Controller<System>::set_control_type; + + using lds::Controller<System>::Reset; + using lds::Controller<System>::Print; +}; +} // namespace gaussian +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md index cff73443..572148f5 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md @@ -1,94 +1,94 @@ ---- -title: ldsCtrlEst_h/lds_gaussian_fit.h -summary: GLDS fit type. - ---- - -# ldsCtrlEst_h/lds_gaussian_fit.h - -GLDS fit type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/)** <br>GLDS [Fit]() Type. | - -## Detailed Description - - - -This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H -#define LDSCTRLEST_LDS_GAUSSIAN_FIT_H - -// namespace -#include "lds_gaussian.h" -// fit type -#include "lds_fit.h" - -namespace lds { -namespace gaussian { -class Fit : public lds::Fit { - public: - Fit() = default; - - Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); - const Matrix& R() const override { return R_; }; - void set_R(const Matrix& R) override { - Reassign(R_, R); - ForceSymPD(R_); - }; - - View h(Matrix& y, const Matrix& x, size_t t) override { - y.col(t) = C_ * x.col(t) + d_; - return y.col(t); - }; -}; - -}; // namespace gaussian -} // namespace lds -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_gaussian_fit.h +summary: GLDS fit type. + +--- + +# ldsCtrlEst_h/lds_gaussian_fit.h + +GLDS fit type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/)** <br>GLDS [Fit]() Type. | + +## Detailed Description + + + +This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H +#define LDSCTRLEST_LDS_GAUSSIAN_FIT_H + +// namespace +#include "lds_gaussian.h" +// fit type +#include "lds_fit.h" + +namespace lds { +namespace gaussian { +class Fit : public lds::Fit { + public: + Fit() = default; + + Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); + const Matrix& R() const override { return R_; }; + void set_R(const Matrix& R) override { + Reassign(R_, R); + ForceSymPD(R_); + }; + + View h(Matrix& y, const Matrix& x, size_t t) override { + y.col(t) = C_ * x.col(t) + d_; + return y.col(t); + }; +}; + +}; // namespace gaussian +} // namespace lds +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md index 68ad2306..3537a36e 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md @@ -1,92 +1,92 @@ ---- -title: ldsCtrlEst_h/lds_gaussian_fit_em.h -summary: GLDS E-M fit type. - ---- - -# ldsCtrlEst_h/lds_gaussian_fit_em.h - -GLDS E-M fit type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/)** <br>GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/) Type. | - -## Detailed Description - - - -This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (`lds::gaussian::emFit_t`). - -References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2). - -[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H -#define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H - -#include "lds_fit_em.h" -#include "lds_gaussian_fit.h" - -namespace lds { -namespace gaussian { -class FitEM : public EM<Fit> { - public: - using EM<Fit>::EM; - - private: - void MaximizeOutput() override; - - void MaximizeMeasurement() override; - - void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) override; -}; - -} // namespace gaussian -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_gaussian_fit_em.h +summary: GLDS E-M fit type. + +--- + +# ldsCtrlEst_h/lds_gaussian_fit_em.h + +GLDS E-M fit type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/)** <br>GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/) Type. | + +## Detailed Description + + + +This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (`lds::gaussian::emFit_t`). + +References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2). + +[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H +#define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H + +#include "lds_fit_em.h" +#include "lds_gaussian_fit.h" + +namespace lds { +namespace gaussian { +class FitEM : public EM<Fit> { + public: + using EM<Fit>::EM; + + private: + void MaximizeOutput() override; + + void MaximizeMeasurement() override; + + void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) override; +}; + +} // namespace gaussian +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md index 9379f5f2..7dc32c1a 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md @@ -1,91 +1,91 @@ ---- -title: ldsCtrlEst_h/lds_gaussian_fit_ssid.h -summary: GLDS SSID fit type. - ---- - -# ldsCtrlEst_h/lds_gaussian_fit_ssid.h - -GLDS SSID fit type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for GLDS. | - -## Detailed Description - - - -This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (`lds::gaussian::ssidFit_t`). - -References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H -#define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H - -#include "lds_fit_ssid.h" -#include "lds_gaussian_fit.h" - -namespace lds { -namespace gaussian { -class FitSSID : public SSID<Fit> { - public: - using SSID<Fit>::SSID; - using SSID<Fit>::Run; - - private: - using SSID<Fit>::CreateHankelDataMat; - using SSID<Fit>::CalcSVD; - using SSID<Fit>::Solve; - - void DecomposeData() override; - - void SolveVanOverschee(); -}; -} // namespace gaussian -} // namespace lds -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_gaussian_fit_ssid.h +summary: GLDS SSID fit type. + +--- + +# ldsCtrlEst_h/lds_gaussian_fit_ssid.h + +GLDS SSID fit type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for GLDS. | + +## Detailed Description + + + +This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (`lds::gaussian::ssidFit_t`). + +References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H +#define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H + +#include "lds_fit_ssid.h" +#include "lds_gaussian_fit.h" + +namespace lds { +namespace gaussian { +class FitSSID : public SSID<Fit> { + public: + using SSID<Fit>::SSID; + using SSID<Fit>::Run; + + private: + using SSID<Fit>::CreateHankelDataMat; + using SSID<Fit>::CalcSVD; + using SSID<Fit>::Solve; + + void DecomposeData() override; + + void SolveVanOverschee(); +}; +} // namespace gaussian +} // namespace lds +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md index 338d66db..fcc3a305 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md @@ -1,114 +1,114 @@ ---- -title: ldsCtrlEst_h/lds_gaussian_sctrl.h -summary: GLDS switched controller type. - ---- - -# ldsCtrlEst_h/lds_gaussian_sctrl.h - -GLDS switched controller type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/)** <br>Gaussian-observation [SwitchedController]() Type. | - -## Detailed Description - - - -This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems ([lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/)). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H -#define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H - -// controller type -#include "lds_gaussian_ctrl.h" -// switched controller -#include "lds_sctrl.h" - -namespace lds { -namespace gaussian { -class SwitchedController : public lds::SwitchedController<System> { - public: - void set_y_ref(const Vector& y_ref) override { - Reassign(y_ref_, y_ref); - cx_ref_ = y_ref - sys_.d(); - } - - // make sure base class template methods available - using lds::SwitchedController<System>::SwitchedController; - using lds::SwitchedController<System>::Switch; - using lds::SwitchedController<System>::Control; - using lds::SwitchedController<System>::ControlOutputReference; - - using lds::SwitchedController<System>::sys; - using lds::SwitchedController<System>::Kc; - using lds::SwitchedController<System>::Kc_inty; - using lds::SwitchedController<System>::Kc_u; - using lds::SwitchedController<System>::g_design; - using lds::SwitchedController<System>::u_ref; - using lds::SwitchedController<System>::x_ref; - using lds::SwitchedController<System>::y_ref; - using lds::SwitchedController<System>::control_type; - - using lds::SwitchedController<System>::set_g_design; - using lds::SwitchedController<System>::set_u_ref; - using lds::SwitchedController<System>::set_x_ref; - using lds::SwitchedController<System>::set_y_ref; - using lds::SwitchedController<System>::set_Kc; - using lds::SwitchedController<System>::set_Kc_inty; - using lds::SwitchedController<System>::set_Kc_u; - using lds::SwitchedController<System>::set_tau_awu; - using lds::SwitchedController<System>::set_control_type; - - using lds::SwitchedController<System>::Reset; - using lds::SwitchedController<System>::Print; -}; // SwitchedController -} // namespace gaussian -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_gaussian_sctrl.h +summary: GLDS switched controller type. + +--- + +# ldsCtrlEst_h/lds_gaussian_sctrl.h + +GLDS switched controller type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/)** <br>Gaussian-observation [SwitchedController]() Type. | + +## Detailed Description + + + +This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems ([lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/)). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H +#define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H + +// controller type +#include "lds_gaussian_ctrl.h" +// switched controller +#include "lds_sctrl.h" + +namespace lds { +namespace gaussian { +class SwitchedController : public lds::SwitchedController<System> { + public: + void set_y_ref(const Vector& y_ref) override { + Reassign(y_ref_, y_ref); + cx_ref_ = y_ref - sys_.d(); + } + + // make sure base class template methods available + using lds::SwitchedController<System>::SwitchedController; + using lds::SwitchedController<System>::Switch; + using lds::SwitchedController<System>::Control; + using lds::SwitchedController<System>::ControlOutputReference; + + using lds::SwitchedController<System>::sys; + using lds::SwitchedController<System>::Kc; + using lds::SwitchedController<System>::Kc_inty; + using lds::SwitchedController<System>::Kc_u; + using lds::SwitchedController<System>::g_design; + using lds::SwitchedController<System>::u_ref; + using lds::SwitchedController<System>::x_ref; + using lds::SwitchedController<System>::y_ref; + using lds::SwitchedController<System>::control_type; + + using lds::SwitchedController<System>::set_g_design; + using lds::SwitchedController<System>::set_u_ref; + using lds::SwitchedController<System>::set_x_ref; + using lds::SwitchedController<System>::set_y_ref; + using lds::SwitchedController<System>::set_Kc; + using lds::SwitchedController<System>::set_Kc_inty; + using lds::SwitchedController<System>::set_Kc_u; + using lds::SwitchedController<System>::set_tau_awu; + using lds::SwitchedController<System>::set_control_type; + + using lds::SwitchedController<System>::Reset; + using lds::SwitchedController<System>::Print; +}; // SwitchedController +} // namespace gaussian +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md index 0cbdc0f3..8843a354 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md @@ -1,98 +1,98 @@ ---- -title: src/lds_gaussian_sys.cpp -summary: GLDS base type. - ---- - -# src/lds_gaussian_sys.cpp - -GLDS base type. [More...](#detailed-description) - - - -## Detailed Description - - - -This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (`lds::gaussian::sys_t`). It inherits functionality from the underlying linear dynamical system (`lds::sys_t`). - - - - - -## Source code - -```cpp -//===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst_h/lds_gaussian_sys.h> - -lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, - data_t q0, data_t r0) - : lds::System(n_u, n_x, n_y, dt, p0, q0) { - - R_.zeros(n_y, n_y); - R_.diag().fill(r0); - - do_recurse_Ke_=true; -}; - -// recursively estimate Ke -void lds::gaussian::System::RecurseKe() { - if (!do_recurse_Ke_) { - return; - } - - // predict covariance - P_ = A_ * P_ * A_.t() + Q_; - - // calc Kalman gain - Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); - - // update covariance - // Reference: Ghahramani et Hinton (1996) - P_ = P_ - Ke_ * C_ * P_; - - if (do_adapt_m) { - P_m_ += Q_m_; // A_m = I (i.e., random walk) - Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); - P_m_ = P_m_ - Ke_m_ * C_ * P_m_; - } -} - -// Simulate -const lds::Vector& lds::gaussian::System::Simulate(const Vector& u_tm1){ - f(u_tm1, true);//simulate dynamics with noise added - h();//output - z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure - return z_; -} - -void lds::gaussian::System::Print() { - lds::System::Print(); - std::cout << "R: \n" << R_ << "\n"; -} -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: src/lds_gaussian_sys.cpp +summary: GLDS base type. + +--- + +# src/lds_gaussian_sys.cpp + +GLDS base type. [More...](#detailed-description) + + + +## Detailed Description + + + +This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (`lds::gaussian::sys_t`). It inherits functionality from the underlying linear dynamical system (`lds::sys_t`). + + + + + +## Source code + +```cpp +//===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst_h/lds_gaussian_sys.h> + +lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, + data_t q0, data_t r0) + : lds::System(n_u, n_x, n_y, dt, p0, q0) { + + R_.zeros(n_y, n_y); + R_.diag().fill(r0); + + do_recurse_Ke_=true; +}; + +// recursively estimate Ke +void lds::gaussian::System::RecurseKe() { + if (!do_recurse_Ke_) { + return; + } + + // predict covariance + P_ = A_ * P_ * A_.t() + Q_; + + // calc Kalman gain + Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); + + // update covariance + // Reference: Ghahramani et Hinton (1996) + P_ = P_ - Ke_ * C_ * P_; + + if (do_adapt_m) { + P_m_ += Q_m_; // A_m = I (i.e., random walk) + Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); + P_m_ = P_m_ - Ke_m_ * C_ * P_m_; + } +} + +// Simulate +const lds::Vector& lds::gaussian::System::Simulate(const Vector& u_tm1){ + f(u_tm1, true);//simulate dynamics with noise added + h();//output + z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure + return z_; +} + +void lds::gaussian::System::Print() { + lds::System::Print(); + std::cout << "R: \n" << R_ << "\n"; +} +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md index 7649b701..bea98c6c 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md @@ -1,126 +1,128 @@ ---- -title: ldsCtrlEst_h/lds_gaussian_sys.h -summary: GLDS base type. - ---- - -# ldsCtrlEst_h/lds_gaussian_sys.h - -GLDS base type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)** <br>Gaussian LDS Type. | - -## Detailed Description - - - -This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (`[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)`). It inherits functionality from the underlying linear dynamical system (`[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)`). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H -#define LDSCTRLEST_LDS_GAUSSIAN_SYS_H - -// namespace -#include "lds_gaussian.h" -// system -#include "lds_sys.h" - -namespace lds { -namespace gaussian { -class System : public lds::System { - public: - System() = default; - - System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, - data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, - data_t r0 = kDefaultR0); - - const Vector& Simulate(const Vector& u_tm1) override; - - // get methods - const Matrix& R() const { return R_; }; - - // set methods - void set_Q(const Matrix& Q) { - lds::System::set_Q(Q); - do_recurse_Ke_ = true; - } - void set_R(const Matrix& R) { - Reassign(R_,R); - do_recurse_Ke_ = true; - }; - - void set_Ke(const Matrix& Ke) { - Reassign(Ke_,Ke); - // if users have set Ke, they must not want to calculate it online. - do_recurse_Ke_ = false; - }; - void set_Ke_m(const Matrix& Ke_m) { - Reassign(Ke_m_,Ke_m); - // if users have set Ke, they must not want to calculate it online. - do_recurse_Ke_ = false; - }; - - void Print(); - - protected: - void h() override { - cx_ = C_ * x_; - y_ = cx_ + d_; - }; - - void RecurseKe() override; - - // Gaussian-output-specific - Matrix R_; - bool do_recurse_Ke_{}; -}; // System -} // namespace gaussian -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_gaussian_sys.h +summary: GLDS base type. + +--- + +# ldsCtrlEst_h/lds_gaussian_sys.h + +GLDS base type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)** <br>Gaussian LDS Type. | + +## Detailed Description + + + +This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (`[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)`). It inherits functionality from the underlying linear dynamical system (`[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)`). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H +#define LDSCTRLEST_LDS_GAUSSIAN_SYS_H + +// namespace +#include "lds_gaussian.h" +// system +#include "lds_sys.h" + +namespace lds { +namespace gaussian { +class System : public lds::System { + public: + System() = default; + + System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, + data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, + data_t r0 = kDefaultR0); + + const Vector& Simulate(const Vector& u_tm1) override; + + // get methods + const Matrix& R() const { return R_; }; + + // set methods + void set_Q(const Matrix& Q) { + lds::System::set_Q(Q); + do_recurse_Ke_ = true; + } + void set_R(const Matrix& R) { + Reassign(R_, R); + do_recurse_Ke_ = true; + }; + + void set_Ke(const Matrix& Ke) { + Reassign(Ke_, Ke); + // if users have set Ke, they must not want to calculate it online. + do_recurse_Ke_ = false; + }; + void set_Ke_m(const Matrix& Ke_m) { + Reassign(Ke_m_, Ke_m); + // if users have set Ke, they must not want to calculate it online. + do_recurse_Ke_ = false; + }; + + void Print(); + + protected: + void h() override { + cx_ = C_ * x_; + y_ = cx_ + d_; + }; + + Vector h_(Vector x) override { return C_ * x + d_; }; + + void RecurseKe() override; + + // Gaussian-output-specific + Matrix R_; + bool do_recurse_Ke_{}; +}; // System +} // namespace gaussian +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md index bf41756f..56413ec6 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md @@ -1,75 +1,75 @@ ---- -title: ldsCtrlEst_h/lds_poisson.h -summary: plds namespace - ---- - -# ldsCtrlEst_h/lds_poisson.h - -`plds` namespace [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Detailed Description - - - -This file declares and partially defines the namespace for linear dynamical systems with Poisson observations (`[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)`). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_POISSON_H -#define LDSCTRLEST_LDS_POISSON_H - -#include "lds.h" - -namespace lds { -namespace poisson { -// TODO(mfbolus): Not sure if defining these as static here makes the most -// sense. Is there a downside to letting multiple poisson System objects share a -// common random number generator? -static std::random_device rd; -static std::mt19937 rng = std::mt19937( - rd()); -} // namespace poisson -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_poisson.h +summary: plds namespace + +--- + +# ldsCtrlEst_h/lds_poisson.h + +`plds` namespace [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Detailed Description + + + +This file declares and partially defines the namespace for linear dynamical systems with Poisson observations (`[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)`). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_POISSON_H +#define LDSCTRLEST_LDS_POISSON_H + +#include "lds.h" + +namespace lds { +namespace poisson { +// TODO(mfbolus): Not sure if defining these as static here makes the most +// sense. Is there a downside to letting multiple poisson System objects share a +// common random number generator? +static std::random_device rd; +static std::mt19937 rng = std::mt19937( + rd()); +} // namespace poisson +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md index fa6691c7..47682eb1 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md @@ -1,121 +1,121 @@ ---- -title: ldsCtrlEst_h/lds_poisson_ctrl.h -summary: PLDS controller type. - ---- - -# ldsCtrlEst_h/lds_poisson_ctrl.h - -PLDS controller type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)** <br>PLDS [Controller]() Type. | - -## Detailed Description - - - -This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system (`[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)`). It inherits functionality from the underlying PLDS model type (`[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)`), including state estimation. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_POISSON_CTRL_H -#define LDSCTRLEST_LDS_POISSON_CTRL_H - -// namespace -#include "lds_poisson.h" -// system type -#include "lds_poisson_sys.h" -// control type -#include "lds_ctrl.h" - -namespace lds { -namespace poisson { -class Controller : public lds::Controller<System> { - public: - void set_y_ref(const Vector& y_ref) override { - Reassign(y_ref_, y_ref); - lds::Limit(y_ref_, kYRefLb, lds::kInf); - cx_ref_ = log(y_ref_) - sys_.d(); - }; - - // make sure base class template methods available - using lds::Controller<System>::Controller; - using lds::Controller<System>::Control; - using lds::Controller<System>::ControlOutputReference; - - using lds::Controller<System>::sys; - using lds::Controller<System>::Kc; - using lds::Controller<System>::Kc_inty; - using lds::Controller<System>::Kc_u; - using lds::Controller<System>::g_design; - using lds::Controller<System>::u_ref; - using lds::Controller<System>::x_ref; - using lds::Controller<System>::y_ref; - using lds::Controller<System>::control_type; - - using lds::Controller<System>::set_sys; - using lds::Controller<System>::set_g_design; - using lds::Controller<System>::set_u_ref; - using lds::Controller<System>::set_x_ref; - using lds::Controller<System>::set_y_ref; - using lds::Controller<System>::set_Kc; - using lds::Controller<System>::set_Kc_inty; - using lds::Controller<System>::set_Kc_u; - using lds::Controller<System>::set_tau_awu; - using lds::Controller<System>::set_control_type; - - using lds::Controller<System>::Reset; - using lds::Controller<System>::Print; - - private: - constexpr static const data_t kYRefLb = - 1e-4; -}; -} // namespace poisson -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_poisson_ctrl.h +summary: PLDS controller type. + +--- + +# ldsCtrlEst_h/lds_poisson_ctrl.h + +PLDS controller type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)** <br>PLDS [Controller]() Type. | + +## Detailed Description + + + +This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system (`[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)`). It inherits functionality from the underlying PLDS model type (`[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)`), including state estimation. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_POISSON_CTRL_H +#define LDSCTRLEST_LDS_POISSON_CTRL_H + +// namespace +#include "lds_poisson.h" +// system type +#include "lds_poisson_sys.h" +// control type +#include "lds_ctrl.h" + +namespace lds { +namespace poisson { +class Controller : public lds::Controller<System> { + public: + void set_y_ref(const Vector& y_ref) override { + Reassign(y_ref_, y_ref); + lds::Limit(y_ref_, kYRefLb, lds::kInf); + cx_ref_ = log(y_ref_) - sys_.d(); + }; + + // make sure base class template methods available + using lds::Controller<System>::Controller; + using lds::Controller<System>::Control; + using lds::Controller<System>::ControlOutputReference; + + using lds::Controller<System>::sys; + using lds::Controller<System>::Kc; + using lds::Controller<System>::Kc_inty; + using lds::Controller<System>::Kc_u; + using lds::Controller<System>::g_design; + using lds::Controller<System>::u_ref; + using lds::Controller<System>::x_ref; + using lds::Controller<System>::y_ref; + using lds::Controller<System>::control_type; + + using lds::Controller<System>::set_sys; + using lds::Controller<System>::set_g_design; + using lds::Controller<System>::set_u_ref; + using lds::Controller<System>::set_x_ref; + using lds::Controller<System>::set_y_ref; + using lds::Controller<System>::set_Kc; + using lds::Controller<System>::set_Kc_inty; + using lds::Controller<System>::set_Kc_u; + using lds::Controller<System>::set_tau_awu; + using lds::Controller<System>::set_control_type; + + using lds::Controller<System>::Reset; + using lds::Controller<System>::Print; + + private: + constexpr static const data_t kYRefLb = + 1e-4; +}; +} // namespace poisson +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md index 144d1b6b..e0da461b 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md @@ -1,102 +1,102 @@ ---- -title: ldsCtrlEst_h/lds_poisson_fit.h -summary: PLDS base fit type. - ---- - -# ldsCtrlEst_h/lds_poisson_fit.h - -PLDS base fit type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/)** <br>PLDS [Fit]() Type. | - -## Detailed Description - - - -This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_POISSON_FIT_H -#define LDSCTRLEST_LDS_POISSON_FIT_H - -// namespace -#include "lds_poisson.h" -// fit -#include "lds_fit.h" - -namespace lds { -namespace poisson { -class Fit : public lds::Fit { - public: - Fit() = default; - - Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) - : lds::Fit(n_u, n_x, n_y, dt){}; - - View h(Matrix& y, const Matrix& x, size_t t) override { - y.col(t) = exp(C_ * x.col(t) + d_); - return y.col(t); - }; - - void set_R(const Matrix& R) override { - std::cerr - << "WARNING: Cannot set R (R[0] = " << R.at(0) - << "). No Gaussian measurement noise in Poisson observation model.\n"; - }; - - const Matrix& R() const override { - return R_; - }; - -}; - -}; // namespace poisson -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_poisson_fit.h +summary: PLDS base fit type. + +--- + +# ldsCtrlEst_h/lds_poisson_fit.h + +PLDS base fit type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/)** <br>PLDS [Fit]() Type. | + +## Detailed Description + + + +This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_POISSON_FIT_H +#define LDSCTRLEST_LDS_POISSON_FIT_H + +// namespace +#include "lds_poisson.h" +// fit +#include "lds_fit.h" + +namespace lds { +namespace poisson { +class Fit : public lds::Fit { + public: + Fit() = default; + + Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) + : lds::Fit(n_u, n_x, n_y, dt){}; + + View h(Matrix& y, const Matrix& x, size_t t) override { + y.col(t) = exp(C_ * x.col(t) + d_); + return y.col(t); + }; + + void set_R(const Matrix& R) override { + std::cerr + << "WARNING: Cannot set R (R[0] = " << R.at(0) + << "). No Gaussian measurement noise in Poisson observation model.\n"; + }; + + const Matrix& R() const override { + return R_; + }; + +}; + +}; // namespace poisson +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md index 86a2ab2c..98906e9e 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md @@ -1,98 +1,98 @@ ---- -title: ldsCtrlEst_h/lds_poisson_fit_em.h -summary: PLDS E-M fit type. - ---- - -# ldsCtrlEst_h/lds_poisson_fit_em.h - -PLDS E-M fit type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/)** <br>PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/) Type. | - -## Detailed Description - - - -This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (`lds::gaussian::emFit_t`). - -References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2). - -[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2. - -[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H -#define LDSCTRLEST_LDS_POISSON_FIT_EM_H - -#include "lds_fit_em.h" -#include "lds_poisson_fit.h" - -namespace lds { -namespace poisson { -class FitEM : public EM<Fit> { - public: - using EM<Fit>::EM; - - private: - void MaximizeOutput() override; - - void MaximizeMeasurement() override{}; - - void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) override; - - data_t NewtonSolveC(); - - void AnalyticalSolveD(); -}; - -} // namespace poisson -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_poisson_fit_em.h +summary: PLDS E-M fit type. + +--- + +# ldsCtrlEst_h/lds_poisson_fit_em.h + +PLDS E-M fit type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/)** <br>PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/) Type. | + +## Detailed Description + + + +This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (`lds::gaussian::emFit_t`). + +References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2). + +[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2. + +[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H +#define LDSCTRLEST_LDS_POISSON_FIT_EM_H + +#include "lds_fit_em.h" +#include "lds_poisson_fit.h" + +namespace lds { +namespace poisson { +class FitEM : public EM<Fit> { + public: + using EM<Fit>::EM; + + private: + void MaximizeOutput() override; + + void MaximizeMeasurement() override{}; + + void RecurseKe(Matrix& Ke, Cube& P_pre, Cube& P_post, size_t t) override; + + data_t NewtonSolveC(); + + void AnalyticalSolveD(); +}; + +} // namespace poisson +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md index dd806216..922747b9 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md @@ -1,90 +1,90 @@ ---- -title: ldsCtrlEst_h/lds_poisson_fit_ssid.h -summary: PLDS SSID fit type. - ---- - -# ldsCtrlEst_h/lds_poisson_fit_ssid.h - -PLDS SSID fit type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for PLDS. | - -## Detailed Description - - - -This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm (`[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)`). - -References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H -#define LDSCTRLEST_LDS_POISSON_FIT_SSID_H - -#include "lds_fit_ssid.h" -#include "lds_poisson_fit.h" - -namespace lds { -namespace poisson { -class FitSSID : public SSID<Fit> { - public: - using SSID<Fit>::SSID; - - private: - void DecomposeData() override; - - void CalcCov(); - - void PoissonToGaussianMoments(); - Matrix cov_; -}; - -} // namespace poisson -} // namespace lds -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_poisson_fit_ssid.h +summary: PLDS SSID fit type. + +--- + +# ldsCtrlEst_h/lds_poisson_fit_ssid.h + +PLDS SSID fit type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for PLDS. | + +## Detailed Description + + + +This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm (`[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)`). + +References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H +#define LDSCTRLEST_LDS_POISSON_FIT_SSID_H + +#include "lds_fit_ssid.h" +#include "lds_poisson_fit.h" + +namespace lds { +namespace poisson { +class FitSSID : public SSID<Fit> { + public: + using SSID<Fit>::SSID; + + private: + void DecomposeData() override; + + void CalcCov(); + + void PoissonToGaussianMoments(); + Matrix cov_; +}; + +} // namespace poisson +} // namespace lds +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md index 885a2585..f9b45231 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md @@ -1,117 +1,117 @@ ---- -title: ldsCtrlEst_h/lds_poisson_sctrl.h -summary: PLDS switched controller type. - ---- - -# ldsCtrlEst_h/lds_poisson_sctrl.h - -PLDS switched controller type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/)** <br>Poisson-observation [SwitchedController]() Type. | - -## Detailed Description - - - -This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems ([lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/)). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H -#define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H - -#include "lds_poisson_ctrl.h" -#include "lds_sctrl.h" - -namespace lds { -namespace poisson { -class SwitchedController : public lds::SwitchedController<System> { - public: - void set_y_ref(const Vector& y_ref) override { - Reassign(y_ref_,y_ref); - lds::Limit(y_ref_, kYRefLB, lds::kInf); - cx_ref_ = log(y_ref_) - sys_.d(); - }; - - // make sure base class template methods available - using lds::SwitchedController<System>::SwitchedController; - using lds::SwitchedController<System>::Switch; - using lds::SwitchedController<System>::Control; - using lds::SwitchedController<System>::ControlOutputReference; - - using lds::SwitchedController<System>::sys; - using lds::SwitchedController<System>::Kc; - using lds::SwitchedController<System>::Kc_inty; - using lds::SwitchedController<System>::Kc_u; - using lds::SwitchedController<System>::g_design; - using lds::SwitchedController<System>::u_ref; - using lds::SwitchedController<System>::x_ref; - using lds::SwitchedController<System>::y_ref; - using lds::SwitchedController<System>::control_type; - - using lds::SwitchedController<System>::set_g_design; - using lds::SwitchedController<System>::set_u_ref; - using lds::SwitchedController<System>::set_x_ref; - using lds::SwitchedController<System>::set_y_ref; - using lds::SwitchedController<System>::set_Kc; - using lds::SwitchedController<System>::set_Kc_inty; - using lds::SwitchedController<System>::set_Kc_u; - using lds::SwitchedController<System>::set_tau_awu; - using lds::SwitchedController<System>::set_control_type; - - using lds::SwitchedController<System>::Reset; - using lds::SwitchedController<System>::Print; - - private: - constexpr static data_t kYRefLB = - 1e-4; -}; -} // namespace poisson -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_poisson_sctrl.h +summary: PLDS switched controller type. + +--- + +# ldsCtrlEst_h/lds_poisson_sctrl.h + +PLDS switched controller type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/)** <br>Poisson-observation [SwitchedController]() Type. | + +## Detailed Description + + + +This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems ([lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/)). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H +#define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H + +#include "lds_poisson_ctrl.h" +#include "lds_sctrl.h" + +namespace lds { +namespace poisson { +class SwitchedController : public lds::SwitchedController<System> { + public: + void set_y_ref(const Vector& y_ref) override { + Reassign(y_ref_,y_ref); + lds::Limit(y_ref_, kYRefLB, lds::kInf); + cx_ref_ = log(y_ref_) - sys_.d(); + }; + + // make sure base class template methods available + using lds::SwitchedController<System>::SwitchedController; + using lds::SwitchedController<System>::Switch; + using lds::SwitchedController<System>::Control; + using lds::SwitchedController<System>::ControlOutputReference; + + using lds::SwitchedController<System>::sys; + using lds::SwitchedController<System>::Kc; + using lds::SwitchedController<System>::Kc_inty; + using lds::SwitchedController<System>::Kc_u; + using lds::SwitchedController<System>::g_design; + using lds::SwitchedController<System>::u_ref; + using lds::SwitchedController<System>::x_ref; + using lds::SwitchedController<System>::y_ref; + using lds::SwitchedController<System>::control_type; + + using lds::SwitchedController<System>::set_g_design; + using lds::SwitchedController<System>::set_u_ref; + using lds::SwitchedController<System>::set_x_ref; + using lds::SwitchedController<System>::set_y_ref; + using lds::SwitchedController<System>::set_Kc; + using lds::SwitchedController<System>::set_Kc_inty; + using lds::SwitchedController<System>::set_Kc_u; + using lds::SwitchedController<System>::set_tau_awu; + using lds::SwitchedController<System>::set_control_type; + + using lds::SwitchedController<System>::Reset; + using lds::SwitchedController<System>::Print; + + private: + constexpr static data_t kYRefLB = + 1e-4; +}; +} // namespace poisson +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md index 5981e03e..4b1e16f4 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md @@ -1,94 +1,94 @@ ---- -title: src/lds_poisson_sys.cpp -summary: PLDS base type. - ---- - -# src/lds_poisson_sys.cpp - -PLDS base type. [More...](#detailed-description) - - - -## Detailed Description - - - -This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (`lds::poisson::sys_t`). It inherits functionality from the underlying linear dynamical system (`lds::sys_t`). - - - - - -## Source code - -```cpp -//===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst_h/lds_poisson_sys.h> - -lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, - data_t p0, data_t q0) - : lds::System(n_u, n_x, n_y, dt, p0, q0) { - diag_y_ = diagmat(y_); - pd_ = std::poisson_distribution<size_t>(0); -}; - -// Correct: Given measurement (z) and current input (u), update estimate of the -// state, covar, output. -// -// see Eden et al. 2004 -void lds::poisson::System::RecurseKe() { - // predict covariance - P_ = A_ * P_ * A_.t() + Q_; - - // update cov - P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); - Ke_ = P_ * C_.t(); - if (do_adapt_m) { - P_m_ += Q_m_; // predict (A_m = I) - P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update - Ke_m_ = P_m_ * C_.t(); - } -} - -// Simulate Measurement: z ~ Poisson(y) -const lds::Vector& lds::poisson::System::Simulate(const Vector& u_tm1) { - f(u_tm1, true); // simulate dynamics with noise added - h(); // output - - z_.zeros(); - for (std::size_t k = 0; k < n_y_; k++) { - // construct a Poisson distribution object with mean y[k] - pd_ = std::poisson_distribution<size_t>(y_[k]); - // pull random sample from this distribution - z_[k] = pd_(rng); - } - - return z_; -} -// ******************* SYS_T ******************* -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: src/lds_poisson_sys.cpp +summary: PLDS base type. + +--- + +# src/lds_poisson_sys.cpp + +PLDS base type. [More...](#detailed-description) + + + +## Detailed Description + + + +This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (`lds::poisson::sys_t`). It inherits functionality from the underlying linear dynamical system (`lds::sys_t`). + + + + + +## Source code + +```cpp +//===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst_h/lds_poisson_sys.h> + +lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, + data_t p0, data_t q0) + : lds::System(n_u, n_x, n_y, dt, p0, q0) { + diag_y_ = diagmat(y_); + pd_ = std::poisson_distribution<size_t>(0); +}; + +// Correct: Given measurement (z) and current input (u), update estimate of the +// state, covar, output. +// +// see Eden et al. 2004 +void lds::poisson::System::RecurseKe() { + // predict covariance + P_ = A_ * P_ * A_.t() + Q_; + + // update cov + P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); + Ke_ = P_ * C_.t(); + if (do_adapt_m) { + P_m_ += Q_m_; // predict (A_m = I) + P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update + Ke_m_ = P_m_ * C_.t(); + } +} + +// Simulate Measurement: z ~ Poisson(y) +const lds::Vector& lds::poisson::System::Simulate(const Vector& u_tm1) { + f(u_tm1, true); // simulate dynamics with noise added + h(); // output + + z_.zeros(); + for (std::size_t k = 0; k < n_y_; k++) { + // construct a Poisson distribution object with mean y[k] + pd_ = std::poisson_distribution<size_t>(y_[k]); + // pull random sample from this distribution + z_[k] = pd_(rng); + } + + return z_; +} +// ******************* SYS_T ******************* +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md index 4dbed5d6..d508364e 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md @@ -1,106 +1,108 @@ ---- -title: ldsCtrlEst_h/lds_poisson_sys.h -summary: PLDS base type. - ---- - -# ldsCtrlEst_h/lds_poisson_sys.h - -PLDS base type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)** <br>Poisson [System]() type. | - -## Detailed Description - - - -This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (`[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)`). It inherits functionality from the underlying linear dynamical system (`[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)`). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_POISSON_SYS_H -#define LDSCTRLEST_LDS_POISSON_SYS_H - -// namespace -#include "lds_poisson.h" -// system -#include "lds_sys.h" - -// needed for Poisson random number generation -#include <random> - -namespace lds { -namespace poisson { - -class System : public lds::System { - public: - System() = default; - - System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, - data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); - - const Vector& Simulate(const Vector& u_tm1) override; - - protected: - void h() override { - cx_ = C_ * x_; - y_ = exp(cx_ + d_); - diag_y_.diag() = y_; - }; - - void RecurseKe() override; - - private: - // Poisson-output-specific - Matrix diag_y_; - std::poisson_distribution<size_t> - pd_; -}; // System -} // namespace poisson -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_poisson_sys.h +summary: PLDS base type. + +--- + +# ldsCtrlEst_h/lds_poisson_sys.h + +PLDS base type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)** <br>Poisson [System]() type. | + +## Detailed Description + + + +This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (`[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)`). It inherits functionality from the underlying linear dynamical system (`[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)`). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_POISSON_SYS_H +#define LDSCTRLEST_LDS_POISSON_SYS_H + +// namespace +#include "lds_poisson.h" +// system +#include "lds_sys.h" + +// needed for Poisson random number generation +#include <random> + +namespace lds { +namespace poisson { + +class System : public lds::System { + public: + System() = default; + + System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, + data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); + + const Vector& Simulate(const Vector& u_tm1) override; + + protected: + void h() override { + cx_ = C_ * x_; + y_ = exp(cx_ + d_); + diag_y_.diag() = y_; + }; + + Vector h_(Vector x) override { return exp(C_ * x + d_); }; + + void RecurseKe() override; + + private: + // Poisson-output-specific + Matrix diag_y_; + std::poisson_distribution<size_t> + pd_; +}; // System +} // namespace poisson +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md index c59f532d..54127497 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md @@ -1,275 +1,275 @@ ---- -title: ldsCtrlEst_h/lds_sctrl.h -summary: SwitchedController type. - ---- - -# ldsCtrlEst_h/lds_sctrl.h - -SwitchedController type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/)** <br>[SwitchedController]() Type. | - -## Detailed Description - - - -This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems ([lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/)). - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_SCTRL_H -#define LDSCTRLEST_LDS_SCTRL_H - -#include "lds_ctrl.h" -#include "lds_uniform_mats.h" -#include "lds_uniform_vecs.h" - -namespace lds { -template <typename System> -class SwitchedController : public Controller<System> { - public: - SwitchedController() = default; - - SwitchedController(const std::vector<System>& systems, data_t u_lb, - data_t u_ub, size_t control_type = 0); - - SwitchedController(std::vector<System>&& systems, data_t u_lb, data_t u_ub, - size_t control_type = 0); - - void Switch(size_t idx, bool do_force_switch = false); - - void set_Kc(const UniformMatrixList<>& Kc) { - Kc_list_ = Kc; - Kc_ = Kc_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - void set_Kc(UniformMatrixList<>&& Kc) { - Kc_list_ = std::move(Kc); - Kc_ = Kc_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - - void set_Kc_inty(const UniformMatrixList<>& Kc_inty) { - Kc_inty_list_ = Kc_inty; - Kc_inty_ = Kc_inty_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - void set_Kc_inty(UniformMatrixList<>&& Kc_inty) { - Kc_inty_list_ = std::move(Kc_inty); - Kc_inty_ = Kc_inty_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - - void set_Kc_u(const UniformMatrixList<>& Kc_u) { - Kc_u_list_ = Kc_u; - Kc_u_ = Kc_u_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - void set_Kc_u(UniformMatrixList<>&& Kc_u) { - Kc_u_list_ = std::move(Kc_u); - Kc_u_ = Kc_u_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - - void set_g_design(const UniformVectorList& g) { - g_design_list_ = g; - g_design_ = g_design_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - void set_g_design(UniformVectorList&& g) { - g_design_list_ = std::move(g); - g_design_ = g_design_list_.at(0); // set to first - if (idx_ != 0) { - Switch(idx_, true); - } - }; - - // make sure base class template methods available - using lds::Controller<System>::Controller; - using lds::Controller<System>::Control; - using lds::Controller<System>::ControlOutputReference; - - using lds::Controller<System>::sys; - using lds::Controller<System>::Kc; - using lds::Controller<System>::Kc_inty; - using lds::Controller<System>::Kc_u; - using lds::Controller<System>::g_design; - using lds::Controller<System>::u_ref; - using lds::Controller<System>::x_ref; - using lds::Controller<System>::y_ref; - using lds::Controller<System>::control_type; - - using lds::Controller<System>::set_u_ref; - using lds::Controller<System>::set_x_ref; - using lds::Controller<System>::set_y_ref; - using lds::Controller<System>::set_tau_awu; - using lds::Controller<System>::set_control_type; - - using lds::Controller<System>::Reset; - using lds::Controller<System>::Print; - - protected: - std::vector<System> - systems_; - size_t n_sys_{}; - size_t idx_{}; - - // controller gains could be different for each - UniformMatrixList<> Kc_list_; - UniformMatrixList<> Kc_inty_list_; - UniformMatrixList<> Kc_u_list_; - - // design-phase input gain could also be different - UniformVectorList g_design_list_; - - // TODO(mfbolus): not sure why I need to do this. - using Controller<System>::Kc_; - using Controller<System>::Kc_inty_; - using Controller<System>::Kc_u_; - using Controller<System>::g_design_; - using Controller<System>::sys_; - // using Controller<System>::u_ref_; - // using Controller<System>::x_ref_; - // using Controller<System>::y_ref_; - // - using Controller<System>::control_type_; - - private: - void InitVars(); - - using lds::Controller<System>::set_sys; - // using Controller<System>::set_Kc; - // using Controller<System>::set_Kc_inty; - // using Controller<System>::set_Kc_u; - // using Controller<System>::set_g_design; -}; - -template <typename System> -inline SwitchedController<System>::SwitchedController( - const std::vector<System>& systems, data_t u_lb, data_t u_ub, - size_t control_type) - : Controller<System>(systems.at(0), u_lb, u_ub, control_type), - systems_(systems) { - InitVars(); -} - -template <typename System> -inline SwitchedController<System>::SwitchedController( - std::vector<System>&& systems, data_t u_lb, data_t u_ub, - size_t control_type) - : Controller<System>(System(systems.at(0).n_u(), systems.at(0).n_x(), - systems.at(0).n_y(), systems.at(0).dt()), - u_lb, u_ub, control_type), - systems_(std::move(systems)) { - InitVars(); -} - -template <typename System> -inline void SwitchedController<System>::InitVars() { - n_sys_ = systems_.size(); - sys_ = systems_.at(0); - - Kc_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_)); - Kc_inty_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_inty_)); - Kc_u_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_inty_)); - g_design_list_ = UniformVectorList(std::vector<Vector>(n_sys_, g_design_)); -} - -template <typename System> -inline void SwitchedController<System>::Switch(size_t idx, - bool do_force_switch) { - if ((idx == idx_) && !do_force_switch) { - return; // already there. - } - - // put old up and get new one out - systems_.at(idx_) = std::move(sys_); - sys_ = std::move(systems_.at(idx)); - - // set the state of this system to that of the previous system - // TODO(mfbolus): This will only work as intended if state matrix is the same. - // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. - sys_.set_m(systems_.at(idx_).m(), true); - sys_.set_x(systems_.at(idx_).x()); - - // swap controller gains - Kc_list_.Swap(Kc_, idx_); - Kc_list_.Swap(Kc_, idx); - - if (control_type_ & kControlTypeIntY) { - Kc_inty_list_.Swap(Kc_inty_, idx_); - Kc_inty_list_.Swap(Kc_inty_, idx); - } - - if (control_type_ & kControlTypeDeltaU) { - Kc_u_list_.Swap(Kc_u_, idx_); - Kc_u_list_.Swap(Kc_u_, idx); - } - - g_design_list_.Swap(g_design_, idx_); - g_design_list_.Swap(g_design_, idx); - - idx_ = idx; -} // Switch - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_sctrl.h +summary: SwitchedController type. + +--- + +# ldsCtrlEst_h/lds_sctrl.h + +SwitchedController type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/)** <br>[SwitchedController]() Type. | + +## Detailed Description + + + +This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems ([lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/)). + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_SCTRL_H +#define LDSCTRLEST_LDS_SCTRL_H + +#include "lds_ctrl.h" +#include "lds_uniform_mats.h" +#include "lds_uniform_vecs.h" + +namespace lds { +template <typename System> +class SwitchedController : public Controller<System> { + public: + SwitchedController() = default; + + SwitchedController(const std::vector<System>& systems, data_t u_lb, + data_t u_ub, size_t control_type = 0); + + SwitchedController(std::vector<System>&& systems, data_t u_lb, data_t u_ub, + size_t control_type = 0); + + void Switch(size_t idx, bool do_force_switch = false); + + void set_Kc(const UniformMatrixList<>& Kc) { + Kc_list_ = Kc; + Kc_ = Kc_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + void set_Kc(UniformMatrixList<>&& Kc) { + Kc_list_ = std::move(Kc); + Kc_ = Kc_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + + void set_Kc_inty(const UniformMatrixList<>& Kc_inty) { + Kc_inty_list_ = Kc_inty; + Kc_inty_ = Kc_inty_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + void set_Kc_inty(UniformMatrixList<>&& Kc_inty) { + Kc_inty_list_ = std::move(Kc_inty); + Kc_inty_ = Kc_inty_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + + void set_Kc_u(const UniformMatrixList<>& Kc_u) { + Kc_u_list_ = Kc_u; + Kc_u_ = Kc_u_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + void set_Kc_u(UniformMatrixList<>&& Kc_u) { + Kc_u_list_ = std::move(Kc_u); + Kc_u_ = Kc_u_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + + void set_g_design(const UniformVectorList& g) { + g_design_list_ = g; + g_design_ = g_design_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + void set_g_design(UniformVectorList&& g) { + g_design_list_ = std::move(g); + g_design_ = g_design_list_.at(0); // set to first + if (idx_ != 0) { + Switch(idx_, true); + } + }; + + // make sure base class template methods available + using lds::Controller<System>::Controller; + using lds::Controller<System>::Control; + using lds::Controller<System>::ControlOutputReference; + + using lds::Controller<System>::sys; + using lds::Controller<System>::Kc; + using lds::Controller<System>::Kc_inty; + using lds::Controller<System>::Kc_u; + using lds::Controller<System>::g_design; + using lds::Controller<System>::u_ref; + using lds::Controller<System>::x_ref; + using lds::Controller<System>::y_ref; + using lds::Controller<System>::control_type; + + using lds::Controller<System>::set_u_ref; + using lds::Controller<System>::set_x_ref; + using lds::Controller<System>::set_y_ref; + using lds::Controller<System>::set_tau_awu; + using lds::Controller<System>::set_control_type; + + using lds::Controller<System>::Reset; + using lds::Controller<System>::Print; + + protected: + std::vector<System> + systems_; + size_t n_sys_{}; + size_t idx_{}; + + // controller gains could be different for each + UniformMatrixList<> Kc_list_; + UniformMatrixList<> Kc_inty_list_; + UniformMatrixList<> Kc_u_list_; + + // design-phase input gain could also be different + UniformVectorList g_design_list_; + + // TODO(mfbolus): not sure why I need to do this. + using Controller<System>::Kc_; + using Controller<System>::Kc_inty_; + using Controller<System>::Kc_u_; + using Controller<System>::g_design_; + using Controller<System>::sys_; + // using Controller<System>::u_ref_; + // using Controller<System>::x_ref_; + // using Controller<System>::y_ref_; + // + using Controller<System>::control_type_; + + private: + void InitVars(); + + using lds::Controller<System>::set_sys; + // using Controller<System>::set_Kc; + // using Controller<System>::set_Kc_inty; + // using Controller<System>::set_Kc_u; + // using Controller<System>::set_g_design; +}; + +template <typename System> +inline SwitchedController<System>::SwitchedController( + const std::vector<System>& systems, data_t u_lb, data_t u_ub, + size_t control_type) + : Controller<System>(systems.at(0), u_lb, u_ub, control_type), + systems_(systems) { + InitVars(); +} + +template <typename System> +inline SwitchedController<System>::SwitchedController( + std::vector<System>&& systems, data_t u_lb, data_t u_ub, + size_t control_type) + : Controller<System>(System(systems.at(0).n_u(), systems.at(0).n_x(), + systems.at(0).n_y(), systems.at(0).dt()), + u_lb, u_ub, control_type), + systems_(std::move(systems)) { + InitVars(); +} + +template <typename System> +inline void SwitchedController<System>::InitVars() { + n_sys_ = systems_.size(); + sys_ = systems_.at(0); + + Kc_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_)); + Kc_inty_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_inty_)); + Kc_u_list_ = UniformMatrixList<>(std::vector<Matrix>(n_sys_, Kc_inty_)); + g_design_list_ = UniformVectorList(std::vector<Vector>(n_sys_, g_design_)); +} + +template <typename System> +inline void SwitchedController<System>::Switch(size_t idx, + bool do_force_switch) { + if ((idx == idx_) && !do_force_switch) { + return; // already there. + } + + // put old up and get new one out + systems_.at(idx_) = std::move(sys_); + sys_ = std::move(systems_.at(idx)); + + // set the state of this system to that of the previous system + // TODO(mfbolus): This will only work as intended if state matrix is the same. + // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. + sys_.set_m(systems_.at(idx_).m(), true); + sys_.set_x(systems_.at(idx_).x()); + + // swap controller gains + Kc_list_.Swap(Kc_, idx_); + Kc_list_.Swap(Kc_, idx); + + if (control_type_ & kControlTypeIntY) { + Kc_inty_list_.Swap(Kc_inty_, idx_); + Kc_inty_list_.Swap(Kc_inty_, idx); + } + + if (control_type_ & kControlTypeDeltaU) { + Kc_u_list_.Swap(Kc_u_, idx_); + Kc_u_list_.Swap(Kc_u_, idx); + } + + g_design_list_.Swap(g_design_, idx_); + g_design_list_.Swap(g_design_, idx); + + idx_ = idx; +} // Switch + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md index 8e313360..3c311f36 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md @@ -1,140 +1,184 @@ ---- -title: src/lds_sys.cpp -summary: LDS base type. - ---- - -# src/lds_sys.cpp - -LDS base type. [More...](#detailed-description) - - - -## Detailed Description - - - -This file implements the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class. - - - - - -## Source code - -```cpp -//===-- lds_sys.cpp - LDS -------------------------------------------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst_h/lds_sys.h> - -lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, - data_t q0) - : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { - InitVars(p0, q0); -} - -void lds::System::InitVars(data_t p0, data_t q0) { - // initial conditions. - x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) - P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); - - m0_ = x0_; - P0_m_ = P0_; - - // signals - x_ = x0_; - P_ = P0_; - m_ = m0_; - P_m_ = P0_m_; - y_ = Vector(n_y_, fill::zeros); - cx_ = Vector(n_y_, fill::zeros); - z_ = Vector(n_y_, fill::zeros); - - // By default, random walk where each state is independent - // In this way, provides independent estimates of rate per channel of output. - A_ = Matrix(n_x_, n_x_, fill::eye); - B_ = Matrix(n_x_, n_u_, fill::zeros); - g_ = Vector(n_u_, fill::ones); - Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); - Q_m_ = Q_; - - C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by - d_ = Vector(n_y_, fill::zeros); - - Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. - Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. - - do_adapt_m = false; -} - -// Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state -// and update estimate of the state, covar, output using Kalman filter -void lds::System::Filter(const Vector& u_tm1, const Vector& z_t) { - // predict mean - f(u_tm1); // dynamics - - h(); // output - - // recursively calculate esimator gains (or just keep existing values) - // (also predicts+updates estimate covariance) - RecurseKe(); - - // update - x_ += Ke_ * (z_t - y_); - if (do_adapt_m) { - m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance - } - - // With new state, estimate output. - h(); // --> posterior -} - -void lds::System::Reset() { - // reset to initial conditions - x_ = x0_; // mean - P_ = P0_; // cov of state estimate - m_ = m0_; // process disturbance - P_m_ = P0_m_; // cov of disturbance estimate - h(); -} - -void lds::System::Print() { - std::cout << "\n ********** SYSTEM ********** \n"; - std::cout << "x: \n" << x_ << "\n"; - std::cout << "P: \n" << P_ << "\n"; - std::cout << "A: \n" << A_ << "\n"; - std::cout << "B: \n" << B_ << "\n"; - std::cout << "g: \n" << g_ << "\n"; - std::cout << "m: \n" << m_ << "\n"; - std::cout << "Q: \n" << Q_ << "\n"; - std::cout << "Q_m: \n" << Q_m_ << "\n"; - std::cout << "d: \n" << d_ << "\n"; - std::cout << "C: \n" << C_ << "\n"; - std::cout << "y: \n" << y_ << "\n"; -} - -//******************* SYS_T ******************* -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: src/lds_sys.cpp +summary: LDS base type. + +--- + +# src/lds_sys.cpp + +LDS base type. [More...](#detailed-description) + + + +## Detailed Description + + + +This file implements the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class. + + + + + +## Source code + +```cpp +//===-- lds_sys.cpp - LDS -------------------------------------------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst_h/lds_sys.h> + +#include <vector> + +lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, + data_t q0) + : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { + InitVars(p0, q0); +} + +void lds::System::InitVars(data_t p0, data_t q0) { + // initial conditions. + x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) + P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); + + m0_ = x0_; + P0_m_ = P0_; + + // signals + x_ = x0_; + P_ = P0_; + m_ = m0_; + P_m_ = P0_m_; + y_ = Vector(n_y_, fill::zeros); + cx_ = Vector(n_y_, fill::zeros); + z_ = Vector(n_y_, fill::zeros); + + // By default, random walk where each state is independent + // In this way, provides independent estimates of rate per channel of output. + A_ = Matrix(n_x_, n_x_, fill::eye); + B_ = Matrix(n_x_, n_u_, fill::zeros); + g_ = Vector(n_u_, fill::ones); + Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); + Q_m_ = Q_; + + C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by + d_ = Vector(n_y_, fill::zeros); + + Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. + Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. + + do_adapt_m = false; +} + +// Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state +// and update estimate of the state, covar, output using Kalman filter +void lds::System::Filter(const Vector& u_tm1, const Vector& z_t) { + // predict mean + f(u_tm1); // dynamics + + h(); // output + + // recursively calculate esimator gains (or just keep existing values) + // (also predicts+updates estimate covariance) + RecurseKe(); + + // update + x_ += Ke_ * (z_t - y_); + if (do_adapt_m) { + m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance + } + + // With new state, estimate output. + h(); // --> posterior +} + +void lds::System::Reset() { + // reset to initial conditions + x_ = x0_; // mean + P_ = P0_; // cov of state estimate + m_ = m0_; // process disturbance + P_m_ = P0_m_; // cov of disturbance estimate + h(); +} + +std::vector<lds::UniformMatrixList<lds::kMatFreeDim2>> +lds::System::nstep_pred_block(lds::UniformMatrixList<lds::kMatFreeDim2> u, + lds::UniformMatrixList<lds::kMatFreeDim2> z, + size_t n_pred) { + lds::UniformMatrixList<lds::kMatFreeDim2> x_filt; + lds::UniformMatrixList<lds::kMatFreeDim2> x_pred; + lds::UniformMatrixList<lds::kMatFreeDim2> y_pred; + + for (size_t k = 0; k < u.size(); k++) { + Reset(); + size_t n_t = arma::size(u[k])[1]; + Matrix x_filt_k(n_x_, n_t, fill::zeros); + Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); + Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); + + for (size_t t = 0; t < n_t - n_pred; t++) { + Vector x_pred_ahead = x_; + for (size_t t_u = t; t_u < t + n_pred; t_u++) { + x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); + } + x_pred_k.col(t) = x_pred_ahead; + y_pred_k.col(t) = h_(x_pred_ahead); + if (t > 0) { + Filter(u[k].col(t - 1), z[k].col(t)); + } + x_filt_k.col(t) = x_; // given previous measurment + } + + for (size_t t = n_t - n_pred; t < n_t; t++) { + if (t > 0) { + Filter(u[k].col(t - 1), z[k].col(t)); + } + x_filt_k.col(t) = x_; + } + + x_filt.append(x_filt_k); + x_pred.append(x_pred_k); + y_pred.append(y_pred_k); + } + return {x_filt, x_pred, y_pred}; +} + +void lds::System::Print() { + std::cout << "\n ********** SYSTEM ********** \n"; + std::cout << "x: \n" << x_ << "\n"; + std::cout << "P: \n" << P_ << "\n"; + std::cout << "A: \n" << A_ << "\n"; + std::cout << "B: \n" << B_ << "\n"; + std::cout << "g: \n" << g_ << "\n"; + std::cout << "m: \n" << m_ << "\n"; + std::cout << "Q: \n" << Q_ << "\n"; + std::cout << "Q_m: \n" << Q_m_ << "\n"; + std::cout << "d: \n" << d_ << "\n"; + std::cout << "C: \n" << C_ << "\n"; + std::cout << "y: \n" << y_ << "\n"; +} + +//******************* SYS_T ******************* +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md index 9e9688d3..244b68e1 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md @@ -1,184 +1,191 @@ ---- -title: ldsCtrlEst_h/lds_sys.h -summary: LDS base type. - ---- - -# ldsCtrlEst_h/lds_sys.h - -LDS base type. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** <br>Linear Dynamical [System]() Type. | - -## Detailed Description - - - -This file declares and partially defines the base type for linear dynamical systems (`[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)`). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// Limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_SYS_H -#define LDSCTRLEST_LDS_SYS_H - -#include "lds.h" - -namespace lds { -class System { - public: - System() = default; - - System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, - data_t q0 = kDefaultQ0); - - virtual ~System() {} - - void Filter(const Vector& u_tm1, const Vector& z); - - virtual const Vector& Simulate(const Vector& u_tm1) = 0; - - void f(const Vector& u, bool do_add_noise = false) { - x_ = A_ * x_ + B_ * (g_ % u) + m_; - if (do_add_noise) { - x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); - } - }; - - virtual void h() = 0; - - size_t n_u() const { return n_u_; }; - size_t n_x() const { return n_x_; }; - size_t n_y() const { return n_y_; }; - data_t dt() const { return dt_; }; - - const Vector& x() const { return x_; }; - const Matrix& P() const { return P_; }; - const Vector& m() const { return m_; }; - const Matrix& P_m() const { return P_m_; }; - const Vector& cx() const { return cx_; }; - const Vector& y() const { return y_; }; - - const Vector& x0() const { return x0_; }; - const Vector& m0() const { return m0_; }; - - const Matrix& A() const { return A_; }; - const Matrix& B() const { return B_; }; - const Vector& g() const { return g_; }; - const Matrix& C() const { return C_; }; - const Vector& d() const { return d_; }; - const Matrix& Ke() const { return Ke_; }; - const Matrix& Ke_m() const { return Ke_m_; }; - const Matrix& Q() { return Q_; }; - const Matrix& Q_m() { return Q_m_; }; - const Matrix& P0() { return P0_; }; - const Matrix& P0_m() { return P0_m_; }; - - void set_A(const Matrix& A) { Reassign(A_, A); }; - void set_B(const Matrix& B) { Reassign(B_, B); }; - void set_m(const Vector& m, bool do_force_assign=false) { - Reassign(m0_, m); - if ((!do_adapt_m) || do_force_assign) { - Reassign(m_, m); - } - }; - void set_g(const Vector& g) { Reassign(g_, g); }; - void set_Q(const Matrix& Q) { Reassign(Q_, Q); }; - void set_Q_m(const Matrix& Q_m) { Reassign(Q_m_, Q_m); }; - void set_x0(const Vector& x0) { Reassign(x0_, x0); }; - void set_P0(const Matrix& P0) { Reassign(P0_, P0); }; - void set_P0_m(const Matrix& P0_m) { Reassign(P0_m_, P0_m); }; - void set_C(const Matrix& C) { Reassign(C_, C); }; - void set_d(const Vector& d) { Reassign(d_, d); }; - void set_x(const Vector& x) { - Reassign(x_, x); - h(); - }; - - void Reset(); - - void Print(); - - // safe to leave this public and non-const - bool do_adapt_m{}; - - protected: - virtual void RecurseKe() = 0; - void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); - - std::size_t n_x_{}; - std::size_t n_u_{}; - std::size_t n_y_{}; - data_t dt_{}; - - // Signals: - Vector x_; - Matrix P_; - Vector m_; - Matrix P_m_; - Vector cx_; - Vector y_; - Vector z_; - - // Parameters: - Vector x0_; - Matrix P0_; - Vector m0_; - Matrix P0_m_; - Matrix A_; - Matrix B_; - Vector g_; - Matrix Q_; - Matrix Q_m_; - Matrix C_; - Vector d_; - - Matrix Ke_; - Matrix Ke_m_; -}; // System - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_sys.h +summary: LDS base type. + +--- + +# ldsCtrlEst_h/lds_sys.h + +LDS base type. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** <br>Linear Dynamical [System]() Type. | + +## Detailed Description + + + +This file declares and partially defines the base type for linear dynamical systems (`[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)`). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// Limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_SYS_H +#define LDSCTRLEST_LDS_SYS_H + +#include "lds.h" +#include "lds_uniform_mats.h" + +namespace lds { +class System { + public: + System() = default; + + System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, + data_t q0 = kDefaultQ0); + + virtual ~System() {} + + void Filter(const Vector& u_tm1, const Vector& z); + + virtual const Vector& Simulate(const Vector& u_tm1) = 0; + + void f(const Vector& u, bool do_add_noise = false) { + x_ = A_ * x_ + B_ * (g_ % u) + m_; + if (do_add_noise) { + x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); + } + }; + + virtual void h() = 0; + + virtual Vector h_(Vector x) = 0; + + size_t n_u() const { return n_u_; }; + size_t n_x() const { return n_x_; }; + size_t n_y() const { return n_y_; }; + data_t dt() const { return dt_; }; + + const Vector& x() const { return x_; }; + const Matrix& P() const { return P_; }; + const Vector& m() const { return m_; }; + const Matrix& P_m() const { return P_m_; }; + const Vector& cx() const { return cx_; }; + const Vector& y() const { return y_; }; + + const Vector& x0() const { return x0_; }; + const Vector& m0() const { return m0_; }; + + const Matrix& A() const { return A_; }; + const Matrix& B() const { return B_; }; + const Vector& g() const { return g_; }; + const Matrix& C() const { return C_; }; + const Vector& d() const { return d_; }; + const Matrix& Ke() const { return Ke_; }; + const Matrix& Ke_m() const { return Ke_m_; }; + const Matrix& Q() { return Q_; }; + const Matrix& Q_m() { return Q_m_; }; + const Matrix& P0() { return P0_; }; + const Matrix& P0_m() { return P0_m_; }; + + void set_A(const Matrix& A) { Reassign(A_, A); }; + void set_B(const Matrix& B) { Reassign(B_, B); }; + void set_m(const Vector& m, bool do_force_assign = false) { + Reassign(m0_, m); + if ((!do_adapt_m) || do_force_assign) { + Reassign(m_, m); + } + }; + void set_g(const Vector& g) { Reassign(g_, g); }; + void set_Q(const Matrix& Q) { Reassign(Q_, Q); }; + void set_Q_m(const Matrix& Q_m) { Reassign(Q_m_, Q_m); }; + void set_x0(const Vector& x0) { Reassign(x0_, x0); }; + void set_P0(const Matrix& P0) { Reassign(P0_, P0); }; + void set_P0_m(const Matrix& P0_m) { Reassign(P0_m_, P0_m); }; + void set_C(const Matrix& C) { Reassign(C_, C); }; + void set_d(const Vector& d) { Reassign(d_, d); }; + void set_x(const Vector& x) { + Reassign(x_, x); + h(); + }; + + void Reset(); + + std::vector<UniformMatrixList<kMatFreeDim2>> nstep_pred_block( + UniformMatrixList<kMatFreeDim2> u, UniformMatrixList<kMatFreeDim2> z, + size_t n_pred = 1); + + void Print(); + + // safe to leave this public and non-const + bool do_adapt_m{}; + + protected: + virtual void RecurseKe() = 0; + void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); + + std::size_t n_x_{}; + std::size_t n_u_{}; + std::size_t n_y_{}; + data_t dt_{}; + + // Signals: + Vector x_; + Matrix P_; + Vector m_; + Matrix P_m_; + Vector cx_; + Vector y_; + Vector z_; + + // Parameters: + Vector x0_; + Matrix P0_; + Vector m0_; + Matrix P0_m_; + Matrix A_; + Matrix B_; + Vector g_; + Matrix Q_; + Matrix Q_m_; + Matrix C_; + Vector d_; + + Matrix Ke_; + Matrix Ke_m_; +}; // System + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md index fa447ebb..3f735c63 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md @@ -1,330 +1,343 @@ ---- -title: ldsCtrlEst_h/lds_uniform_mats.h -summary: List of uniformly sized matrices. - ---- - -# ldsCtrlEst_h/lds_uniform_mats.h - -List of uniformly sized matrices. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)** | - -## Detailed Description - - - -This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// Limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H -#define LDSCTRLEST_LDS_UNIFORM_MATS_H - -#include <array> // std::array -#include <vector> // std::vector - -#include "lds.h" - -namespace lds { -template <MatrixListFreeDim D = kMatFreeDimNone> -class UniformMatrixList : public std::vector<Matrix> { - private: - // TODO(mfbolus): would rather *uncomment* the below for sake of conversion - // using std::vector<Matrix>::vector; - using std::vector<Matrix>::operator=; - using std::vector<Matrix>::operator[]; - using std::vector<Matrix>::begin; - using std::vector<Matrix>::end; - using std::vector<Matrix>::size; - - public: - using std::vector<Matrix>::at; - UniformMatrixList() = default; - - explicit UniformMatrixList(const std::vector<Matrix>& mats, - std::array<size_t, 2> dim = {0, 0}); - - explicit UniformMatrixList(std::vector<Matrix>&& mats, - std::array<size_t, 2> dim = {0, 0}); - - UniformMatrixList(std::initializer_list<Matrix> mats, - std::array<size_t, 2> dim = {0, 0}); - - UniformMatrixList(const UniformMatrixList<D>& that); - - UniformMatrixList(UniformMatrixList<D>&& that) noexcept; - - ~UniformMatrixList() = default; - - const std::array<size_t, 2>& dim(size_t n = 0) const { return dim_.at(n); } - - size_t size() { return std::vector<Matrix>::size(); }; - - const Matrix& at(size_t n) { return std::vector<Matrix>::at(n); }; - - void Swap(Matrix& that, size_t n); - - UniformMatrixList<D>& operator=(const UniformMatrixList<D>& that); - UniformMatrixList<D>& operator=(UniformMatrixList<D>&& that) noexcept; - - private: - void CheckDimensions(std::array<size_t, 2> dim); - std::vector<std::array<size_t, 2>> dim_; -}; - -template <MatrixListFreeDim D> -inline void UniformMatrixList<D>::Swap(Matrix& that, size_t n) { - // make sure request in range - if (n >= this->size()) { - std::cerr - << "Requested UniformMatrixList element out of bounds. Skipping.\n"; - return; - } - // check dim - bool does_match = true; - if (!(D == kMatFreeDim1)) { - does_match = does_match && (dim_[0][0] == that.n_rows); - } - if (!(D == kMatFreeDim2)) { - does_match = does_match && (dim_[0][1] == that.n_cols); - } - if (!does_match) { - std::cerr << "Cannot swap a UniformMatrixList element for an element of " - "different size. Skipping.\n"; - return; - } - // if checks pass, perform swap - Matrix tmp = std::move((*this)[n]); - (*this)[n] = std::move(that); - that = std::move(tmp); - - if (D == kMatFreeDim1) { - this->dim_[n][0] = (*this)[n].n_rows; - } - if (D == kMatFreeDim2) { - this->dim_[n][1] = (*this)[n].n_cols; - } -} - -template <MatrixListFreeDim D> -inline UniformMatrixList<D>& UniformMatrixList<D>::operator=( - const UniformMatrixList<D>& that) { - // make sure dim_ vector is initialized - if (dim_.empty()) { - dim_ = std::vector<std::array<size_t, 2>>(that.size(), {0, 0}); - } - // check dimensions - if (!this->empty()) { - if (this->size() != that.size()) { - std::ostringstream ss; - ss << "cannot reassign " << this->size() << " matrices with " - << that.size() << " matrices"; - throw std::runtime_error(ss.str()); - } - - // if dimensions a not zero and do not match, skip move with error message. - bool dims_nonzero = true; - for (auto d : dim_) { - if (!(D == kMatFreeDim1) && d[0] < 1) { - dims_nonzero = false; - break; - } - if (!(D == kMatFreeDim2) && d[1] < 1) { - dims_nonzero = false; - break; - } - } - if (dims_nonzero) { - bool does_match = true; - if (!(D == kMatFreeDim1)) { - does_match = does_match && (dim_[0][0] == that.at(0).n_rows); - } - if (!(D == kMatFreeDim2)) { - does_match = does_match && (dim_[0][1] == that.at(0).n_cols); - } - if (!does_match) { - std::ostringstream ss; - ss << "cannot reassign matrices of size " << dim_[0][0] << "x" - << dim_[0][1] << " with matrices of size " << that.at(0).n_rows - << "x" << that.at(0).n_cols; - throw std::runtime_error(ss.str()); - } - } - } - - for (size_t k = 0; k < this->size(); k++) { - (*this)[k] = that[k]; - dim_[k] = that.dim(k); - } - - return (*this); -} - -template <MatrixListFreeDim D> -inline UniformMatrixList<D>& UniformMatrixList<D>::operator=( - UniformMatrixList<D>&& that) noexcept { - // // check dimensions - // // if empty, assume a default constructed object and safe to move - // if (!this->empty()) { - // if (this->size() != that.size()) { - // std::cerr << "Cannot reassign " << this->size() << " matrices with " - // << that.size() << " matrices. Skipping.\n"; - // return (*this); - // } - // - // // if dimensions a not zero and do not match, skip move with error - // message. bool dims_nonzero = true; for (auto d : dim_) { - // if (!(D == kMatFreeDim1) && (d[0] < 1)) { - // dims_nonzero = false; - // break; - // } - // if (!(D == kMatFreeDim2) && (d[1] < 1)) { - // dims_nonzero = false; - // break; - // } - // } - // - // if (dims_nonzero) { - // bool does_match = true; - // if (!(D == kMatFreeDim1)) { - // does_match = does_match && (dim_[0][0] == that.at(0).n_rows); - // } - // - // if (!(D == kMatFreeDim2)) { - // does_match = does_match && (dim_[0][1] == that.at(0).n_cols); - // } - // - // if (!does_match) { - // this->at(0).print("this[0] = "); - // that.at(0).print("that[0] = "); - // std::cerr - // << "Cannot move a UniformMatrixList element of size (" << - // that.at(0).n_rows << "," << that.at(0).n_cols << ") for an - // element of size (" << dim_[0][0] << "," << dim_[0][1] << "). - // Skipping.\n"; - // return (*this); - // } - // } - // } - - dim_ = that.dim_; - std::vector<Matrix>::operator=(std::move(that)); - - return (*this); -} - -template <MatrixListFreeDim D> -UniformMatrixList<D>::UniformMatrixList(const std::vector<Matrix>& mats, - std::array<size_t, 2> dim) - : vector(mats) { - CheckDimensions(dim); -} - -template <MatrixListFreeDim D> -UniformMatrixList<D>::UniformMatrixList(std::vector<Matrix>&& mats, - std::array<size_t, 2> dim) - : vector(std::move(mats)) { - CheckDimensions(dim); -}; - -template <MatrixListFreeDim D> -UniformMatrixList<D>::UniformMatrixList(std::initializer_list<Matrix> mats, - std::array<size_t, 2> dim) - : vector(mats) { - CheckDimensions(dim); -}; - -template <MatrixListFreeDim D> -UniformMatrixList<D>::UniformMatrixList(const UniformMatrixList<D>& that) - : vector(that) { - (*this) = that; -} - -template <MatrixListFreeDim D> -UniformMatrixList<D>::UniformMatrixList(UniformMatrixList<D>&& that) noexcept - : vector(std::move(that)) { - for (size_t k = 0; k < this->size(); k++) { - std::array<size_t, 2> dim_k({this->at(k).n_rows, this->at(k).n_cols}); - dim_.push_back(dim_k); - } -} - -template <MatrixListFreeDim D> -void UniformMatrixList<D>::CheckDimensions(std::array<size_t, 2> dim) { - // change behavior based on free dim D - if ((dim[0] == 0) && !(D == kMatFreeDim1)) { - dim[0] = this->at(0).n_rows; - } - if ((dim[1] == 0) && !(D == kMatFreeDim2)) { - dim[1] = this->at(0).n_cols; - } - - // make sure dimensiolaties are all uniform - bool does_match(true); - for (const Matrix& mat : *this) { - if (!(D == kMatFreeDim1)) { - does_match = does_match && (mat.n_rows == dim[0]); - } - if (!(D == kMatFreeDim2)) { - does_match = does_match && (mat.n_cols == dim[1]); - } - if (!does_match) { - throw std::runtime_error( - "Dimensionality of one or more input matrices are not uniform."); - } - } - - dim_ = std::vector<std::array<size_t, 2>>(this->size(), dim); - for (size_t k = 0; k < this->size(); k++) { - dim_[k][0] = (*this)[k].n_rows; - dim_[k][1] = (*this)[k].n_cols; - } -} - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_uniform_mats.h +summary: List of uniformly sized matrices. + +--- + +# ldsCtrlEst_h/lds_uniform_mats.h + +List of uniformly sized matrices. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)** | + +## Detailed Description + + + +This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// Limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H +#define LDSCTRLEST_LDS_UNIFORM_MATS_H + +#include <array> // std::array +#include <vector> // std::vector + +#include "lds.h" + +namespace lds { +template <MatrixListFreeDim D = kMatFreeDimNone> +class UniformMatrixList : public std::vector<Matrix> { + private: + // TODO(mfbolus): would rather *uncomment* the below for sake of conversion + // using std::vector<Matrix>::vector; + // don't allow push_back to be used since it doesn't check dims + using std::vector<Matrix>::push_back; + + public: + using std::vector<Matrix>::operator=; + using std::vector<Matrix>::operator[]; + using std::vector<Matrix>::begin; + using std::vector<Matrix>::end; + using std::vector<Matrix>::size; + using std::vector<Matrix>::at; + UniformMatrixList() = default; + + explicit UniformMatrixList(const std::vector<Matrix>& mats, + std::array<size_t, 2> dim = {0, 0}); + + explicit UniformMatrixList(std::vector<Matrix>&& mats, + std::array<size_t, 2> dim = {0, 0}); + + UniformMatrixList(std::initializer_list<Matrix> mats, + std::array<size_t, 2> dim = {0, 0}); + + UniformMatrixList(const UniformMatrixList<D>& that); + + UniformMatrixList(UniformMatrixList<D>&& that) noexcept; + + ~UniformMatrixList() = default; + + const std::array<size_t, 2>& dim(size_t n = 0) const { return dim_.at(n); } + + size_t size() { return std::vector<Matrix>::size(); }; + + const Matrix& at(size_t n) { return std::vector<Matrix>::at(n); }; + + void Swap(Matrix& that, size_t n); + + UniformMatrixList<D>& operator=(const UniformMatrixList<D>& that); + UniformMatrixList<D>& operator=(UniformMatrixList<D>&& that) noexcept; + void append(const Matrix& mat); + + private: + void CheckDimensions(std::array<size_t, 2> dim); + std::vector<std::array<size_t, 2>> dim_; +}; + +template <MatrixListFreeDim D> +inline void UniformMatrixList<D>::Swap(Matrix& that, size_t n) { + // make sure request in range + if (n >= this->size()) { + std::cerr + << "Requested UniformMatrixList element out of bounds. Skipping.\n"; + return; + } + // check dim + bool does_match = true; + if (!(D == kMatFreeDim1)) { + does_match = does_match && (dim_[0][0] == that.n_rows); + } + if (!(D == kMatFreeDim2)) { + does_match = does_match && (dim_[0][1] == that.n_cols); + } + if (!does_match) { + std::cerr << "Cannot swap a UniformMatrixList element for an element of " + "different size. Skipping.\n"; + return; + } + // if checks pass, perform swap + // not moving, since it causes memory issues. + // so this method isn't a memory-saver as designed for now + Matrix tmp = (*this)[n]; + (*this)[n] = that; + that = tmp; + + if (D == kMatFreeDim1) { + this->dim_[n][0] = (*this)[n].n_rows; + } + if (D == kMatFreeDim2) { + this->dim_[n][1] = (*this)[n].n_cols; + } +} + +template <MatrixListFreeDim D> +void UniformMatrixList<D>::append(const Matrix& mat) { + std::array<size_t, 2> dim({mat.n_rows, mat.n_cols}); + CheckDimensions(dim); + std::vector<Matrix>::push_back(mat); + dim_.push_back(dim); +} + +template <MatrixListFreeDim D> +inline UniformMatrixList<D>& UniformMatrixList<D>::operator=( + const UniformMatrixList<D>& that) { + // make sure dim_ vector is initialized + if (dim_.empty()) { + dim_ = std::vector<std::array<size_t, 2>>(that.size(), {0, 0}); + } + // check dimensions + if (!this->empty()) { + if (this->size() != that.size()) { + std::ostringstream ss; + ss << "cannot reassign " << this->size() << " matrices with " + << that.size() << " matrices"; + throw std::runtime_error(ss.str()); + } + + // if dimensions a not zero and do not match, skip move with error message. + bool dims_nonzero = true; + for (auto d : dim_) { + if (!(D == kMatFreeDim1) && d[0] < 1) { + dims_nonzero = false; + break; + } + if (!(D == kMatFreeDim2) && d[1] < 1) { + dims_nonzero = false; + break; + } + } + if (dims_nonzero) { + bool does_match = true; + if (!(D == kMatFreeDim1)) { + does_match = does_match && (dim_[0][0] == that.at(0).n_rows); + } + if (!(D == kMatFreeDim2)) { + does_match = does_match && (dim_[0][1] == that.at(0).n_cols); + } + if (!does_match) { + std::ostringstream ss; + ss << "cannot reassign matrices of size " << dim_[0][0] << "x" + << dim_[0][1] << " with matrices of size " << that.at(0).n_rows + << "x" << that.at(0).n_cols; + throw std::runtime_error(ss.str()); + } + } + } + + for (size_t k = 0; k < this->size(); k++) { + (*this)[k] = that[k]; + dim_[k] = that.dim(k); + } + + return (*this); +} + +template <MatrixListFreeDim D> +inline UniformMatrixList<D>& UniformMatrixList<D>::operator=( + UniformMatrixList<D>&& that) noexcept { + // // check dimensions + // // if empty, assume a default constructed object and safe to move + // if (!this->empty()) { + // if (this->size() != that.size()) { + // std::cerr << "Cannot reassign " << this->size() << " matrices with " + // << that.size() << " matrices. Skipping.\n"; + // return (*this); + // } + // + // // if dimensions a not zero and do not match, skip move with error + // message. bool dims_nonzero = true; for (auto d : dim_) { + // if (!(D == kMatFreeDim1) && (d[0] < 1)) { + // dims_nonzero = false; + // break; + // } + // if (!(D == kMatFreeDim2) && (d[1] < 1)) { + // dims_nonzero = false; + // break; + // } + // } + // + // if (dims_nonzero) { + // bool does_match = true; + // if (!(D == kMatFreeDim1)) { + // does_match = does_match && (dim_[0][0] == that.at(0).n_rows); + // } + // + // if (!(D == kMatFreeDim2)) { + // does_match = does_match && (dim_[0][1] == that.at(0).n_cols); + // } + // + // if (!does_match) { + // this->at(0).print("this[0] = "); + // that.at(0).print("that[0] = "); + // std::cerr + // << "Cannot move a UniformMatrixList element of size (" << + // that.at(0).n_rows << "," << that.at(0).n_cols << ") for an + // element of size (" << dim_[0][0] << "," << dim_[0][1] << "). + // Skipping.\n"; + // return (*this); + // } + // } + // } + + dim_ = that.dim_; + std::vector<Matrix>::operator=(std::move(that)); + + return (*this); +} + +template <MatrixListFreeDim D> +UniformMatrixList<D>::UniformMatrixList(const std::vector<Matrix>& mats, + std::array<size_t, 2> dim) + : vector(mats) { + CheckDimensions(dim); +} + +template <MatrixListFreeDim D> +UniformMatrixList<D>::UniformMatrixList(std::vector<Matrix>&& mats, + std::array<size_t, 2> dim) + : vector(std::move(mats)) { + CheckDimensions(dim); +}; + +template <MatrixListFreeDim D> +UniformMatrixList<D>::UniformMatrixList(std::initializer_list<Matrix> mats, + std::array<size_t, 2> dim) + : vector(mats) { + CheckDimensions(dim); +}; + +template <MatrixListFreeDim D> +UniformMatrixList<D>::UniformMatrixList(const UniformMatrixList<D>& that) + : vector(that) { + (*this) = that; +} + +template <MatrixListFreeDim D> +UniformMatrixList<D>::UniformMatrixList(UniformMatrixList<D>&& that) noexcept + : vector(std::move(that)) { + for (size_t k = 0; k < this->size(); k++) { + std::array<size_t, 2> dim_k({this->at(k).n_rows, this->at(k).n_cols}); + dim_.push_back(dim_k); + } +} + +template <MatrixListFreeDim D> +void UniformMatrixList<D>::CheckDimensions(std::array<size_t, 2> dim) { + // change behavior based on free dim D + if ((dim[0] == 0) && !(D == kMatFreeDim1)) { + dim[0] = this->at(0).n_rows; + } + if ((dim[1] == 0) && !(D == kMatFreeDim2)) { + dim[1] = this->at(0).n_cols; + } + + // make sure dimensiolaties are all uniform + bool does_match(true); + for (const Matrix& mat : *this) { + if (!(D == kMatFreeDim1)) { + does_match = does_match && (mat.n_rows == dim[0]); + } + if (!(D == kMatFreeDim2)) { + does_match = does_match && (mat.n_cols == dim[1]); + } + if (!does_match) { + throw std::runtime_error( + "Dimensionality of one or more input matrices are not uniform."); + } + } + + dim_ = std::vector<std::array<size_t, 2>>(this->size(), dim); + for (size_t k = 0; k < this->size(); k++) { + dim_[k][0] = (*this)[k].n_rows; + dim_[k][1] = (*this)[k].n_cols; + } +} + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md index 33c86e2f..7b49aa65 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md @@ -1,268 +1,268 @@ ---- -title: ldsCtrlEst_h/lds_uniform_systems.h -summary: List of uniformly sized Systems. - ---- - -# ldsCtrlEst_h/lds_uniform_systems.h - -List of uniformly sized Systems. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/)** | - -## Detailed Description - - - -This file provides a container for uniformly sized Systems. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// Limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H -#define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H - -#include <array> // std::array -#include <vector> // std::vector - -// namespace -#include "lds.h" -// System type -#include "lds_sys.h" - -namespace lds { -template <typename System> -class UniformSystemList : public std::vector<System> { - static_assert(std::is_base_of<lds::System, System>::value, - "System must be derived from lds::System type."); - - private: - // TODO(mfbolus): would rather *uncomment* the below for sake of conversion - // using std::vector<System>::vector; - using std::vector<System>::operator=; - using std::vector<System>::operator[]; - using std::vector<System>::at; - using std::vector<System>::begin; - using std::vector<System>::end; - using std::vector<System>::size; - - public: - UniformSystemList() = default; - - explicit UniformSystemList(const std::vector<System>& systems, - std::array<size_t, 3> dim = {0, 0, 0}); - - explicit UniformSystemList(std::vector<System>&& systems, - std::array<size_t, 3> dim = {0, 0, 0}); - - UniformSystemList(std::initializer_list<System> systems, - std::array<size_t, 3> dim = {0, 0, 0}); - - UniformSystemList(const UniformSystemList& that); - UniformSystemList(UniformSystemList&& that) noexcept; - - ~UniformSystemList() = default; - - const std::array<size_t, 3>& dim() const { return dim_; } - - size_t size() { return std::vector<System>::size(); }; - - const System& at(size_t n) { return std::vector<System>::at(n); }; - - void Swap(System& that, size_t n); - - UniformSystemList& operator=(const UniformSystemList& that); - UniformSystemList& operator=(UniformSystemList&& that) noexcept; - - private: - void CheckDimensions(std::array<size_t, 3> dim); - std::array<size_t, 3> dim_{}; -}; - -template <typename System> -inline void UniformSystemList<System>::Swap(System& that, size_t n) { - // make sure request in range - if (n >= this->size()) { - std::cerr - << "Requested UniformSystemList element out of bounds. Skipping.\n"; - return; - } - // check dim - bool does_match = (dim_[0] == that.n_u()) && (dim_[1] == that.n_x()) && - (dim_[2] == that.n_y()); - if (!does_match) { - std::cerr << "Cannot swap a UniformSystemList element for an element of " - "different size. Skipping.\n"; - return; - } - // if checks pass, perform swap - System tmp = std::move((*this)[n]); - (*this)[n] = std::move(that); - that = std::move(tmp); -} - -template <typename System> -inline UniformSystemList<System>& UniformSystemList<System>::operator=( - const UniformSystemList& that) { - // check dimensions - if (!this->empty()) { - if (this->size() != that.size()) { - std::ostringstream ss; - ss << "cannot reassign " << this->size() << " systems with " - << that.size() << " systems"; - throw std::runtime_error(ss.str()); - } - - if (dim_[0] + dim_[1] + dim_[2]) { - std::array<size_t, 3> other_dim(that.dim()); - if (dim_ != other_dim) { - std::ostringstream ss; - ss << "cannot reassign systems of size " << dim_[0] << "x" << dim_[1] - << "x" << dim_[2] << " with systems of size " << other_dim[0] << "x" - << other_dim[1] << "x" << dim_[2]; - throw std::runtime_error(ss.str()); - } - } - } - - for (size_t k = 0; k < this->size(); k++) { - (*this)[k] = that[k]; - } - return (*this); -} - -template <typename System> -inline UniformSystemList<System>& UniformSystemList<System>::operator=( - UniformSystemList&& that) noexcept { - // // check dimensions - // // if empty, assume a default constructed object and safe to move - // if (!this->empty()) { - // if (this->size() != that.size()) { - // std::cerr << "Cannot reassign " << this->size() << " systems with " - // << that.size() << " systems. Skipping.\n"; - // return (*this); - // } - // - // // if dimensions a not zero and do not match, skip move with error - // message. if (dim_[0] + dim_[1] + dim_[2]) { - // bool does_match = (dim_[0] == that.at(0).n_u()) && - // (dim_[1] == that.at(0).n_x()) && - // (dim_[2] == that.at(0).n_y()); - // if (!does_match) { - // std::cerr - // << "Cannot move a UniformSystemList element for an element of " - // "different size. Skipping.\n"; - // return (*this); - // } - // } - // } - - dim_ = that.dim_; - std::vector<System>::operator=(std::move(that)); - - return (*this); -} - -template <typename System> -UniformSystemList<System>::UniformSystemList(const std::vector<System>& systems, - std::array<size_t, 3> dim) - : std::vector<System>(systems) { - CheckDimensions(dim); -} - -template <typename System> -UniformSystemList<System>::UniformSystemList(std::vector<System>&& systems, - std::array<size_t, 3> dim) - : std::vector<System>(std::move(systems)) { - CheckDimensions(dim); -}; - -template <typename System> -UniformSystemList<System>::UniformSystemList( - std::initializer_list<System> systems, std::array<size_t, 3> dim) - : std::vector<System>(systems) { - CheckDimensions(dim); -}; - -template <typename System> -UniformSystemList<System>::UniformSystemList(const UniformSystemList& that) - : std::vector<System>(that) { - (*this) = that; -} - -template <typename System> -UniformSystemList<System>::UniformSystemList(UniformSystemList&& that) noexcept - : std::vector<System>(std::move(that)) { - this->dim_[0] = this->at(0).n_u(); - this->dim_[1] = this->at(0).n_x(); - this->dim_[2] = this->at(0).n_y(); -} - -template <typename System> -void UniformSystemList<System>::CheckDimensions(std::array<size_t, 3> dim) { - if (dim[0] + dim[1] + dim[2]) { - dim_ = dim; - } else { - dim_[0] = this->at(0).n_u(); - dim_[1] = this->at(0).n_x(); - dim_[2] = this->at(0).n_y(); - } - - // make sure dimensiolaties are all uniform - bool does_match(true); - for (const System& sys : *this) { - does_match = does_match && (sys.n_u() == dim_[0]); - does_match = does_match && (sys.n_x() == dim_[1]); - does_match = does_match && (sys.n_y() == dim_[2]); - if (!does_match) { - throw std::runtime_error( - "Dimensionality of one or more input systems are not uniform."); - } - } -} - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_uniform_systems.h +summary: List of uniformly sized Systems. + +--- + +# ldsCtrlEst_h/lds_uniform_systems.h + +List of uniformly sized Systems. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/)** | + +## Detailed Description + + + +This file provides a container for uniformly sized Systems. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// Limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H +#define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H + +#include <array> // std::array +#include <vector> // std::vector + +// namespace +#include "lds.h" +// System type +#include "lds_sys.h" + +namespace lds { +template <typename System> +class UniformSystemList : public std::vector<System> { + static_assert(std::is_base_of<lds::System, System>::value, + "System must be derived from lds::System type."); + + private: + // TODO(mfbolus): would rather *uncomment* the below for sake of conversion + // using std::vector<System>::vector; + using std::vector<System>::operator=; + using std::vector<System>::operator[]; + using std::vector<System>::at; + using std::vector<System>::begin; + using std::vector<System>::end; + using std::vector<System>::size; + + public: + UniformSystemList() = default; + + explicit UniformSystemList(const std::vector<System>& systems, + std::array<size_t, 3> dim = {0, 0, 0}); + + explicit UniformSystemList(std::vector<System>&& systems, + std::array<size_t, 3> dim = {0, 0, 0}); + + UniformSystemList(std::initializer_list<System> systems, + std::array<size_t, 3> dim = {0, 0, 0}); + + UniformSystemList(const UniformSystemList& that); + UniformSystemList(UniformSystemList&& that) noexcept; + + ~UniformSystemList() = default; + + const std::array<size_t, 3>& dim() const { return dim_; } + + size_t size() { return std::vector<System>::size(); }; + + const System& at(size_t n) { return std::vector<System>::at(n); }; + + void Swap(System& that, size_t n); + + UniformSystemList& operator=(const UniformSystemList& that); + UniformSystemList& operator=(UniformSystemList&& that) noexcept; + + private: + void CheckDimensions(std::array<size_t, 3> dim); + std::array<size_t, 3> dim_{}; +}; + +template <typename System> +inline void UniformSystemList<System>::Swap(System& that, size_t n) { + // make sure request in range + if (n >= this->size()) { + std::cerr + << "Requested UniformSystemList element out of bounds. Skipping.\n"; + return; + } + // check dim + bool does_match = (dim_[0] == that.n_u()) && (dim_[1] == that.n_x()) && + (dim_[2] == that.n_y()); + if (!does_match) { + std::cerr << "Cannot swap a UniformSystemList element for an element of " + "different size. Skipping.\n"; + return; + } + // if checks pass, perform swap + System tmp = std::move((*this)[n]); + (*this)[n] = std::move(that); + that = std::move(tmp); +} + +template <typename System> +inline UniformSystemList<System>& UniformSystemList<System>::operator=( + const UniformSystemList& that) { + // check dimensions + if (!this->empty()) { + if (this->size() != that.size()) { + std::ostringstream ss; + ss << "cannot reassign " << this->size() << " systems with " + << that.size() << " systems"; + throw std::runtime_error(ss.str()); + } + + if (dim_[0] + dim_[1] + dim_[2]) { + std::array<size_t, 3> other_dim(that.dim()); + if (dim_ != other_dim) { + std::ostringstream ss; + ss << "cannot reassign systems of size " << dim_[0] << "x" << dim_[1] + << "x" << dim_[2] << " with systems of size " << other_dim[0] << "x" + << other_dim[1] << "x" << dim_[2]; + throw std::runtime_error(ss.str()); + } + } + } + + for (size_t k = 0; k < this->size(); k++) { + (*this)[k] = that[k]; + } + return (*this); +} + +template <typename System> +inline UniformSystemList<System>& UniformSystemList<System>::operator=( + UniformSystemList&& that) noexcept { + // // check dimensions + // // if empty, assume a default constructed object and safe to move + // if (!this->empty()) { + // if (this->size() != that.size()) { + // std::cerr << "Cannot reassign " << this->size() << " systems with " + // << that.size() << " systems. Skipping.\n"; + // return (*this); + // } + // + // // if dimensions a not zero and do not match, skip move with error + // message. if (dim_[0] + dim_[1] + dim_[2]) { + // bool does_match = (dim_[0] == that.at(0).n_u()) && + // (dim_[1] == that.at(0).n_x()) && + // (dim_[2] == that.at(0).n_y()); + // if (!does_match) { + // std::cerr + // << "Cannot move a UniformSystemList element for an element of " + // "different size. Skipping.\n"; + // return (*this); + // } + // } + // } + + dim_ = that.dim_; + std::vector<System>::operator=(std::move(that)); + + return (*this); +} + +template <typename System> +UniformSystemList<System>::UniformSystemList(const std::vector<System>& systems, + std::array<size_t, 3> dim) + : std::vector<System>(systems) { + CheckDimensions(dim); +} + +template <typename System> +UniformSystemList<System>::UniformSystemList(std::vector<System>&& systems, + std::array<size_t, 3> dim) + : std::vector<System>(std::move(systems)) { + CheckDimensions(dim); +}; + +template <typename System> +UniformSystemList<System>::UniformSystemList( + std::initializer_list<System> systems, std::array<size_t, 3> dim) + : std::vector<System>(systems) { + CheckDimensions(dim); +}; + +template <typename System> +UniformSystemList<System>::UniformSystemList(const UniformSystemList& that) + : std::vector<System>(that) { + (*this) = that; +} + +template <typename System> +UniformSystemList<System>::UniformSystemList(UniformSystemList&& that) noexcept + : std::vector<System>(std::move(that)) { + this->dim_[0] = this->at(0).n_u(); + this->dim_[1] = this->at(0).n_x(); + this->dim_[2] = this->at(0).n_y(); +} + +template <typename System> +void UniformSystemList<System>::CheckDimensions(std::array<size_t, 3> dim) { + if (dim[0] + dim[1] + dim[2]) { + dim_ = dim; + } else { + dim_[0] = this->at(0).n_u(); + dim_[1] = this->at(0).n_x(); + dim_[2] = this->at(0).n_y(); + } + + // make sure dimensiolaties are all uniform + bool does_match(true); + for (const System& sys : *this) { + does_match = does_match && (sys.n_u() == dim_[0]); + does_match = does_match && (sys.n_x() == dim_[1]); + does_match = does_match && (sys.n_y() == dim_[2]); + if (!does_match) { + throw std::runtime_error( + "Dimensionality of one or more input systems are not uniform."); + } + } +} + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md index 1d6a78af..2e6e9187 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md @@ -1,107 +1,107 @@ ---- -title: src/lds_uniform_vecs.cpp -summary: Uniformly sized vectors. - ---- - -# src/lds_uniform_vecs.cpp - -Uniformly sized vectors. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Detailed Description - - - -This file provides a container for uniformly sized vectors. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// Limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#include <ldsCtrlEst_h/lds_uniform_vecs.h> - -namespace lds { - -UniformVectorList::UniformVectorList(const std::vector<Vector>& vecs, - size_t dim) - : vector(vecs) { - CheckDimensions(dim); -} - -UniformVectorList::UniformVectorList(std::vector<Vector>&& vecs, size_t dim) - : vector(std::move(vecs)) { - CheckDimensions(dim); -}; - -UniformVectorList::UniformVectorList(std::initializer_list<Vector> vecs, - size_t dim) - : vector(vecs) { - CheckDimensions(dim); -}; - -UniformVectorList::UniformVectorList(const UniformVectorList& that) - : vector(that) { - (*this) = that; -} - -UniformVectorList::UniformVectorList(UniformVectorList&& that) noexcept - : vector(std::move(that)) { - this->dim_ = this->at(0).n_elem; -} - -void UniformVectorList::CheckDimensions(size_t dim) { - if (dim) { - dim_ = dim; - } else { - dim_ = this->at(0).n_elem; - } - - // make sure dimensiolaties are all uniform - bool does_match(true); - for (const Vector& vec : *this) { - does_match = does_match && (vec.n_elem == dim_); - if (!does_match) { - throw std::runtime_error( - "Dimensionality of one or more input matrices are not uniform."); - } - } -} - -} // namespace lds -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: src/lds_uniform_vecs.cpp +summary: Uniformly sized vectors. + +--- + +# src/lds_uniform_vecs.cpp + +Uniformly sized vectors. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Detailed Description + + + +This file provides a container for uniformly sized vectors. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// Limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include <ldsCtrlEst_h/lds_uniform_vecs.h> + +namespace lds { + +UniformVectorList::UniformVectorList(const std::vector<Vector>& vecs, + size_t dim) + : vector(vecs) { + CheckDimensions(dim); +} + +UniformVectorList::UniformVectorList(std::vector<Vector>&& vecs, size_t dim) + : vector(std::move(vecs)) { + CheckDimensions(dim); +}; + +UniformVectorList::UniformVectorList(std::initializer_list<Vector> vecs, + size_t dim) + : vector(vecs) { + CheckDimensions(dim); +}; + +UniformVectorList::UniformVectorList(const UniformVectorList& that) + : vector(that) { + (*this) = that; +} + +UniformVectorList::UniformVectorList(UniformVectorList&& that) noexcept + : vector(std::move(that)) { + this->dim_ = this->at(0).n_elem; +} + +void UniformVectorList::CheckDimensions(size_t dim) { + if (dim) { + dim_ = dim; + } else { + dim_ = this->at(0).n_elem; + } + + // make sure dimensiolaties are all uniform + bool does_match(true); + for (const Vector& vec : *this) { + does_match = does_match && (vec.n_elem == dim_); + if (!does_match) { + throw std::runtime_error( + "Dimensionality of one or more input matrices are not uniform."); + } + } +} + +} // namespace lds +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md index 620ae01e..338d1489 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md @@ -1,192 +1,192 @@ ---- -title: ldsCtrlEst_h/lds_uniform_vecs.h -summary: List of uniformly sized vectors. - ---- - -# ldsCtrlEst_h/lds_uniform_vecs.h - -List of uniformly sized vectors. [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/)** | - -## Detailed Description - - - -This file provides a container for uniformly sized vectors. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// Limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H -#define LDSCTRLEST_LDS_UNIFORM_VECS_H - -#include <array> // std::array -#include <vector> // std::vector - -#include "lds.h" - -namespace lds { - -class UniformVectorList : public std::vector<Vector> { - private: - // TODO(mfbolus): would rather *uncomment* the below for sake of conversion - // using std::vector<Vector>::vector; - using std::vector<Vector>::operator=; - using std::vector<Vector>::operator[]; - using std::vector<Vector>::at; - using std::vector<Vector>::begin; - using std::vector<Vector>::end; - using std::vector<Vector>::size; - - public: - UniformVectorList() = default; - - explicit UniformVectorList(const std::vector<Vector>& vecs, size_t dim = 0); - - explicit UniformVectorList(std::vector<Vector>&& vecs, size_t dim = 0); - - UniformVectorList(std::initializer_list<Vector> vecs, size_t dim = 0); - - UniformVectorList(const UniformVectorList& that); - UniformVectorList(UniformVectorList&& that) noexcept; - ~UniformVectorList() = default; - - size_t dim() const { return dim_; } - - size_t size() { return std::vector<Vector>::size(); }; - - const Vector& at(size_t n) { return std::vector<Vector>::at(n); }; - - void Swap(Vector& that, size_t n); - - UniformVectorList& operator=(const UniformVectorList& that); - UniformVectorList& operator=(UniformVectorList&& that) noexcept; - - private: - void CheckDimensions(size_t dim); - size_t dim_{}; -}; - -inline void UniformVectorList::Swap(Vector& that, size_t n) { - // make sure request in range - if (n >= this->size()) { - std::cerr - << "Requested UniformMatrixList element out of bounds. Skipping.\n"; - return; - } - // check dim - bool does_match = dim_ == that.n_elem; - if (!does_match) { - std::cerr << "Cannot swap a UniformMatrixList element for an element of " - "different size. Skipping.\n"; - return; - } - // if checks pass, perform swap - Vector tmp = std::move((*this)[n]); - (*this)[n] = std::move(that); - that = std::move(tmp); -} - -inline UniformVectorList& UniformVectorList::operator=( - const UniformVectorList& that) { - // check dimensions - if (!this->empty()) { - if (this->size() != that.size()) { - std::ostringstream ss; - ss << "cannot reassign " << this->size() << " vectors with " - << that.size() << " vectors"; - throw std::runtime_error(ss.str()); - } - - if (dim_) { - size_t other_dim(that.dim()); - if (dim_ != other_dim) { - std::ostringstream ss; - ss << "cannot reassign vectors of size " << dim_ - << " with vectors of size " << other_dim; - throw std::runtime_error(ss.str()); - } - } - } - - for (size_t k = 0; k < this->size(); k++) { - (*this)[k] = that[k]; - } - - return (*this); -} - -inline UniformVectorList& UniformVectorList::operator=( - UniformVectorList&& that) noexcept { - // // check dimensions - // if (!this->empty()) { - // if (this->size() != that.size()) { - // std::cerr << "Cannot reassign " << this->size() << " vectors with " - // << that.size() << " vectors. Skipping.\n"; - // return (*this); - // } - // - // if (dim_) { - // size_t other_dim(that.dim()); - // if (dim_ != other_dim) { - // std::cerr << "Cannot reassign vectors of size " << dim_ - // << " with matrices of size " << other_dim << ". - // Skipping.\n"; - // return (*this); - // } - // } - // } - - dim_ = that.dim_; - std::vector<Vector>::operator=(std::move(that)); - - return (*this); -} - -} // namespace lds - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/lds_uniform_vecs.h +summary: List of uniformly sized vectors. + +--- + +# ldsCtrlEst_h/lds_uniform_vecs.h + +List of uniformly sized vectors. [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[lds](/lds-ctrl-est/docs/api/namespaces/namespacelds/)** <br>Linear Dynamical Systems (LDS) namespace. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/)** | + +## Detailed Description + + + +This file provides a container for uniformly sized vectors. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// Limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H +#define LDSCTRLEST_LDS_UNIFORM_VECS_H + +#include <array> // std::array +#include <vector> // std::vector + +#include "lds.h" + +namespace lds { + +class UniformVectorList : public std::vector<Vector> { + private: + // TODO(mfbolus): would rather *uncomment* the below for sake of conversion + // using std::vector<Vector>::vector; + using std::vector<Vector>::operator=; + using std::vector<Vector>::operator[]; + using std::vector<Vector>::at; + using std::vector<Vector>::begin; + using std::vector<Vector>::end; + using std::vector<Vector>::size; + + public: + UniformVectorList() = default; + + explicit UniformVectorList(const std::vector<Vector>& vecs, size_t dim = 0); + + explicit UniformVectorList(std::vector<Vector>&& vecs, size_t dim = 0); + + UniformVectorList(std::initializer_list<Vector> vecs, size_t dim = 0); + + UniformVectorList(const UniformVectorList& that); + UniformVectorList(UniformVectorList&& that) noexcept; + ~UniformVectorList() = default; + + size_t dim() const { return dim_; } + + size_t size() { return std::vector<Vector>::size(); }; + + const Vector& at(size_t n) { return std::vector<Vector>::at(n); }; + + void Swap(Vector& that, size_t n); + + UniformVectorList& operator=(const UniformVectorList& that); + UniformVectorList& operator=(UniformVectorList&& that) noexcept; + + private: + void CheckDimensions(size_t dim); + size_t dim_{}; +}; + +inline void UniformVectorList::Swap(Vector& that, size_t n) { + // make sure request in range + if (n >= this->size()) { + std::cerr + << "Requested UniformMatrixList element out of bounds. Skipping.\n"; + return; + } + // check dim + bool does_match = dim_ == that.n_elem; + if (!does_match) { + std::cerr << "Cannot swap a UniformMatrixList element for an element of " + "different size. Skipping.\n"; + return; + } + // if checks pass, perform swap + Vector tmp = std::move((*this)[n]); + (*this)[n] = std::move(that); + that = std::move(tmp); +} + +inline UniformVectorList& UniformVectorList::operator=( + const UniformVectorList& that) { + // check dimensions + if (!this->empty()) { + if (this->size() != that.size()) { + std::ostringstream ss; + ss << "cannot reassign " << this->size() << " vectors with " + << that.size() << " vectors"; + throw std::runtime_error(ss.str()); + } + + if (dim_) { + size_t other_dim(that.dim()); + if (dim_ != other_dim) { + std::ostringstream ss; + ss << "cannot reassign vectors of size " << dim_ + << " with vectors of size " << other_dim; + throw std::runtime_error(ss.str()); + } + } + } + + for (size_t k = 0; k < this->size(); k++) { + (*this)[k] = that[k]; + } + + return (*this); +} + +inline UniformVectorList& UniformVectorList::operator=( + UniformVectorList&& that) noexcept { + // // check dimensions + // if (!this->empty()) { + // if (this->size() != that.size()) { + // std::cerr << "Cannot reassign " << this->size() << " vectors with " + // << that.size() << " vectors. Skipping.\n"; + // return (*this); + // } + // + // if (dim_) { + // size_t other_dim(that.dim()); + // if (dim_ != other_dim) { + // std::cerr << "Cannot reassign vectors of size " << dim_ + // << " with matrices of size " << other_dim << ". + // Skipping.\n"; + // return (*this); + // } + // } + // } + + dim_ = that.dim_; + std::vector<Vector>::operator=(std::move(that)); + + return (*this); +} + +} // namespace lds + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md b/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md index c64256a1..e6da187b 100644 --- a/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md @@ -1,132 +1,132 @@ ---- -title: ldsCtrlEst_h/mex_c_util.h -summary: arma <-> mex interoperability utilities (Matlab C API) - ---- - -# ldsCtrlEst_h/mex_c_util.h - -arma <-> mex interoperability utilities (Matlab C API) [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[armamexc](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/)** <br>arma/mex interface using Matlab C API | - -## Detailed Description - - - -This file defines utility functions for interoperability between armadillo and Matlab/Octave's C mex API. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_MEXC_UTIL_H -#define LDSCTRLEST_MEXC_UTIL_H - -#include <ldsCtrlEst> - -#include "mex.h" - -// // If Matlab_FOUND, include matrix.h. -// // (Octave does not need/have it.) -// #ifdef Matlab_FOUND -// #include "matrix.h" -// #endif - -namespace armamexc { -template <class T> -inline auto m2T_scalar(const mxArray *matlab_scalar) -> T { - if (mxGetData(matlab_scalar)) { - return static_cast<T>(mxGetScalar(matlab_scalar)); - } - mexErrMsgTxt("No data available."); - return 0; -} - -template <class T> -inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, - bool strict = true) -> arma::Mat<T> { - if (mxGetData(matlab_mat)) { - const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); - if (n_dim == 2) { - return arma::Mat<T>(static_cast<T *>(mxGetData(matlab_mat)), - mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, - strict); - } - mexErrMsgTxt("Number of dimensions must be 2."); - return arma::Mat<T>(); - } - mexErrMsgTxt("No data available."); - return arma::Mat<T>(); -} - -// TODO(mfbolus): make these templated. - -template <typename T> -inline auto a2m_mat(arma::Mat<T> const &arma_mat) -> mxArray * { - mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, - mxDOUBLE_CLASS, mxREAL); - if (matlab_mat) { - auto *dst_pointer = static_cast<T *>(mxGetData(matlab_mat)); - const auto *src_pointer = const_cast<T *>(arma_mat.memptr()); - // TODO(mfbolus): I just want to MOVE the data, not copy. - std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); - return matlab_mat; - } - mexErrMsgTxt("Failed to create matlab mat from arma::Mat."); - return nullptr; -} - -template <typename T> -inline auto a2m_vec(arma::Col<T> const &arma_vec) -> mxArray * { - mxArray *matlab_mat = - mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); - if (matlab_mat) { - auto *dst_pointer = static_cast<T *>(mxGetData(matlab_mat)); - const auto *src_pointer = const_cast<T *>(arma_vec.memptr()); - // TODO(mfbolus): I just want to MOVE the data, not copy. - std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); - return matlab_mat; - } - mexErrMsgTxt("Failed to create matlab mat from arma::Col."); - return nullptr; -} - -} // namespace armamexc - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/mex_c_util.h +summary: arma <-> mex interoperability utilities (Matlab C API) + +--- + +# ldsCtrlEst_h/mex_c_util.h + +arma <-> mex interoperability utilities (Matlab C API) [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[armamexc](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/)** <br>arma/mex interface using Matlab C API | + +## Detailed Description + + + +This file defines utility functions for interoperability between armadillo and Matlab/Octave's C mex API. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_MEXC_UTIL_H +#define LDSCTRLEST_MEXC_UTIL_H + +#include <ldsCtrlEst> + +#include "mex.h" + +// // If Matlab_FOUND, include matrix.h. +// // (Octave does not need/have it.) +// #ifdef Matlab_FOUND +// #include "matrix.h" +// #endif + +namespace armamexc { +template <class T> +inline auto m2T_scalar(const mxArray *matlab_scalar) -> T { + if (mxGetData(matlab_scalar)) { + return static_cast<T>(mxGetScalar(matlab_scalar)); + } + mexErrMsgTxt("No data available."); + return 0; +} + +template <class T> +inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, + bool strict = true) -> arma::Mat<T> { + if (mxGetData(matlab_mat)) { + const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); + if (n_dim == 2) { + return arma::Mat<T>(static_cast<T *>(mxGetData(matlab_mat)), + mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, + strict); + } + mexErrMsgTxt("Number of dimensions must be 2."); + return arma::Mat<T>(); + } + mexErrMsgTxt("No data available."); + return arma::Mat<T>(); +} + +// TODO(mfbolus): make these templated. + +template <typename T> +inline auto a2m_mat(arma::Mat<T> const &arma_mat) -> mxArray * { + mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, + mxDOUBLE_CLASS, mxREAL); + if (matlab_mat) { + auto *dst_pointer = static_cast<T *>(mxGetData(matlab_mat)); + const auto *src_pointer = const_cast<T *>(arma_mat.memptr()); + // TODO(mfbolus): I just want to MOVE the data, not copy. + std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); + return matlab_mat; + } + mexErrMsgTxt("Failed to create matlab mat from arma::Mat."); + return nullptr; +} + +template <typename T> +inline auto a2m_vec(arma::Col<T> const &arma_vec) -> mxArray * { + mxArray *matlab_mat = + mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); + if (matlab_mat) { + auto *dst_pointer = static_cast<T *>(mxGetData(matlab_mat)); + const auto *src_pointer = const_cast<T *>(arma_vec.memptr()); + // TODO(mfbolus): I just want to MOVE the data, not copy. + std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); + return matlab_mat; + } + mexErrMsgTxt("Failed to create matlab mat from arma::Col."); + return nullptr; +} + +} // namespace armamexc + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md b/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md index b8dec155..efc0b13c 100644 --- a/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md @@ -1,153 +1,153 @@ ---- -title: ldsCtrlEst_h/mex_cpp_util.h -summary: arma <-> mex interoperability utilities (Matlab C++ API) - ---- - -# ldsCtrlEst_h/mex_cpp_util.h - -arma <-> mex interoperability utilities (Matlab C++ API) [More...](#detailed-description) - - - -## Namespaces - -| Name | -| -------------- | -| **[armamexcpp](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/)** <br>arma/mex interface using Matlab C++ API | - -## Detailed Description - - - -This file defines utility functions for interoperability between armadillo and Matlab's C++ mex API. - - - - - -## Source code - -```cpp -//===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// -// -// Copyright 2021 Michael Bolus -// Copyright 2021 Georgia Institute of Technology -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -//===----------------------------------------------------------------------===// -//===----------------------------------------------------------------------===// - -#ifndef LDSCTRLEST_MEXCPP_UTIL_H -#define LDSCTRLEST_MEXCPP_UTIL_H - -#include <ldsCtrlEst> - -#include "mex.hpp" -#include "mexAdapter.hpp" - -namespace armamexcpp { -template <class T> -std::vector<arma::Mat<T>> m2a_cellmat(matlab::data::CellArray& matlab_cell) { - size_t n_cells = matlab_cell.getNumberOfElements(); - std::vector<arma::Mat<T>> arma_mat(n_cells, - arma::Mat<T>(1, 1, arma::fill::zeros)); - for (size_t k = 0; k < n_cells; k++) { - matlab::data::TypedArray<T> matlab_mat = matlab_cell[k]; - auto dims = matlab_mat.getDimensions(); - arma_mat[k] = arma::Mat<T>(matlab_mat.release().get(), dims[0], dims[1]); - } - return arma_mat; -}; - -template <class T> -std::vector<T> m2s_vec(matlab::data::TypedArray<T>& matlab_array) { - size_t n_elem = matlab_array.getNumberOfElements(); - T* ptr = matlab_array.release().get(); - std::vector<T> vec(ptr, ptr + n_elem); - return vec; -}; - -template <class T> -arma::Col<T> m2a_vec(matlab::data::TypedArray<T> matlab_array) { - size_t n_elem = matlab_array.getNumberOfElements(); - // T* ptr = matlab_array.release().get(); - // arma::Col<T> vec(ptr, n_elem); //, false); - // TODO(mfbolus): for some reason, using the above pointer at times leads to - // getting garbage values. matlab array values may be stored in non-contiguous - // memory? - arma::Col<T> vec(n_elem, arma::fill::zeros); - for (size_t k = 0; k < n_elem; k++) { - vec[k] = matlab_array[k]; - } - return vec; -}; - -template <class T> -arma::Mat<T> m2a_mat(matlab::data::TypedArray<T> matlab_array) { - // ArrayDimensions == std::vector<size_t> - auto dims = matlab_array.getDimensions(); - // T* ptr = matlab_array.release().get(); - // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) - // arma::Mat<T> mat(ptr, dims[0], dims[1]); //, false); - - // TODO(mfbolus): for some reason, using the above pointer at times leads to - // getting garbage values. matlab array values may be stored in non-contiguous - // memory? - // - // armadillo and matlab both use column-major ordering, so this should work: - size_t n_elem = dims[0] * dims[1]; - arma::Mat<T> mat(dims[0], dims[1], arma::fill::zeros); - size_t k(0); - for (auto m: matlab_array) { - mat[k] = m; - k++; - } - return mat; -}; - -template <class T> -matlab::data::TypedArray<T> a2m_mat(const arma::Mat<T>& arma_mat, - matlab::data::ArrayFactory& factory) { - const matlab::data::TypedArray<T> matlab_mat = factory.createArray<T>( - {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), - arma_mat.memptr() + arma_mat.n_elem); - return matlab_mat; -}; - -template <class T> -matlab::data::TypedArray<T> a2m_vec(const arma::Col<T>& arma_vec, - matlab::data::ArrayFactory& factory) { - const matlab::data::TypedArray<T> matlab_mat = - factory.createArray<T>({arma_vec.n_elem, 1}, arma_vec.memptr(), - arma_vec.memptr() + arma_vec.n_elem); - return matlab_mat; -}; - -template <class T> -matlab::data::TypedArray<T> s2m_vec(const std::vector<T>& std_vec, - matlab::data::ArrayFactory& factory) { - const matlab::data::TypedArray<T> matlab_mat = factory.createArray<T>( - {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); - return matlab_mat; -}; -} // namespace armamexcpp - -#endif -``` - - -------------------------------- - -Updated on 19 May 2022 at 17:16:05 Eastern Daylight Time +--- +title: ldsCtrlEst_h/mex_cpp_util.h +summary: arma <-> mex interoperability utilities (Matlab C++ API) + +--- + +# ldsCtrlEst_h/mex_cpp_util.h + +arma <-> mex interoperability utilities (Matlab C++ API) [More...](#detailed-description) + + + +## Namespaces + +| Name | +| -------------- | +| **[armamexcpp](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/)** <br>arma/mex interface using Matlab C++ API | + +## Detailed Description + + + +This file defines utility functions for interoperability between armadillo and Matlab's C++ mex API. + + + + + +## Source code + +```cpp +//===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#ifndef LDSCTRLEST_MEXCPP_UTIL_H +#define LDSCTRLEST_MEXCPP_UTIL_H + +#include <ldsCtrlEst> + +#include "mex.hpp" +#include "mexAdapter.hpp" + +namespace armamexcpp { +template <class T> +std::vector<arma::Mat<T>> m2a_cellmat(matlab::data::CellArray& matlab_cell) { + size_t n_cells = matlab_cell.getNumberOfElements(); + std::vector<arma::Mat<T>> arma_mat(n_cells, + arma::Mat<T>(1, 1, arma::fill::zeros)); + for (size_t k = 0; k < n_cells; k++) { + matlab::data::TypedArray<T> matlab_mat = matlab_cell[k]; + auto dims = matlab_mat.getDimensions(); + arma_mat[k] = arma::Mat<T>(matlab_mat.release().get(), dims[0], dims[1]); + } + return arma_mat; +}; + +template <class T> +std::vector<T> m2s_vec(matlab::data::TypedArray<T>& matlab_array) { + size_t n_elem = matlab_array.getNumberOfElements(); + T* ptr = matlab_array.release().get(); + std::vector<T> vec(ptr, ptr + n_elem); + return vec; +}; + +template <class T> +arma::Col<T> m2a_vec(matlab::data::TypedArray<T> matlab_array) { + size_t n_elem = matlab_array.getNumberOfElements(); + // T* ptr = matlab_array.release().get(); + // arma::Col<T> vec(ptr, n_elem); //, false); + // TODO(mfbolus): for some reason, using the above pointer at times leads to + // getting garbage values. matlab array values may be stored in non-contiguous + // memory? + arma::Col<T> vec(n_elem, arma::fill::zeros); + for (size_t k = 0; k < n_elem; k++) { + vec[k] = matlab_array[k]; + } + return vec; +}; + +template <class T> +arma::Mat<T> m2a_mat(matlab::data::TypedArray<T> matlab_array) { + // ArrayDimensions == std::vector<size_t> + auto dims = matlab_array.getDimensions(); + // T* ptr = matlab_array.release().get(); + // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) + // arma::Mat<T> mat(ptr, dims[0], dims[1]); //, false); + + // TODO(mfbolus): for some reason, using the above pointer at times leads to + // getting garbage values. matlab array values may be stored in non-contiguous + // memory? + // + // armadillo and matlab both use column-major ordering, so this should work: + size_t n_elem = dims[0] * dims[1]; + arma::Mat<T> mat(dims[0], dims[1], arma::fill::zeros); + size_t k(0); + for (auto m: matlab_array) { + mat[k] = m; + k++; + } + return mat; +}; + +template <class T> +matlab::data::TypedArray<T> a2m_mat(const arma::Mat<T>& arma_mat, + matlab::data::ArrayFactory& factory) { + const matlab::data::TypedArray<T> matlab_mat = factory.createArray<T>( + {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), + arma_mat.memptr() + arma_mat.n_elem); + return matlab_mat; +}; + +template <class T> +matlab::data::TypedArray<T> a2m_vec(const arma::Col<T>& arma_vec, + matlab::data::ArrayFactory& factory) { + const matlab::data::TypedArray<T> matlab_mat = + factory.createArray<T>({arma_vec.n_elem, 1}, arma_vec.memptr(), + arma_vec.memptr() + arma_vec.n_elem); + return matlab_mat; +}; + +template <class T> +matlab::data::TypedArray<T> s2m_vec(const std::vector<T>& std_vec, + matlab::data::ArrayFactory& factory) { + const matlab::data::TypedArray<T> matlab_mat = factory.createArray<T>( + {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); + return matlab_mat; +}; +} // namespace armamexcpp + +#endif +``` + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Modules/_index.md b/misc/docs-hugo/content/docs/api/Modules/_index.md index a4bc6ccf..01cdf812 100644 --- a/misc/docs-hugo/content/docs/api/Modules/_index.md +++ b/misc/docs-hugo/content/docs/api/Modules/_index.md @@ -1,28 +1,28 @@ ---- -title: Modules - ---- - -# Modules - - - - - - - -- **[Control Mode Bit Masks](/lds-ctrl-est/docs/api/modules/group__control__masks/)** <br>provides fill types for constructing new armadillo vectors, matrices - - - - - -- **[Defaults](/lds-ctrl-est/docs/api/modules/group__defaults/)** - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time +--- +title: Modules + +--- + +# Modules + + + + + + + +- **[Control Mode Bit Masks](/lds-ctrl-est/docs/api/modules/group__control__masks/)** <br>provides fill types for constructing new armadillo vectors, matrices + + + + + +- **[Defaults](/lds-ctrl-est/docs/api/modules/group__defaults/)** + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md b/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md index 972b05de..a846662f 100644 --- a/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md +++ b/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md @@ -1,70 +1,70 @@ ---- -title: Control Mode Bit Masks -summary: provides fill types for constructing new armadillo vectors, matrices - ---- - -# Control Mode Bit Masks - -provides fill types for constructing new armadillo vectors, matrices <br> <br>[More...](#detailed-description) -<br> - - -## Attributes - -| | Name | -| -------------- | -------------- | -| const std::size_t | **[kControlTypeDeltaU](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypedeltau)** <br>control designed to penalize change in input | -| const std::size_t | **[kControlTypeIntY](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeinty)** <br>control using integral action | -| const std::size_t | **[kControlTypeAdaptM](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeadaptm)** <br>adapt control setpoint with re-estimated disturbance `m` | - -## Detailed Description - - - -Control mode bit masks. These can be bit-wise OR'd to use in combination. - - - -## Attribute Details - -### kControlTypeDeltaU - -```cpp -static const std::size_t kControlTypeDeltaU = 0x1; -``` - - - -Control was designed to penalize change in input (i.e., the state was augmented with input `u`) - - -### kControlTypeIntY - -```cpp -static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1; -``` - - - -Control using integral action (i.e., the state was augmented with output `y` during design) - - -### kControlTypeAdaptM - -```cpp -static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2; -``` - - - -Adapt control setpoint adapted with re-estimated process disturbance `m`. - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: Control Mode Bit Masks +summary: provides fill types for constructing new armadillo vectors, matrices + +--- + +# Control Mode Bit Masks + +provides fill types for constructing new armadillo vectors, matrices <br> <br>[More...](#detailed-description) +<br> + + +## Attributes + +| | Name | +| -------------- | -------------- | +| const std::size_t | **[kControlTypeDeltaU](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypedeltau)** <br>control designed to penalize change in input | +| const std::size_t | **[kControlTypeIntY](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeinty)** <br>control using integral action | +| const std::size_t | **[kControlTypeAdaptM](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeadaptm)** <br>adapt control setpoint with re-estimated disturbance `m` | + +## Detailed Description + + + +Control mode bit masks. These can be bit-wise OR'd to use in combination. + + + +## Attribute Details + +### kControlTypeDeltaU + +```cpp +static const std::size_t kControlTypeDeltaU = 0x1; +``` + + + +Control was designed to penalize change in input (i.e., the state was augmented with input `u`) + + +### kControlTypeIntY + +```cpp +static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1; +``` + + + +Control using integral action (i.e., the state was augmented with output `y` during design) + + +### kControlTypeAdaptM + +```cpp +static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2; +``` + + + +Adapt control setpoint adapted with re-estimated process disturbance `m`. + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Modules/group__defaults.md b/misc/docs-hugo/content/docs/api/Modules/group__defaults.md index 4f744568..f72c3c16 100644 --- a/misc/docs-hugo/content/docs/api/Modules/group__defaults.md +++ b/misc/docs-hugo/content/docs/api/Modules/group__defaults.md @@ -1,60 +1,60 @@ ---- -title: Defaults - ---- - -# Defaults - -<br> <br>[More...](#detailed-description) -<br> - - -## Attributes - -| | Name | -| -------------- | -------------- | -| const data_t | **[kDefaultP0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultp0)** <br>default state estimate covar | -| const data_t | **[kDefaultQ0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultq0)** <br>default process noise covar | -| const data_t | **[kDefaultR0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultr0)** <br>default output noise covar | - -## Detailed Description - - - -Default values for common variables (e.g., default diagonal elements of covariances) - - - -## Attribute Details - -### kDefaultP0 - -```cpp -static const data_t kDefaultP0 = 1e-6; -``` - - - -### kDefaultQ0 - -```cpp -static const data_t kDefaultQ0 = 1e-6; -``` - - - -### kDefaultR0 - -```cpp -static const data_t kDefaultR0 = 1e-2; -``` - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: Defaults + +--- + +# Defaults + +<br> <br>[More...](#detailed-description) +<br> + + +## Attributes + +| | Name | +| -------------- | -------------- | +| const data_t | **[kDefaultP0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultp0)** <br>default state estimate covar | +| const data_t | **[kDefaultQ0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultq0)** <br>default process noise covar | +| const data_t | **[kDefaultR0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultr0)** <br>default output noise covar | + +## Detailed Description + + + +Default values for common variables (e.g., default diagonal elements of covariances) + + + +## Attribute Details + +### kDefaultP0 + +```cpp +static const data_t kDefaultP0 = 1e-6; +``` + + + +### kDefaultQ0 + +```cpp +static const data_t kDefaultQ0 = 1e-6; +``` + + + +### kDefaultR0 + +```cpp +static const data_t kDefaultR0 = 1e-2; +``` + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Namespaces/_index.md b/misc/docs-hugo/content/docs/api/Namespaces/_index.md index 0ff4e7f6..41d6e411 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/_index.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/_index.md @@ -1,38 +1,42 @@ ---- -title: Namespaces - ---- - -# Namespaces - - - - - - -- **[armamexc](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/)** <br>arma/mex interface using Matlab C API - - - -- **[armamexcpp](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/)** <br>arma/mex interface using Matlab C++ API - - - - - - -- **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. - - - -- **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time +--- +title: Namespaces + +--- + +# Namespaces + + + + + + +- **[armamexc](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/)** <br>arma/mex interface using Matlab C API + + + +- **[armamexcpp](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/)** <br>arma/mex interface using Matlab C++ API + + + + + + +- **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. + + + +- **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. + + + + + + +- **[std](/lds-ctrl-est/docs/api/namespaces/namespacestd/)** + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md index fb69bd3e..924e1250 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md @@ -1,124 +1,124 @@ ---- -title: armamexc -summary: arma/mex interface using Matlab C API - ---- - -# armamexc - -arma/mex interface using Matlab C API <br> <br>[More...](#detailed-description) -<br> - - -## Functions - -| | Name | -| -------------- | -------------- | -| template <class T \> <br>auto | **[m2T_scalar](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-m2t-scalar)**(const mxArray * matlab_scalar)<br>Convert Matlab mxArray to scalar of type T. | -| template <class T \> <br>auto | **[m2a_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-m2a-mat)**(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)<br>Convert matlab matrix to armadillo. | -| template <typename T \> <br>auto | **[a2m_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-a2m-mat)**(arma::Mat< T > const & arma_mat)<br>Convert armadillo to matlab matrix. | -| template <typename T \> <br>auto | **[a2m_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-a2m-vec)**(arma::Col< T > const & arma_vec)<br>Convert armadillo to matlab vector. | - -## Detailed Description - - - -Utilities for arma/mex interface _using Matlab C API_ - - -## Function Details - -### m2T_scalar - -```cpp -template <class T > -inline auto m2T_scalar( - const mxArray * matlab_scalar -) -``` - - - -**Parameters**: - - * **matlab_scalar** matlab scalar - - -**Template Parameters**: - - * **T** type - - -**Return**: scalar of type T - -### m2a_mat - -```cpp -template <class T > -inline auto m2a_mat( - const mxArray * matlab_mat, - bool copy_aux_mem =false, - bool strict =true -) -``` - - - -**Parameters**: - - * **matlab_mat** matlab matrix - * **copy_aux_mem** [optional] whether to copy auxiliary memory - * **strict** [optional] strictly enforce the above - - -**Template Parameters**: - - * **T** type - - -**Return**: armadillo matrix of type T - -### a2m_mat - -```cpp -template <typename T > -inline auto a2m_mat( - arma::Mat< T > const & arma_mat -) -``` - - - -**Parameters**: - - * **arma_mat** armadillo matrix - - -**Return**: matlab matrix - -### a2m_vec - -```cpp -template <typename T > -inline auto a2m_vec( - arma::Col< T > const & arma_vec -) -``` - - - -**Parameters**: - - * **arma_vec** armadillo vector - - -**Return**: matlab vector - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time +--- +title: armamexc +summary: arma/mex interface using Matlab C API + +--- + +# armamexc + +arma/mex interface using Matlab C API <br> <br>[More...](#detailed-description) +<br> + + +## Functions + +| | Name | +| -------------- | -------------- | +| template <class T \> <br>T | **[m2T_scalar](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-m2t-scalar)**(const mxArray * matlab_scalar)<br>Convert Matlab mxArray to scalar of type T. | +| template <class T \> <br>arma::Mat< T > | **[m2a_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-m2a-mat)**(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)<br>Convert matlab matrix to armadillo. | +| template <typename T \> <br>mxArray * | **[a2m_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-a2m-mat)**(arma::Mat< T > const & arma_mat)<br>Convert armadillo to matlab matrix. | +| template <typename T \> <br>mxArray * | **[a2m_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-a2m-vec)**(arma::Col< T > const & arma_vec)<br>Convert armadillo to matlab vector. | + +## Detailed Description + + + +Utilities for arma/mex interface _using Matlab C API_ + + +## Function Details + +### m2T_scalar + +```cpp +template <class T > +inline T m2T_scalar( + const mxArray * matlab_scalar +) +``` + + + +**Parameters**: + + * **matlab_scalar** matlab scalar + + +**Template Parameters**: + + * **T** type + + +**Return**: scalar of type T + +### m2a_mat + +```cpp +template <class T > +inline arma::Mat< T > m2a_mat( + const mxArray * matlab_mat, + bool copy_aux_mem =false, + bool strict =true +) +``` + + + +**Parameters**: + + * **matlab_mat** matlab matrix + * **copy_aux_mem** [optional] whether to copy auxiliary memory + * **strict** [optional] strictly enforce the above + + +**Template Parameters**: + + * **T** type + + +**Return**: armadillo matrix of type T + +### a2m_mat + +```cpp +template <typename T > +inline mxArray * a2m_mat( + arma::Mat< T > const & arma_mat +) +``` + + + +**Parameters**: + + * **arma_mat** armadillo matrix + + +**Return**: matlab matrix + +### a2m_vec + +```cpp +template <typename T > +inline mxArray * a2m_vec( + arma::Col< T > const & arma_vec +) +``` + + + +**Parameters**: + + * **arma_vec** armadillo vector + + +**Return**: matlab vector + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:26 EST diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md index e160ecf4..777df93f 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md @@ -1,208 +1,208 @@ ---- -title: armamexcpp -summary: arma/mex interface using Matlab C++ API - ---- - -# armamexcpp - -arma/mex interface using Matlab C++ API <br> <br>[More...](#detailed-description) -<br> - - -## Functions - -| | Name | -| -------------- | -------------- | -| template <class T \> <br>std::vector< arma::Mat< T > > | **[m2a_cellmat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-cellmat)**(matlab::data::CellArray & matlab_cell)<br>Convert matlab cell array to vector of armadillo matrices. | -| template <class T \> <br>std::vector< T > | **[m2s_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2s-vec)**(matlab::data::TypedArray< T > & matlab_array)<br>Convert matlab matrix to a vector of scalars. | -| template <class T \> <br>arma::Col< T > | **[m2a_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-vec)**(matlab::data::TypedArray< T > matlab_array)<br>Convert matlab to armadillo vector. | -| template <class T \> <br>arma::Mat< T > | **[m2a_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-mat)**(matlab::data::TypedArray< T > matlab_array)<br>Convert matlab to armadillo matrix. | -| template <class T \> <br>matlab::data::TypedArray< T > | **[a2m_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-a2m-mat)**(const arma::Mat< T > & arma_mat, matlab::data::ArrayFactory & factory)<br>Convert armadillo to matlab matrix. | -| template <class T \> <br>matlab::data::TypedArray< T > | **[a2m_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-a2m-vec)**(const arma::Col< T > & arma_vec, matlab::data::ArrayFactory & factory)<br>Convert armadillo to matlab vector. | -| template <class T \> <br>matlab::data::TypedArray< T > | **[s2m_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-s2m-vec)**(const std::vector< T > & std_vec, matlab::data::ArrayFactory & factory)<br>Convert vector of scalar T to matlab matrix. | - -## Detailed Description - - - -utilities for arma/mex interface _using Matlab C++ API_ - - -## Function Details - -### m2a_cellmat - -```cpp -template <class T > -std::vector< arma::Mat< T > > m2a_cellmat( - matlab::data::CellArray & matlab_cell -) -``` - - - -**Parameters**: - - * **matlab_cell** matlab cell - - -**Template Parameters**: - - * **T** type - - -**Return**: vector of armadillo matrices of type T - -### m2s_vec - -```cpp -template <class T > -std::vector< T > m2s_vec( - matlab::data::TypedArray< T > & matlab_array -) -``` - - - -**Parameters**: - - * **matlab_array** matlab array - - -**Template Parameters**: - - * **T** type - - -**Return**: vector of type T - -### m2a_vec - -```cpp -template <class T > -arma::Col< T > m2a_vec( - matlab::data::TypedArray< T > matlab_array -) -``` - - - -**Parameters**: - - * **matlab_array** matlab array - - -**Template Parameters**: - - * **T** type - - -**Return**: armadillo vector of type T - -### m2a_mat - -```cpp -template <class T > -arma::Mat< T > m2a_mat( - matlab::data::TypedArray< T > matlab_array -) -``` - - - -**Parameters**: - - * **matlab_array** matlab matrix - - -**Template Parameters**: - - * **T** type - - -**Return**: armadillo matrix of type T - -### a2m_mat - -```cpp -template <class T > -matlab::data::TypedArray< T > a2m_mat( - const arma::Mat< T > & arma_mat, - matlab::data::ArrayFactory & factory -) -``` - - - -**Parameters**: - - * **arma_mat** arma matrix - * **factory** matlab "array factory" - - -**Template Parameters**: - - * **T** type - - -**Return**: matlab matrix - -### a2m_vec - -```cpp -template <class T > -matlab::data::TypedArray< T > a2m_vec( - const arma::Col< T > & arma_vec, - matlab::data::ArrayFactory & factory -) -``` - - - -**Parameters**: - - * **arma_vec** armadillo vector - * **factory** matlab "array factory" - - -**Template Parameters**: - - * **T** type - - -**Return**: matlab matrix - -### s2m_vec - -```cpp -template <class T > -matlab::data::TypedArray< T > s2m_vec( - const std::vector< T > & std_vec, - matlab::data::ArrayFactory & factory -) -``` - - - -**Parameters**: - - * **std_vec** standard vector - * **factory** matlab "array factory" - - -**Template Parameters**: - - * **T** type - - -**Return**: matlab matrix - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time +--- +title: armamexcpp +summary: arma/mex interface using Matlab C++ API + +--- + +# armamexcpp + +arma/mex interface using Matlab C++ API <br> <br>[More...](#detailed-description) +<br> + + +## Functions + +| | Name | +| -------------- | -------------- | +| template <class T \> <br>std::vector< arma::Mat< T > > | **[m2a_cellmat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-cellmat)**(matlab::data::CellArray & matlab_cell)<br>Convert matlab cell array to vector of armadillo matrices. | +| template <class T \> <br>std::vector< T > | **[m2s_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2s-vec)**(matlab::data::TypedArray< T > & matlab_array)<br>Convert matlab matrix to a vector of scalars. | +| template <class T \> <br>arma::Col< T > | **[m2a_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-vec)**(matlab::data::TypedArray< T > matlab_array)<br>Convert matlab to armadillo vector. | +| template <class T \> <br>arma::Mat< T > | **[m2a_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-mat)**(matlab::data::TypedArray< T > matlab_array)<br>Convert matlab to armadillo matrix. | +| template <class T \> <br>matlab::data::TypedArray< T > | **[a2m_mat](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-a2m-mat)**(const arma::Mat< T > & arma_mat, matlab::data::ArrayFactory & factory)<br>Convert armadillo to matlab matrix. | +| template <class T \> <br>matlab::data::TypedArray< T > | **[a2m_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-a2m-vec)**(const arma::Col< T > & arma_vec, matlab::data::ArrayFactory & factory)<br>Convert armadillo to matlab vector. | +| template <class T \> <br>matlab::data::TypedArray< T > | **[s2m_vec](/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-s2m-vec)**(const std::vector< T > & std_vec, matlab::data::ArrayFactory & factory)<br>Convert vector of scalar T to matlab matrix. | + +## Detailed Description + + + +utilities for arma/mex interface _using Matlab C++ API_ + + +## Function Details + +### m2a_cellmat + +```cpp +template <class T > +std::vector< arma::Mat< T > > m2a_cellmat( + matlab::data::CellArray & matlab_cell +) +``` + + + +**Parameters**: + + * **matlab_cell** matlab cell + + +**Template Parameters**: + + * **T** type + + +**Return**: vector of armadillo matrices of type T + +### m2s_vec + +```cpp +template <class T > +std::vector< T > m2s_vec( + matlab::data::TypedArray< T > & matlab_array +) +``` + + + +**Parameters**: + + * **matlab_array** matlab array + + +**Template Parameters**: + + * **T** type + + +**Return**: vector of type T + +### m2a_vec + +```cpp +template <class T > +arma::Col< T > m2a_vec( + matlab::data::TypedArray< T > matlab_array +) +``` + + + +**Parameters**: + + * **matlab_array** matlab array + + +**Template Parameters**: + + * **T** type + + +**Return**: armadillo vector of type T + +### m2a_mat + +```cpp +template <class T > +arma::Mat< T > m2a_mat( + matlab::data::TypedArray< T > matlab_array +) +``` + + + +**Parameters**: + + * **matlab_array** matlab matrix + + +**Template Parameters**: + + * **T** type + + +**Return**: armadillo matrix of type T + +### a2m_mat + +```cpp +template <class T > +matlab::data::TypedArray< T > a2m_mat( + const arma::Mat< T > & arma_mat, + matlab::data::ArrayFactory & factory +) +``` + + + +**Parameters**: + + * **arma_mat** arma matrix + * **factory** matlab "array factory" + + +**Template Parameters**: + + * **T** type + + +**Return**: matlab matrix + +### a2m_vec + +```cpp +template <class T > +matlab::data::TypedArray< T > a2m_vec( + const arma::Col< T > & arma_vec, + matlab::data::ArrayFactory & factory +) +``` + + + +**Parameters**: + + * **arma_vec** armadillo vector + * **factory** matlab "array factory" + + +**Template Parameters**: + + * **T** type + + +**Return**: matlab matrix + +### s2m_vec + +```cpp +template <class T > +matlab::data::TypedArray< T > s2m_vec( + const std::vector< T > & std_vec, + matlab::data::ArrayFactory & factory +) +``` + + + +**Parameters**: + + * **std_vec** standard vector + * **factory** matlab "array factory" + + +**Template Parameters**: + + * **T** type + + +**Return**: matlab matrix + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:26 EST diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md index f10de3ef..221f358a 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md @@ -1,376 +1,313 @@ ---- -title: lds -summary: Linear Dynamical Systems (LDS) namespace. - ---- - -# lds - -Linear Dynamical Systems (LDS) namespace. <br> - -## Namespaces - -| Name | -| -------------- | -| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | -| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1controller/)** | -| class | **[lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1em/)** | -| class | **[lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1fit/)** <br>LDS [Fit]() Type. | -| class | **[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)** | -| class | **[lds::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1switchedcontroller/)** <br>[SwitchedController]() Type. | -| class | **[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1system/)** <br>Linear Dynamical [System]() Type. | -| class | **[lds::UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformmatrixlist/)** | -| class | **[lds::UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformsystemlist/)** | -| class | **[lds::UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1uniformvectorlist/)** | - -## Types - -| | Name | -| -------------- | -------------- | -| enum| **[SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt)** { kSSIDNone, kSSIDMOESP, kSSIDCVA}<br>weighting options for [SSID]() | -| enum| **[MatrixListFreeDim](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-matrixlistfreedim)** { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} | -| using double | **[data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t)** | -| using arma::Col< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[Vector](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-vector)** | -| using arma::Mat< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[Matrix](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-matrix)** | -| using arma::Cube< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[Cube](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-cube)** | -| using arma::subview< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[View](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-view)** | - -## Functions - -| | Name | -| -------------- | -------------- | -| void | **[Limit](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit)**(std::vector< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > & x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) ub) | -| void | **[Limit](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit)**(Vector & x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) ub) | -| void | **[Limit](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit)**(Matrix & x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) ub) | -| void | **[Reassign](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-reassign)**(Vector & some, const Vector & other, const std::string & parenthetical ="Reassign")<br>reassigns contents of some Vector in place | -| void | **[Reassign](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-reassign)**(Matrix & some, const Matrix & other, const std::string & parenthetical ="Reassign")<br>reassigns contents of some Matrix in place | -| void | **[ForceSymPD](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-forcesympd)**(Matrix & X)<br>forces matrix to be symmetric positive-definite | -| void | **[ForceSymMinEig](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-forcesymmineig)**(Matrix & X, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) eig_min =0)<br>forces matrix to be symmetric and have a minimum eigenvalue | -| void | **[lq](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-lq)**(Matrix & L, Matrix & Qt, const Matrix & X)<br>LQ decomposition. | -| Matrix | **[calcCov](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-calccov)**(const Matrix & A, const Matrix & B)<br>Calculate covariance matrix. | - -## Attributes - -| | Name | -| -------------- | -------------- | -| const std::size_t | **[kControlTypeDeltaU](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypedeltau)** <br>control designed to penalize change in input | -| const std::size_t | **[kControlTypeIntY](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeinty)** <br>control using integral action | -| const std::size_t | **[kControlTypeAdaptM](/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeadaptm)** <br>adapt control setpoint with re-estimated disturbance `m` | -| const [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[kInf](/lds-ctrl-est/docs/api/namespaces/namespacelds/#variable-kinf)** <br>Some useful numbers. | -| const [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[kPi](/lds-ctrl-est/docs/api/namespaces/namespacelds/#variable-kpi)** | -| const [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[kDefaultP0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultp0)** <br>default state estimate covar | -| const [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[kDefaultQ0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultq0)** <br>default process noise covar | -| const [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[kDefaultR0](/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultr0)** <br>default output noise covar | - -## Type Details - -### SSIDWt - -| Enumerator | Value | Description | -| ---------- | ----- | ----------- | -| kSSIDNone | | None. | -| kSSIDMOESP | | MOESP (AKA "robust method" in van Overschee 1996) | -| kSSIDCVA | | CVA "Canonical Variate Analysis". | - - - - - -Weighting options for singular value decomposition performed during subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) - -Reference: - -van Overschee, de Moor. 1996. Subspace Identification for Linear Systems. - - -### MatrixListFreeDim - -| Enumerator | Value | Description | -| ---------- | ----- | ----------- | -| kMatFreeDimNone | | neither dim free to be hetero in mat list | -| kMatFreeDim1 | | allow 1st dim of mats in list to be hetero | -| kMatFreeDim2 | | allow 2nd dim of mats in list to be hetero | - - - - - -### data_t - -```cpp -using lds::data_t = typedef double; -``` - - - -Type of all data in library. If need 32b, change `double` to `float`. This could be potentially useful for large scale problems where there are memory constraints. - - -### Vector - -```cpp -using lds::Vector = typedef arma::Col<data_t>; -``` - - - -### Matrix - -```cpp -using lds::Matrix = typedef arma::Mat<data_t>; -``` - - - -### Cube - -```cpp -using lds::Cube = typedef arma::Cube<data_t>; -``` - - - -### View - -```cpp -using lds::View = typedef arma::subview<data_t>; -``` - - - - -## Function Details - -### Limit - -```cpp -inline void Limit( - std::vector< data_t > & x, - data_t lb, - data_t ub -) -``` - - - -### Limit - -```cpp -inline void Limit( - Vector & x, - data_t lb, - data_t ub -) -``` - - - -### Limit - -```cpp -inline void Limit( - Matrix & x, - data_t lb, - data_t ub -) -``` - - - -### Reassign - -```cpp -inline void Reassign( - Vector & some, - const Vector & other, - const std::string & parenthetical ="Reassign" -) -``` - - - -**Parameters**: - - * **some** some Vector - * **other** other Vector - * **parenthetical** optional description provided by caller to ease debugging - - -### Reassign - -```cpp -inline void Reassign( - Matrix & some, - const Matrix & other, - const std::string & parenthetical ="Reassign" -) -``` - - - -**Parameters**: - - * **some** some Matrix - * **other** other Matrix - * **parenthetical** optional description provided by caller to ease debugging - - -### ForceSymPD - -```cpp -void ForceSymPD( - Matrix & X -) -``` - - - -**Parameters**: - - * **X** mutated matrix - - -### ForceSymMinEig - -```cpp -void ForceSymMinEig( - Matrix & X, - data_t eig_min =0 -) -``` - - - -**Parameters**: - - * **X** mutated matrix - * **eig_min** [optional] minimum eigen value - - -### lq - -```cpp -void lq( - Matrix & L, - Matrix & Qt, - const Matrix & X -) -``` - - - -**Parameters**: - - * **L** lower triangle matrix - * **Qt** orthonormal matrix (transposed cf QR decomp) - * **X** matrix being decomposed - - -### calcCov - -```cpp -Matrix calcCov( - const Matrix & A, - const Matrix & B -) -``` - - - -**Parameters**: - - * **A** some matrix - * **B** some other matrix - - -**Return**: covariance - - -## Attribute Details - -### kControlTypeDeltaU - -```cpp -static const std::size_t kControlTypeDeltaU = 0x1; -``` - - - -Control was designed to penalize change in input (i.e., the state was augmented with input `u`) - - -### kControlTypeIntY - -```cpp -static const std::size_t kControlTypeIntY = kControlTypeDeltaU << 1; -``` - - - -Control using integral action (i.e., the state was augmented with output `y` during design) - - -### kControlTypeAdaptM - -```cpp -static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU << 2; -``` - - - -Adapt control setpoint adapted with re-estimated process disturbance `m`. - - -### kInf - -```cpp -static const data_t kInf = std::numeric_limits<data_t>::infinity(); -``` - - - -### kPi - -```cpp -static const data_t kPi = arma::datum::pi; -``` - - - -### kDefaultP0 - -```cpp -static const data_t kDefaultP0 = 1e-6; -``` - - - -### kDefaultQ0 - -```cpp -static const data_t kDefaultQ0 = 1e-6; -``` - - - -### kDefaultR0 - -```cpp -static const data_t kDefaultR0 = 1e-2; -``` - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:03 Eastern Daylight Time +--- +title: lds +summary: Linear Dynamical Systems (LDS) namespace. + +--- + +# lds + +Linear Dynamical Systems (LDS) namespace. <br> + +## Namespaces + +| Name | +| -------------- | +| **[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)** <br>Linear Dynamical Systems with Gaussian observations. | +| **[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)** <br>Linear Dynamical Systems with Poisson observations. | + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/)** | +| class | **[lds::EM](/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/)** | +| class | **[lds::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/)** <br>LDS [Fit]() Type. | +| class | **[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)** | +| class | **[lds::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/)** <br>[SwitchedController]() Type. | +| class | **[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)** <br>Linear Dynamical [System]() Type. | +| class | **[lds::UniformMatrixList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/)** | +| class | **[lds::UniformSystemList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/)** | +| class | **[lds::UniformVectorList](/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/)** | + +## Types + +| | Name | +| -------------- | -------------- | +| enum| **[SSIDWt](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt)** { kSSIDNone, kSSIDMOESP, kSSIDCVA}<br>weighting options for [SSID]() | +| enum| **[MatrixListFreeDim](/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-matrixlistfreedim)** { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} | +| using double | **[data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t)** | +| using arma::Col< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[Vector](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-vector)** | +| using arma::Mat< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[Matrix](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-matrix)** | +| using arma::Cube< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[Cube](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-cube)** | +| using arma::subview< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > | **[View](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-view)** | + +## Functions + +| | Name | +| -------------- | -------------- | +| void | **[Limit](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit)**(std::vector< [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) > & x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) ub) | +| void | **[Limit](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit)**(Vector & x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) ub) | +| void | **[Limit](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit)**(Matrix & x, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) lb, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) ub) | +| void | **[Reassign](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-reassign)**(Vector & some, const Vector & other, const std::string & parenthetical ="Reassign")<br>reassigns contents of some Vector in place | +| void | **[Reassign](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-reassign)**(Matrix & some, const Matrix & other, const std::string & parenthetical ="Reassign")<br>reassigns contents of some Matrix in place | +| void | **[ForceSymPD](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-forcesympd)**(Matrix & X)<br>forces matrix to be symmetric positive-definite | +| void | **[ForceSymMinEig](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-forcesymmineig)**(Matrix & X, [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) eig_min =0)<br>forces matrix to be symmetric and have a minimum eigenvalue | +| void | **[lq](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-lq)**(Matrix & L, Matrix & Qt, const Matrix & X)<br>LQ decomposition. | +| Matrix | **[calcCov](/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-calccov)**(const Matrix & A, const Matrix & B)<br>Calculate covariance matrix. | + +## Attributes + +| | Name | +| -------------- | -------------- | +| const [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[kInf](/lds-ctrl-est/docs/api/namespaces/namespacelds/#variable-kinf)** <br>Some useful numbers. | +| const [data_t](/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t) | **[kPi](/lds-ctrl-est/docs/api/namespaces/namespacelds/#variable-kpi)** | + +## Type Details + +### SSIDWt + +| Enumerator | Value | Description | +| ---------- | ----- | ----------- | +| kSSIDNone | | None. | +| kSSIDMOESP | | MOESP (AKA "robust method" in van Overschee 1996) | +| kSSIDCVA | | CVA "Canonical Variate Analysis". | + + + + + +Weighting options for singular value decomposition performed during subspace identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) + +Reference: + +van Overschee, de Moor. 1996. Subspace Identification for Linear Systems. + + +### MatrixListFreeDim + +| Enumerator | Value | Description | +| ---------- | ----- | ----------- | +| kMatFreeDimNone | | neither dim free to be hetero in mat list | +| kMatFreeDim1 | | allow 1st dim of mats in list to be hetero | +| kMatFreeDim2 | | allow 2nd dim of mats in list to be hetero | + + + + + +### data_t + +```cpp +using lds::data_t = double; +``` + + + +Type of all data in library. If need 32b, change `double` to `float`. This could be potentially useful for large scale problems where there are memory constraints. + + +### Vector + +```cpp +using lds::Vector = arma::Col<data_t>; +``` + + + +### Matrix + +```cpp +using lds::Matrix = arma::Mat<data_t>; +``` + + + +### Cube + +```cpp +using lds::Cube = arma::Cube<data_t>; +``` + + + +### View + +```cpp +using lds::View = arma::subview<data_t>; +``` + + + + +## Function Details + +### Limit + +```cpp +inline void Limit( + std::vector< data_t > & x, + data_t lb, + data_t ub +) +``` + + + +### Limit + +```cpp +inline void Limit( + Vector & x, + data_t lb, + data_t ub +) +``` + + + +### Limit + +```cpp +inline void Limit( + Matrix & x, + data_t lb, + data_t ub +) +``` + + + +### Reassign + +```cpp +inline void Reassign( + Vector & some, + const Vector & other, + const std::string & parenthetical ="Reassign" +) +``` + + + +**Parameters**: + + * **some** some Vector + * **other** other Vector + * **parenthetical** optional description provided by caller to ease debugging + + +### Reassign + +```cpp +inline void Reassign( + Matrix & some, + const Matrix & other, + const std::string & parenthetical ="Reassign" +) +``` + + + +**Parameters**: + + * **some** some Matrix + * **other** other Matrix + * **parenthetical** optional description provided by caller to ease debugging + + +### ForceSymPD + +```cpp +void ForceSymPD( + Matrix & X +) +``` + + + +**Parameters**: + + * **X** mutated matrix + + +### ForceSymMinEig + +```cpp +void ForceSymMinEig( + Matrix & X, + data_t eig_min =0 +) +``` + + + +**Parameters**: + + * **X** mutated matrix + * **eig_min** [optional] minimum eigen value + + +### lq + +```cpp +void lq( + Matrix & L, + Matrix & Qt, + const Matrix & X +) +``` + + + +**Parameters**: + + * **L** lower triangle matrix + * **Qt** orthonormal matrix (transposed cf QR decomp) + * **X** matrix being decomposed + + +### calcCov + +```cpp +Matrix calcCov( + const Matrix & A, + const Matrix & B +) +``` + + + +**Parameters**: + + * **A** some matrix + * **B** some other matrix + + +**Return**: covariance + + +## Attribute Details + +### kInf + +```cpp +static const data_t kInf = std::numeric_limits<data_t>::infinity(); +``` + + + +### kPi + +```cpp +static const data_t kPi = arma::datum::pi; +``` + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:26 EST diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md index 7f4734fa..e4dfbbe7 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md @@ -1,30 +1,30 @@ ---- -title: lds::gaussian -summary: Linear Dynamical Systems with Gaussian observations. - ---- - -# lds::gaussian - -Linear Dynamical Systems with Gaussian observations. <br> - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1controller/)** <br>Gaussian-observation [Controller]() Type. | -| class | **[lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/)** <br>GLDS [Fit]() Type. | -| class | **[lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitem/)** <br>GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fit/) Type. | -| class | **[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1fitssid/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for GLDS. | -| class | **[lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1switchedcontroller/)** <br>Gaussian-observation [SwitchedController]() Type. | -| class | **[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1system/)** <br>Gaussian LDS Type. | - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: lds::gaussian +summary: Linear Dynamical Systems with Gaussian observations. + +--- + +# lds::gaussian + +Linear Dynamical Systems with Gaussian observations. <br> + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::gaussian::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/)** <br>Gaussian-observation [Controller]() Type. | +| class | **[lds::gaussian::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/)** <br>GLDS [Fit]() Type. | +| class | **[lds::gaussian::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/)** <br>GLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/) Type. | +| class | **[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for GLDS. | +| class | **[lds::gaussian::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/)** <br>Gaussian-observation [SwitchedController]() Type. | +| class | **[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)** <br>Gaussian LDS Type. | + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md index d3ee26e8..7620c8d7 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md @@ -1,56 +1,56 @@ ---- -title: lds::poisson -summary: Linear Dynamical Systems with Poisson observations. - ---- - -# lds::poisson - -Linear Dynamical Systems with Poisson observations. <br> - -## Classes - -| | Name | -| -------------- | -------------- | -| class | **[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1controller/)** <br>PLDS [Controller]() Type. | -| class | **[lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/)** <br>PLDS [Fit]() Type. | -| class | **[lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitem/)** <br>PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fit/) Type. | -| class | **[lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1fitssid/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1ssid/)) for PLDS. | -| class | **[lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1switchedcontroller/)** <br>Poisson-observation [SwitchedController]() Type. | -| class | **[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1system/)** <br>Poisson [System]() type. | - -## Attributes - -| | Name | -| -------------- | -------------- | -| std::random_device | **[rd](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/#variable-rd)** <br>random device for simulating poisson data | -| std::mt19937 | **[rng](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/#variable-rng)** <br>random number generator for simulating poisson data | - - - -## Attribute Details - -### rd - -```cpp -static std::random_device rd; -``` - - - -### rng - -```cpp -static std::mt19937 rng = std::mt19937( - rd()); -``` - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:04 Eastern Daylight Time +--- +title: lds::poisson +summary: Linear Dynamical Systems with Poisson observations. + +--- + +# lds::poisson + +Linear Dynamical Systems with Poisson observations. <br> + +## Classes + +| | Name | +| -------------- | -------------- | +| class | **[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)** <br>PLDS [Controller]() Type. | +| class | **[lds::poisson::Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/)** <br>PLDS [Fit]() Type. | +| class | **[lds::poisson::FitEM](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/)** <br>PLDS E-M [Fit](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/) Type. | +| class | **[lds::poisson::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/)** <br>Subspace Identification ([SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)) for PLDS. | +| class | **[lds::poisson::SwitchedController](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/)** <br>Poisson-observation [SwitchedController]() Type. | +| class | **[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)** <br>Poisson [System]() type. | + +## Attributes + +| | Name | +| -------------- | -------------- | +| std::random_device | **[rd](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/#variable-rd)** <br>random device for simulating poisson data | +| std::mt19937 | **[rng](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/#variable-rng)** <br>random number generator for simulating poisson data | + + + +## Attribute Details + +### rd + +```cpp +static std::random_device rd; +``` + + + +### rng + +```cpp +static std::mt19937 rng = std::mt19937( + rd()); +``` + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md new file mode 100644 index 00000000..e7e779a1 --- /dev/null +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md @@ -0,0 +1,18 @@ +--- +title: std + +--- + +# std + +<br> + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/api/Pages/_index.md b/misc/docs-hugo/content/docs/api/Pages/_index.md index 75a00e31..0c91eafb 100644 --- a/misc/docs-hugo/content/docs/api/Pages/_index.md +++ b/misc/docs-hugo/content/docs/api/Pages/_index.md @@ -1,16 +1,16 @@ ---- -title: Pages - ---- - -# Pages - - - - - - - -------------------------------- - -Updated on 19 May 2022 at 17:16:06 Eastern Daylight Time +--- +title: Pages + +--- + +# Pages + + + + + + + +------------------------------- + +Updated on 5 March 2025 at 21:41:27 EST diff --git a/misc/docs-hugo/content/docs/getting-started/getting-started.md b/misc/docs-hugo/content/docs/getting-started/getting-started.md index c68271df..fe624740 100644 --- a/misc/docs-hugo/content/docs/getting-started/getting-started.md +++ b/misc/docs-hugo/content/docs/getting-started/getting-started.md @@ -26,16 +26,16 @@ Homebrew is "The Missing Package Manager for macOS" which will make installing l /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" ``` -You can then use it to install CMake and gfortran: +You can then use it to install CMake, gfortran, and pkg-config: ```shell -brew install cmake gfortran +brew install cmake gfortran pkg-config ``` ## Linux Pre-requisites You'll need Git, CMake, GCC, gfortran, etc. ```shell -sudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential +sudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build ``` ## Windows Installation diff --git a/misc/docs-hugo/content/docs/terminology/control-estimation.md b/misc/docs-hugo/content/docs/terminology/control-estimation.md index 6f951be9..56a9b8eb 100644 --- a/misc/docs-hugo/content/docs/terminology/control-estimation.md +++ b/misc/docs-hugo/content/docs/terminology/control-estimation.md @@ -63,3 +63,8 @@ Integral action and the {{<katex>}} \Delta \mathbf{u} {{</katex>}} control law c ## Calculating reference state-control from output In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce {{<katex>}} \mathbf{x}^{\rm ref} {{</katex>}} and {{<katex>}} \mathbf{u}^{\rm ref} {{</katex>}}. Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (`lds::Controller<System>::ControlOutputReference`). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs. +## Model Predictive Control +Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications. + +In the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time. + diff --git a/misc/docs-hugo/content/docs/terminology/model.md b/misc/docs-hugo/content/docs/terminology/model.md index fc5c7e2d..18d4d537 100644 --- a/misc/docs-hugo/content/docs/terminology/model.md +++ b/misc/docs-hugo/content/docs/terminology/model.md @@ -63,3 +63,9 @@ z_{t}^i \sim \rm{Poisson} \left(y_{t}^i \right) c : i^th row of output matrix (C) d : output bias + + +## Model Predictive Control (MPC) +Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications. + +In the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time. diff --git a/misc/docs-hugo/content/issues-contributing.md b/misc/docs-hugo/content/issues-contributing.md index 1930e065..3c0b4b2e 100644 --- a/misc/docs-hugo/content/issues-contributing.md +++ b/misc/docs-hugo/content/issues-contributing.md @@ -3,3 +3,12 @@ If you encounter bugs when using this library or have specific feature requests # Contributing We welcome any community contributions to this project. Please fork the repository and if possible use `clang-format` and `clang-tidy` to conform to the coding format/style of this repository. + +When editing any documentation/guides, please use the markdown docs in `misc/docs-hugo` instead of directly editing the HTML docs. +This may require having `hugo`, `graphviz` and `doxygen` installed through `brew`, as well as `doxybook2` installed through a git clone. + +Clone the `doxybook2` repository online and place the executable in your `usr/local/bin` folder or another `bin` folder. +Run `sudo chmod +x /usr/local/bin/doxybook2` to give doxybook2 permissions. Run `doxybook2 --help` to ensure that this works properly. If permission is still denied, navigate to System Preferences > Security & Privacy > General. +You should see a message at the bottom that says: "doxybook2 was blocked from use because it is not from an identified developer." Click Allow Anyway. + +Updated docs can be built by running `cd scripts` and `./update-docs.sh`. After a successful build, navigate to `/misc/docs-hugo/` and run `hugo server` to create a local host of the website. Access the website using local host to ensure the correct results. diff --git a/misc/docs-hugo/layouts/partials/docs/menu-filetree.html b/misc/docs-hugo/layouts/partials/docs/menu-filetree.html new file mode 100644 index 00000000..f8290df3 --- /dev/null +++ b/misc/docs-hugo/layouts/partials/docs/menu-filetree.html @@ -0,0 +1,45 @@ +{{ $bookSection := default "docs" .Site.Params.BookSection }} +{{ if eq $bookSection "*" }} + {{ $bookSection = "/" }}{{/* Backward compatibility */}} +{{ end }} + +{{ with .Site.GetPage $bookSection }} + {{ template "book-section-children" (dict "Section" . "CurrentPage" $) }} +{{ end }} + +{{ define "book-section-children" }}{{/* (dict "Section" .Section "CurrentPage" .CurrentPage) */}} + <ul> + {{ range (where .Section.Pages "Params.bookhidden" "ne" true) }} + {{ if .IsSection }} + <li {{- if .Params.BookFlatSection }} class="book-section-flat" {{ end -}}> + {{ template "book-page-link" (dict "Page" . "CurrentPage" $.CurrentPage) }} + {{ template "book-section-children" (dict "Section" . "CurrentPage" $.CurrentPage) }} + </li> + {{ else if and .IsPage .Content }} + <li> + {{ template "book-page-link" (dict "Page" . "CurrentPage" $.CurrentPage) }} + </li> + {{ end }} + {{ end }} + </ul> +{{ end }} + +{{ define "book-page-link" }}{{/* (dict "Page" .Page "CurrentPage" .CurrentPage) */}} + {{ $current := eq .CurrentPage .Page }} + {{ $ancestor := .Page.IsAncestor .CurrentPage }} + + {{ if .Page.Params.bookCollapseSection }} + <input type="checkbox" id="section-{{ md5 .Page }}" class="toggle" {{ if or $current $ancestor }}checked{{ end }} /> + <label for="section-{{ md5 .Page }}" class="flex justify-between"> + <a {{ if .Page.Content }}href="{{ .Page.Permalink }}"{{ else }}role="button"{{ end }} class="{{ if $current }}active{{ end }}"> + {{- partial "docs/title" .Page -}} + </a> + </label> + {{ else if .Page.Content }} + <a href="{{ .Page.Permalink }}" class="{{ if $current }} active{{ end }}"> + {{- partial "docs/title" .Page -}} + </a> + {{ else }} + <span>{{- partial "docs/title" .Page -}}</span> + {{ end }} +{{ end }} diff --git a/docs/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css b/misc/docs-hugo/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.content similarity index 100% rename from docs/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css rename to misc/docs-hugo/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.content diff --git a/misc/docs-hugo/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json b/misc/docs-hugo/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json new file mode 100644 index 00000000..1790550f --- /dev/null +++ b/misc/docs-hugo/resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json @@ -0,0 +1 @@ +{"Target":"book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css","MediaType":"text/css","Data":{"Integrity":"sha256-V/f2YIcVF6W/z7Xi3oU9gG9+NNlOvV8/O61i6d264gk="}} \ No newline at end of file diff --git a/scripts/convert-doxygen-to-md.sh b/scripts/convert-doxygen-to-md.sh index a45a9b97..88d3308a 100755 --- a/scripts/convert-doxygen-to-md.sh +++ b/scripts/convert-doxygen-to-md.sh @@ -1,5 +1,8 @@ #!/bin/sh +# Create directories if they don't exist +mkdir -p ../misc/docs-hugo/content/docs/api + # remove existing api docs rm -rf ../misc/docs-hugo/content/docs/api/* diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index 5c65947a..05d240d0 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -1,11 +1,11 @@ #!/bin/sh # convert doxygen xml to markdown for usage with hugo static site gen -./convert-doxygen-to-md.sh +./convert-doxygen-to-md.sh && cd .. # generate site -cd ../misc/docs-hugo && hugo && cd ../../scripts +cd misc/docs-hugo && hugo && cd ../.. # remove existing docs/ and add new in its place -rm -rf ../docs/* && cp -r ../misc/docs-hugo/public/* ../docs/ +rm -rf ../docs/* && cp -r misc/docs-hugo/public/* ../docs/ From 9f8e030ee979a124b1f7686ae084e0bc3fbe09ff Mon Sep 17 00:00:00 2001 From: Muhammad Yousuf <muhammad.y.2305@gmail.com> Date: Mon, 31 Mar 2025 16:05:41 -0400 Subject: [PATCH 3/4] Fixed docs folder --- scripts/update-docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index 05d240d0..b556044f 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -7,5 +7,5 @@ cd misc/docs-hugo && hugo && cd ../.. # remove existing docs/ and add new in its place -rm -rf ../docs/* && cp -r misc/docs-hugo/public/* ../docs/ +rm -rf docs/* && cp -r misc/docs-hugo/public/* docs/ From d5102f415754287c684913f72a7e6a4aca3181e5 Mon Sep 17 00:00:00 2001 From: Muhammad Yousuf <muhammad.y.2305@gmail.com> Date: Mon, 31 Mar 2025 16:07:10 -0400 Subject: [PATCH 4/4] Fixing the docs folder --- docs/404.html | 51 + docs/acknowledgements/index.html | 244 +++ ...853d806f7e34d94ebd5f3f3bad62e9ddbae209.css | 1 + docs/categories/index.html | 269 +++ docs/categories/index.xml | 11 + docs/categories/page/1/index.html | 10 + ...lasslds_1_1_controller__inherit__graph.png | Bin 0 -> 28819 bytes docs/classlds_1_1_system__inherit__graph.png | Bin 0 -> 7184 bytes .../classlds_1_1_controller/index.html | 1482 +++++++++++++++++ .../api/classes/classlds_1_1_e_m/index.html | 1229 ++++++++++++++ .../api/classes/classlds_1_1_fit/index.html | 907 ++++++++++ .../classes/classlds_1_1_s_s_i_d/index.html | 896 ++++++++++ .../index.html | 846 ++++++++++ .../classes/classlds_1_1_system/index.html | 1324 +++++++++++++++ .../index.html | 647 +++++++ .../index.html | 617 +++++++ .../index.html | 600 +++++++ .../index.html | 560 +++++++ .../classlds_1_1gaussian_1_1_fit/index.html | 553 ++++++ .../index.html | 465 ++++++ .../index.html | 392 +++++ .../index.html | 658 ++++++++ .../index.html | 855 ++++++++++ .../index.html | 560 +++++++ .../classlds_1_1poisson_1_1_fit/index.html | 553 ++++++ .../index.html | 465 ++++++ .../index.html | 392 +++++ .../index.html | 658 ++++++++ .../classlds_1_1poisson_1_1_system/index.html | 733 ++++++++ docs/docs/api/classes/index.html | 398 +++++ docs/docs/api/classes/index.xml | 158 ++ .../eg_glds_ctrl_8cpp-example/index.html | 468 ++++++ .../index.html | 471 ++++++ .../eg_plds_ctrl_8cpp-example/index.html | 444 +++++ .../eg_plds_est_8cpp-example/index.html | 393 +++++ .../index.html | 441 +++++ docs/docs/api/examples/index.html | 286 ++++ docs/docs/api/examples/index.xml | 46 + .../index.html | 437 +++++ .../index.html | 311 ++++ .../index.html | 311 ++++ .../index.html | 287 ++++ .../api/files/eg__glds__ctrl_8cpp/index.html | 679 ++++++++ .../eg__glds__du__plds__ctrl_8cpp/index.html | 691 ++++++++ .../api/files/eg__plds__ctrl_8cpp/index.html | 649 ++++++++ .../api/files/eg__plds__est_8cpp/index.html | 617 +++++++ .../eg__plds__switched__ctrl_8cpp/index.html | 880 ++++++++++ docs/docs/api/files/index.html | 438 +++++ docs/docs/api/files/index.xml | 291 ++++ docs/docs/api/files/lds_8cpp/index.html | 405 +++++ docs/docs/api/files/lds_8h/index.html | 427 +++++ docs/docs/api/files/lds__ctrl_8h/index.html | 808 +++++++++ docs/docs/api/files/lds__fit_8h/index.html | 408 +++++ .../docs/api/files/lds__fit__em_8h/index.html | 891 ++++++++++ .../api/files/lds__fit__ssid_8h/index.html | 837 ++++++++++ .../api/files/lds__gaussian_8h/index.html | 316 ++++ .../files/lds__gaussian__ctrl_8h/index.html | 375 +++++ .../files/lds__gaussian__fit_8h/index.html | 353 ++++ .../lds__gaussian__fit__em_8h/index.html | 349 ++++ .../lds__gaussian__fit__ssid_8h/index.html | 349 ++++ .../files/lds__gaussian__sctrl_8h/index.html | 373 +++++ .../files/lds__gaussian__sys_8cpp/index.html | 329 ++++ .../files/lds__gaussian__sys_8h/index.html | 387 +++++ .../docs/api/files/lds__poisson_8h/index.html | 320 ++++ .../files/lds__poisson__ctrl_8h/index.html | 380 +++++ .../api/files/lds__poisson__fit_8h/index.html | 361 ++++ .../files/lds__poisson__fit__em_8h/index.html | 354 ++++ .../lds__poisson__fit__ssid_8h/index.html | 348 ++++ .../files/lds__poisson__sctrl_8h/index.html | 376 +++++ .../files/lds__poisson__sys_8cpp/index.html | 325 ++++ .../api/files/lds__poisson__sys_8h/index.html | 367 ++++ docs/docs/api/files/lds__sctrl_8h/index.html | 532 ++++++ docs/docs/api/files/lds__sys_8cpp/index.html | 415 +++++ docs/docs/api/files/lds__sys_8h/index.html | 448 +++++ .../files/lds__uniform__mats_8h/index.html | 600 +++++++ .../files/lds__uniform__systems_8h/index.html | 525 ++++++ .../files/lds__uniform__vecs_8cpp/index.html | 350 ++++ .../files/lds__uniform__vecs_8h/index.html | 449 +++++ .../docs/api/files/mex__c__util_8h/index.html | 375 +++++ .../api/files/mex__cpp__util_8h/index.html | 396 +++++ docs/docs/api/index.html | 404 +++++ docs/docs/api/index.xml | 11 + .../modules/group__control__masks/index.html | 321 ++++ .../api/modules/group__defaults/index.html | 376 +++++ docs/docs/api/modules/index.html | 265 +++ docs/docs/api/modules/index.xml | 25 + docs/docs/api/namespaces/index.html | 286 ++++ docs/docs/api/namespaces/index.xml | 53 + .../namespaces/namespacearmamexc/index.html | 373 +++++ .../namespaces/namespacearmamexcpp/index.html | 452 +++++ .../api/namespaces/namespacelds/index.html | 723 ++++++++ .../namespacelds_1_1gaussian/index.html | 289 ++++ .../namespacelds_1_1poisson/index.html | 340 ++++ .../api/namespaces/namespacestd/index.html | 249 +++ docs/docs/api/pages/index.html | 247 +++ docs/docs/api/pages/index.xml | 11 + .../getting-started/index.html | 405 +++++ docs/docs/getting-started/windows/index.html | 320 ++++ docs/docs/index.html | 243 +++ docs/docs/index.xml | 39 + .../terminology/control-estimation/index.html | 368 ++++ docs/docs/terminology/model/index.html | 361 ++++ .../docs/tutorials/eg_glds_control/index.html | 439 +++++ .../eg_plds_state_estimation/index.html | 368 ++++ .../eg_switched_plds_control/index.html | 412 +++++ docs/docs/tutorials/index.html | 243 +++ docs/docs/tutorials/index.xml | 32 + docs/eg_glds_ctrl_output.png | Bin 0 -> 169071 bytes docs/eg_plds_ctrl_output.png | Bin 0 -> 217595 bytes docs/eg_plds_est_output.png | Bin 0 -> 148988 bytes docs/eg_plds_est_output_hist.png | Bin 0 -> 29070 bytes docs/eg_plds_switched_ctrl_output.png | Bin 0 -> 156078 bytes ...b10cc8cbb02c0e2b14346c8eca1b0148e9277.json | 1 + ...08a3d059084312c7d6ca2681cf60eebe832c2.json | 1 + ...b5ecb6b0c3b3fcee13be147e897573018facc.json | 1 + ...d2837a2544211e1d1e02e860af9cc2cf113f6.json | 1 + ...df78bb633baa004df0164d36937dcba61b94f.json | 1 + ...a4c37549866f3f72e93c93939e71f342009dc.json | 1 + ...2993621d34c5a76c9ed5ecd29b1bf4ad2f8db.json | 1 + ...223552556f3ef4854450f4c4b69af5e5495e2.json | 1 + ...a474b4ef48b8bab6a8c0001d3c629222792ea.json | 1 + ...19ba439cb5d872f0ffca15bdfacaa9767331b.json | 1 + ...f6fddfe22120c22886c64132a9b3ce602e147.json | 1 + ...6ccd7c962a2433ee501ee3ca9187ef3d617fd.json | 1 + ...6ee87be5533f8cf09b0d7dfd21dc847b28bc5.json | 1 + ...842e12bc2202a08e95f5d633e3dddc2361d47.json | 1 + ...eba1a283a32b46261720b6440d5fe719c1f2b.json | 1 + ...11c0e77ad58c4ba1b2be8f44059d8123bc4be.json | 1 + ...0d54a28ca532d1ccd81528e387c7f29e19bc009.js | 1 + ...7d9d678bc4c46b70cb7a06f9bba5bd1e8bdc4cc.js | 1 + ...f08e8475e5248c85493b0156ec12b9539dbcdbd.js | 1 + ...ea3161729eb3b69b7cddc6bf9bd73a6fcd98c77.js | 1 + ...fdeb12a9567eb52ebfb017533f9c9af1b260392.js | 1 + ...5c5fd0b4a171810e4d841275f7d1518ebf41745.js | 1 + ...55a3d63892d9f0912768ecfb7f2bcfc918bed5e.js | 1 + ...fdbe00d24d52ff0df91c14896a97f26bf4313f9.js | 1 + ...86a9a79febfc27f66dd68f22ef769e3813e6167.js | 1 + ...f78d7f64342b4171dcaf7a72ee318c51b9f18a2.js | 1 + ...35b25b24413de23ebefd815043d3b47a3f83f39.js | 1 + ...4c3e2771bd1f2346c649deb3ce623f66ae220fa.js | 1 + ...a9e744cc1aa5c958d392f9370da657373b84c58.js | 1 + ...d32240230f8eaaeb7255908e5f80d952ea7d851.js | 1 + ...c5ee2c1cc8c438004bb94172707abc3cd5a577d.js | 1 + ...c2f6a76ff7b9bb9de7284c85b2214750c1f608b.js | 1 + docs/favicon.png | Bin 0 -> 109 bytes docs/favicon.svg | 1 + docs/flexsearch.min.js | 42 + docs/fonts/roboto-mono-v13-latin-regular.woff | Bin 0 -> 15160 bytes .../fonts/roboto-mono-v13-latin-regular.woff2 | Bin 0 -> 12312 bytes docs/fonts/roboto-v27-latin-700.woff | Bin 0 -> 20396 bytes docs/fonts/roboto-v27-latin-700.woff2 | Bin 0 -> 15828 bytes docs/fonts/roboto-v27-latin-regular.woff | Bin 0 -> 20332 bytes docs/fonts/roboto-v27-latin-regular.woff2 | Bin 0 -> 15688 bytes docs/index.html | 292 ++++ docs/index.xml | 592 +++++++ docs/issues-contributing/index.html | 263 +++ docs/katex/auto-render.min.js | 1 + docs/katex/fonts/KaTeX_AMS-Regular.ttf | Bin 0 -> 70972 bytes docs/katex/fonts/KaTeX_AMS-Regular.woff | Bin 0 -> 38868 bytes docs/katex/fonts/KaTeX_AMS-Regular.woff2 | Bin 0 -> 32944 bytes docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf | Bin 0 -> 19316 bytes docs/katex/fonts/KaTeX_Caligraphic-Bold.woff | Bin 0 -> 11696 bytes docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 | Bin 0 -> 10448 bytes .../katex/fonts/KaTeX_Caligraphic-Regular.ttf | Bin 0 -> 18684 bytes .../fonts/KaTeX_Caligraphic-Regular.woff | Bin 0 -> 11460 bytes .../fonts/KaTeX_Caligraphic-Regular.woff2 | Bin 0 -> 10240 bytes docs/katex/fonts/KaTeX_Fraktur-Bold.ttf | Bin 0 -> 35660 bytes docs/katex/fonts/KaTeX_Fraktur-Bold.woff | Bin 0 -> 22632 bytes docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 | Bin 0 -> 20360 bytes docs/katex/fonts/KaTeX_Fraktur-Regular.ttf | Bin 0 -> 34352 bytes docs/katex/fonts/KaTeX_Fraktur-Regular.woff | Bin 0 -> 22088 bytes docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 | Bin 0 -> 19784 bytes docs/katex/fonts/KaTeX_Main-Bold.ttf | Bin 0 -> 60784 bytes docs/katex/fonts/KaTeX_Main-Bold.woff | Bin 0 -> 35464 bytes docs/katex/fonts/KaTeX_Main-Bold.woff2 | Bin 0 -> 30244 bytes docs/katex/fonts/KaTeX_Main-BoldItalic.ttf | Bin 0 -> 44496 bytes docs/katex/fonts/KaTeX_Main-BoldItalic.woff | Bin 0 -> 25352 bytes docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 | Bin 0 -> 21944 bytes docs/katex/fonts/KaTeX_Main-Italic.ttf | Bin 0 -> 47640 bytes docs/katex/fonts/KaTeX_Main-Italic.woff | Bin 0 -> 26228 bytes docs/katex/fonts/KaTeX_Main-Italic.woff2 | Bin 0 -> 22748 bytes docs/katex/fonts/KaTeX_Main-Regular.ttf | Bin 0 -> 69520 bytes docs/katex/fonts/KaTeX_Main-Regular.woff | Bin 0 -> 38112 bytes docs/katex/fonts/KaTeX_Main-Regular.woff2 | Bin 0 -> 32464 bytes docs/katex/fonts/KaTeX_Math-BoldItalic.ttf | Bin 0 -> 39308 bytes docs/katex/fonts/KaTeX_Math-BoldItalic.woff | Bin 0 -> 22324 bytes docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 | Bin 0 -> 19720 bytes docs/katex/fonts/KaTeX_Math-Italic.ttf | Bin 0 -> 40992 bytes docs/katex/fonts/KaTeX_Math-Italic.woff | Bin 0 -> 22844 bytes docs/katex/fonts/KaTeX_Math-Italic.woff2 | Bin 0 -> 20096 bytes docs/katex/fonts/KaTeX_SansSerif-Bold.ttf | Bin 0 -> 33688 bytes docs/katex/fonts/KaTeX_SansSerif-Bold.woff | Bin 0 -> 18516 bytes docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 | Bin 0 -> 15732 bytes docs/katex/fonts/KaTeX_SansSerif-Italic.ttf | Bin 0 -> 30960 bytes docs/katex/fonts/KaTeX_SansSerif-Italic.woff | Bin 0 -> 17572 bytes docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 | Bin 0 -> 15024 bytes docs/katex/fonts/KaTeX_SansSerif-Regular.ttf | Bin 0 -> 29812 bytes docs/katex/fonts/KaTeX_SansSerif-Regular.woff | Bin 0 -> 16228 bytes .../katex/fonts/KaTeX_SansSerif-Regular.woff2 | Bin 0 -> 13708 bytes docs/katex/fonts/KaTeX_Script-Regular.ttf | Bin 0 -> 24620 bytes docs/katex/fonts/KaTeX_Script-Regular.woff | Bin 0 -> 13428 bytes docs/katex/fonts/KaTeX_Script-Regular.woff2 | Bin 0 -> 12064 bytes docs/katex/fonts/KaTeX_Size1-Regular.ttf | Bin 0 -> 12916 bytes docs/katex/fonts/KaTeX_Size1-Regular.woff | Bin 0 -> 6696 bytes docs/katex/fonts/KaTeX_Size1-Regular.woff2 | Bin 0 -> 5592 bytes docs/katex/fonts/KaTeX_Size2-Regular.ttf | Bin 0 -> 12172 bytes docs/katex/fonts/KaTeX_Size2-Regular.woff | Bin 0 -> 6436 bytes docs/katex/fonts/KaTeX_Size2-Regular.woff2 | Bin 0 -> 5392 bytes docs/katex/fonts/KaTeX_Size3-Regular.ttf | Bin 0 -> 8120 bytes docs/katex/fonts/KaTeX_Size3-Regular.woff | Bin 0 -> 4568 bytes docs/katex/fonts/KaTeX_Size3-Regular.woff2 | Bin 0 -> 3728 bytes docs/katex/fonts/KaTeX_Size4-Regular.ttf | Bin 0 -> 11016 bytes docs/katex/fonts/KaTeX_Size4-Regular.woff | Bin 0 -> 6184 bytes docs/katex/fonts/KaTeX_Size4-Regular.woff2 | Bin 0 -> 5028 bytes docs/katex/fonts/KaTeX_Typewriter-Regular.ttf | Bin 0 -> 35924 bytes .../katex/fonts/KaTeX_Typewriter-Regular.woff | Bin 0 -> 20260 bytes .../fonts/KaTeX_Typewriter-Regular.woff2 | Bin 0 -> 17272 bytes docs/katex/katex.min.css | 1 + docs/katex/katex.min.js | 1 + docs/ldsctrlest-logo.png | Bin 0 -> 16128 bytes docs/manifest.json | 15 + docs/mermaid.min.js | 32 + docs/sitemap.xml | 193 +++ docs/svg/calendar.svg | 1 + docs/svg/edit.svg | 1 + docs/svg/menu.svg | 1 + docs/svg/toc.svg | 1 + docs/svg/translate.svg | 1 + docs/tags/index.html | 269 +++ docs/tags/index.xml | 11 + docs/tags/page/1/index.html | 10 + .../content/docs/api/Classes/_index.md | 2 +- .../api/Classes/classlds_1_1_controller.md | 2 +- .../docs/api/Classes/classlds_1_1_e_m.md | 2 +- .../docs/api/Classes/classlds_1_1_fit.md | 2 +- .../docs/api/Classes/classlds_1_1_s_s_i_d.md | 2 +- .../classlds_1_1_switched_controller.md | 2 +- .../docs/api/Classes/classlds_1_1_system.md | 2 +- .../classlds_1_1_uniform_matrix_list.md | 2 +- .../classlds_1_1_uniform_system_list.md | 2 +- .../classlds_1_1_uniform_vector_list.md | 2 +- .../classlds_1_1gaussian_1_1_controller.md | 2 +- .../Classes/classlds_1_1gaussian_1_1_fit.md | 2 +- .../classlds_1_1gaussian_1_1_fit_e_m.md | 2 +- .../classlds_1_1gaussian_1_1_fit_s_s_i_d.md | 2 +- ...lds_1_1gaussian_1_1_switched_controller.md | 2 +- .../classlds_1_1gaussian_1_1_system.md | 2 +- .../classlds_1_1poisson_1_1_controller.md | 2 +- .../Classes/classlds_1_1poisson_1_1_fit.md | 2 +- .../classlds_1_1poisson_1_1_fit_e_m.md | 2 +- .../classlds_1_1poisson_1_1_fit_s_s_i_d.md | 2 +- ...slds_1_1poisson_1_1_switched_controller.md | 2 +- .../Classes/classlds_1_1poisson_1_1_system.md | 2 +- .../content/docs/api/Examples/_index.md | 2 +- .../api/Examples/eg_glds_ctrl_8cpp-example.md | 2 +- .../eg_glds_du_plds_ctrl_8cpp-example.md | 2 +- .../api/Examples/eg_plds_ctrl_8cpp-example.md | 2 +- .../api/Examples/eg_plds_est_8cpp-example.md | 2 +- .../eg_plds_switched_ctrl_8cpp-example.md | 2 +- .../content/docs/api/Files/_index.md | 2 +- .../dir_156a98879751e549d6939ca71a62d61f.md | 2 +- .../dir_68267d1309a1af8e8297ef4c3efbcdba.md | 2 +- .../dir_d28a4824dc47e487b107a5db32ef43c4.md | 2 +- .../dir_d44c64559bbebec7f509842c48db8b23.md | 2 +- .../docs/api/Files/eg__glds__ctrl_8cpp.md | 2 +- .../Files/eg__glds__du__plds__ctrl_8cpp.md | 2 +- .../docs/api/Files/eg__plds__ctrl_8cpp.md | 2 +- .../docs/api/Files/eg__plds__est_8cpp.md | 2 +- .../Files/eg__plds__switched__ctrl_8cpp.md | 2 +- .../content/docs/api/Files/lds_8cpp.md | 2 +- .../content/docs/api/Files/lds_8h.md | 2 +- .../content/docs/api/Files/lds__ctrl_8h.md | 2 +- .../content/docs/api/Files/lds__fit_8h.md | 2 +- .../content/docs/api/Files/lds__fit__em_8h.md | 2 +- .../docs/api/Files/lds__fit__ssid_8h.md | 2 +- .../docs/api/Files/lds__gaussian_8h.md | 2 +- .../docs/api/Files/lds__gaussian__ctrl_8h.md | 2 +- .../docs/api/Files/lds__gaussian__fit_8h.md | 2 +- .../api/Files/lds__gaussian__fit__em_8h.md | 2 +- .../api/Files/lds__gaussian__fit__ssid_8h.md | 2 +- .../docs/api/Files/lds__gaussian__sctrl_8h.md | 2 +- .../docs/api/Files/lds__gaussian__sys_8cpp.md | 2 +- .../docs/api/Files/lds__gaussian__sys_8h.md | 2 +- .../content/docs/api/Files/lds__poisson_8h.md | 2 +- .../docs/api/Files/lds__poisson__ctrl_8h.md | 2 +- .../docs/api/Files/lds__poisson__fit_8h.md | 2 +- .../api/Files/lds__poisson__fit__em_8h.md | 2 +- .../api/Files/lds__poisson__fit__ssid_8h.md | 2 +- .../docs/api/Files/lds__poisson__sctrl_8h.md | 2 +- .../docs/api/Files/lds__poisson__sys_8cpp.md | 2 +- .../docs/api/Files/lds__poisson__sys_8h.md | 2 +- .../content/docs/api/Files/lds__sctrl_8h.md | 2 +- .../content/docs/api/Files/lds__sys_8cpp.md | 2 +- .../content/docs/api/Files/lds__sys_8h.md | 2 +- .../docs/api/Files/lds__uniform__mats_8h.md | 2 +- .../api/Files/lds__uniform__systems_8h.md | 2 +- .../docs/api/Files/lds__uniform__vecs_8cpp.md | 2 +- .../docs/api/Files/lds__uniform__vecs_8h.md | 2 +- .../content/docs/api/Files/mex__c__util_8h.md | 2 +- .../docs/api/Files/mex__cpp__util_8h.md | 2 +- .../content/docs/api/Modules/_index.md | 2 +- .../docs/api/Modules/group__control__masks.md | 2 +- .../docs/api/Modules/group__defaults.md | 2 +- .../content/docs/api/Namespaces/_index.md | 2 +- .../docs/api/Namespaces/namespacearmamexc.md | 2 +- .../api/Namespaces/namespacearmamexcpp.md | 2 +- .../docs/api/Namespaces/namespacelds.md | 2 +- .../Namespaces/namespacelds_1_1gaussian.md | 2 +- .../api/Namespaces/namespacelds_1_1poisson.md | 2 +- .../docs/api/Namespaces/namespacestd.md | 2 +- .../content/docs/api/Pages/_index.md | 2 +- 311 files changed, 47697 insertions(+), 80 deletions(-) create mode 100644 docs/404.html create mode 100644 docs/acknowledgements/index.html create mode 100644 docs/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css create mode 100644 docs/categories/index.html create mode 100644 docs/categories/index.xml create mode 100644 docs/categories/page/1/index.html create mode 100644 docs/classlds_1_1_controller__inherit__graph.png create mode 100644 docs/classlds_1_1_system__inherit__graph.png create mode 100644 docs/docs/api/classes/classlds_1_1_controller/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_e_m/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_fit/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_s_s_i_d/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_switched_controller/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_system/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_uniform_matrix_list/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_uniform_system_list/index.html create mode 100644 docs/docs/api/classes/classlds_1_1_uniform_vector_list/index.html create mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1_controller/index.html create mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1_fit/index.html create mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/index.html create mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/index.html create mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/index.html create mode 100644 docs/docs/api/classes/classlds_1_1gaussian_1_1_system/index.html create mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1_controller/index.html create mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1_fit/index.html create mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/index.html create mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/index.html create mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/index.html create mode 100644 docs/docs/api/classes/classlds_1_1poisson_1_1_system/index.html create mode 100644 docs/docs/api/classes/index.html create mode 100644 docs/docs/api/classes/index.xml create mode 100644 docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html create mode 100644 docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html create mode 100644 docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html create mode 100644 docs/docs/api/examples/eg_plds_est_8cpp-example/index.html create mode 100644 docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html create mode 100644 docs/docs/api/examples/index.html create mode 100644 docs/docs/api/examples/index.xml create mode 100644 docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html create mode 100644 docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html create mode 100644 docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html create mode 100644 docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html create mode 100644 docs/docs/api/files/eg__glds__ctrl_8cpp/index.html create mode 100644 docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html create mode 100644 docs/docs/api/files/eg__plds__ctrl_8cpp/index.html create mode 100644 docs/docs/api/files/eg__plds__est_8cpp/index.html create mode 100644 docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html create mode 100644 docs/docs/api/files/index.html create mode 100644 docs/docs/api/files/index.xml create mode 100644 docs/docs/api/files/lds_8cpp/index.html create mode 100644 docs/docs/api/files/lds_8h/index.html create mode 100644 docs/docs/api/files/lds__ctrl_8h/index.html create mode 100644 docs/docs/api/files/lds__fit_8h/index.html create mode 100644 docs/docs/api/files/lds__fit__em_8h/index.html create mode 100644 docs/docs/api/files/lds__fit__ssid_8h/index.html create mode 100644 docs/docs/api/files/lds__gaussian_8h/index.html create mode 100644 docs/docs/api/files/lds__gaussian__ctrl_8h/index.html create mode 100644 docs/docs/api/files/lds__gaussian__fit_8h/index.html create mode 100644 docs/docs/api/files/lds__gaussian__fit__em_8h/index.html create mode 100644 docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html create mode 100644 docs/docs/api/files/lds__gaussian__sctrl_8h/index.html create mode 100644 docs/docs/api/files/lds__gaussian__sys_8cpp/index.html create mode 100644 docs/docs/api/files/lds__gaussian__sys_8h/index.html create mode 100644 docs/docs/api/files/lds__poisson_8h/index.html create mode 100644 docs/docs/api/files/lds__poisson__ctrl_8h/index.html create mode 100644 docs/docs/api/files/lds__poisson__fit_8h/index.html create mode 100644 docs/docs/api/files/lds__poisson__fit__em_8h/index.html create mode 100644 docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html create mode 100644 docs/docs/api/files/lds__poisson__sctrl_8h/index.html create mode 100644 docs/docs/api/files/lds__poisson__sys_8cpp/index.html create mode 100644 docs/docs/api/files/lds__poisson__sys_8h/index.html create mode 100644 docs/docs/api/files/lds__sctrl_8h/index.html create mode 100644 docs/docs/api/files/lds__sys_8cpp/index.html create mode 100644 docs/docs/api/files/lds__sys_8h/index.html create mode 100644 docs/docs/api/files/lds__uniform__mats_8h/index.html create mode 100644 docs/docs/api/files/lds__uniform__systems_8h/index.html create mode 100644 docs/docs/api/files/lds__uniform__vecs_8cpp/index.html create mode 100644 docs/docs/api/files/lds__uniform__vecs_8h/index.html create mode 100644 docs/docs/api/files/mex__c__util_8h/index.html create mode 100644 docs/docs/api/files/mex__cpp__util_8h/index.html create mode 100644 docs/docs/api/index.html create mode 100644 docs/docs/api/index.xml create mode 100644 docs/docs/api/modules/group__control__masks/index.html create mode 100644 docs/docs/api/modules/group__defaults/index.html create mode 100644 docs/docs/api/modules/index.html create mode 100644 docs/docs/api/modules/index.xml create mode 100644 docs/docs/api/namespaces/index.html create mode 100644 docs/docs/api/namespaces/index.xml create mode 100644 docs/docs/api/namespaces/namespacearmamexc/index.html create mode 100644 docs/docs/api/namespaces/namespacearmamexcpp/index.html create mode 100644 docs/docs/api/namespaces/namespacelds/index.html create mode 100644 docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html create mode 100644 docs/docs/api/namespaces/namespacelds_1_1poisson/index.html create mode 100644 docs/docs/api/namespaces/namespacestd/index.html create mode 100644 docs/docs/api/pages/index.html create mode 100644 docs/docs/api/pages/index.xml create mode 100644 docs/docs/getting-started/getting-started/index.html create mode 100644 docs/docs/getting-started/windows/index.html create mode 100644 docs/docs/index.html create mode 100644 docs/docs/index.xml create mode 100644 docs/docs/terminology/control-estimation/index.html create mode 100644 docs/docs/terminology/model/index.html create mode 100644 docs/docs/tutorials/eg_glds_control/index.html create mode 100644 docs/docs/tutorials/eg_plds_state_estimation/index.html create mode 100644 docs/docs/tutorials/eg_switched_plds_control/index.html create mode 100644 docs/docs/tutorials/index.html create mode 100644 docs/docs/tutorials/index.xml create mode 100644 docs/eg_glds_ctrl_output.png create mode 100644 docs/eg_plds_ctrl_output.png create mode 100644 docs/eg_plds_est_output.png create mode 100644 docs/eg_plds_est_output_hist.png create mode 100644 docs/eg_plds_switched_ctrl_output.png create mode 100644 docs/en.search-data.min.1bc81b8f6c065ee207cd6c9ffacb10cc8cbb02c0e2b14346c8eca1b0148e9277.json create mode 100644 docs/en.search-data.min.30eff4c2c98b0267a65ac83cdd908a3d059084312c7d6ca2681cf60eebe832c2.json create mode 100644 docs/en.search-data.min.3daf9b62119cf749584e6093a5ab5ecb6b0c3b3fcee13be147e897573018facc.json create mode 100644 docs/en.search-data.min.4c637a056ddfb2c8bcb41aabcefd2837a2544211e1d1e02e860af9cc2cf113f6.json create mode 100644 docs/en.search-data.min.51330e61c7b1fd13d76bf8145d7df78bb633baa004df0164d36937dcba61b94f.json create mode 100644 docs/en.search-data.min.65c34f625f9950a602a0226433da4c37549866f3f72e93c93939e71f342009dc.json create mode 100644 docs/en.search-data.min.88021e71daa56310a06a39db6e72993621d34c5a76c9ed5ecd29b1bf4ad2f8db.json create mode 100644 docs/en.search-data.min.916c0a771f703a05af3ec0233f9223552556f3ef4854450f4c4b69af5e5495e2.json create mode 100644 docs/en.search-data.min.9cfd320ab0b81a7a116ce6b7963a474b4ef48b8bab6a8c0001d3c629222792ea.json create mode 100644 docs/en.search-data.min.c719c889cec9be6cd5a2ff58d2119ba439cb5d872f0ffca15bdfacaa9767331b.json create mode 100644 docs/en.search-data.min.d2e859c10d1d47f88862dc00415f6fddfe22120c22886c64132a9b3ce602e147.json create mode 100644 docs/en.search-data.min.d8a5f567aaddfd14291ea701eca6ccd7c962a2433ee501ee3ca9187ef3d617fd.json create mode 100644 docs/en.search-data.min.d8f6979e36e91aaee0955534c9a6ee87be5533f8cf09b0d7dfd21dc847b28bc5.json create mode 100644 docs/en.search-data.min.e015cd687a9e9fd3fc4e08fb324842e12bc2202a08e95f5d633e3dddc2361d47.json create mode 100644 docs/en.search-data.min.f0cd534ef163f541ebcbce10f51eba1a283a32b46261720b6440d5fe719c1f2b.json create mode 100644 docs/en.search-data.min.f9279ffce4d2a4ee361f219216511c0e77ad58c4ba1b2be8f44059d8123bc4be.json create mode 100644 docs/en.search.min.0a4ff8fc05541e016283051ae0d54a28ca532d1ccd81528e387c7f29e19bc009.js create mode 100644 docs/en.search.min.0af7fedf957aa0ede799b0fc87d9d678bc4c46b70cb7a06f9bba5bd1e8bdc4cc.js create mode 100644 docs/en.search.min.106502c57e06165042a7025a2f08e8475e5248c85493b0156ec12b9539dbcdbd.js create mode 100644 docs/en.search.min.456a135a860cf0205fd958556ea3161729eb3b69b7cddc6bf9bd73a6fcd98c77.js create mode 100644 docs/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js create mode 100644 docs/en.search.min.618b0a14a873644d726c4cdf25c5fd0b4a171810e4d841275f7d1518ebf41745.js create mode 100644 docs/en.search.min.6bf4a945c37a68b0e35197d5055a3d63892d9f0912768ecfb7f2bcfc918bed5e.js create mode 100644 docs/en.search.min.82d18e8d935694d7ff0b8a23dfdbe00d24d52ff0df91c14896a97f26bf4313f9.js create mode 100644 docs/en.search.min.880d08e6f5b646cba945c77cc86a9a79febfc27f66dd68f22ef769e3813e6167.js create mode 100644 docs/en.search.min.a7b5cd643b42a37fe645bb6e7f78d7f64342b4171dcaf7a72ee318c51b9f18a2.js create mode 100644 docs/en.search.min.ac7993fe8d4a4c7aff8278e4035b25b24413de23ebefd815043d3b47a3f83f39.js create mode 100644 docs/en.search.min.af624a2a488cdbe2acbfe043f4c3e2771bd1f2346c649deb3ce623f66ae220fa.js create mode 100644 docs/en.search.min.b8d7078e7074dff396e335edaa9e744cc1aa5c958d392f9370da657373b84c58.js create mode 100644 docs/en.search.min.ba0bee9096d7214e3ef986b48d32240230f8eaaeb7255908e5f80d952ea7d851.js create mode 100644 docs/en.search.min.dd282e74f117abd7cc99a5962c5ee2c1cc8c438004bb94172707abc3cd5a577d.js create mode 100644 docs/en.search.min.fd38584a97146b6a0437fa7aec2f6a76ff7b9bb9de7284c85b2214750c1f608b.js create mode 100644 docs/favicon.png create mode 100644 docs/favicon.svg create mode 100644 docs/flexsearch.min.js create mode 100644 docs/fonts/roboto-mono-v13-latin-regular.woff create mode 100644 docs/fonts/roboto-mono-v13-latin-regular.woff2 create mode 100644 docs/fonts/roboto-v27-latin-700.woff create mode 100644 docs/fonts/roboto-v27-latin-700.woff2 create mode 100644 docs/fonts/roboto-v27-latin-regular.woff create mode 100644 docs/fonts/roboto-v27-latin-regular.woff2 create mode 100644 docs/index.html create mode 100644 docs/index.xml create mode 100644 docs/issues-contributing/index.html create mode 100644 docs/katex/auto-render.min.js create mode 100644 docs/katex/fonts/KaTeX_AMS-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_AMS-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_AMS-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf create mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Bold.woff create mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 create mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Fraktur-Bold.ttf create mode 100644 docs/katex/fonts/KaTeX_Fraktur-Bold.woff create mode 100644 docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 create mode 100644 docs/katex/fonts/KaTeX_Fraktur-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Fraktur-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Main-Bold.ttf create mode 100644 docs/katex/fonts/KaTeX_Main-Bold.woff create mode 100644 docs/katex/fonts/KaTeX_Main-Bold.woff2 create mode 100644 docs/katex/fonts/KaTeX_Main-BoldItalic.ttf create mode 100644 docs/katex/fonts/KaTeX_Main-BoldItalic.woff create mode 100644 docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 create mode 100644 docs/katex/fonts/KaTeX_Main-Italic.ttf create mode 100644 docs/katex/fonts/KaTeX_Main-Italic.woff create mode 100644 docs/katex/fonts/KaTeX_Main-Italic.woff2 create mode 100644 docs/katex/fonts/KaTeX_Main-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Main-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Main-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Math-BoldItalic.ttf create mode 100644 docs/katex/fonts/KaTeX_Math-BoldItalic.woff create mode 100644 docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 create mode 100644 docs/katex/fonts/KaTeX_Math-Italic.ttf create mode 100644 docs/katex/fonts/KaTeX_Math-Italic.woff create mode 100644 docs/katex/fonts/KaTeX_Math-Italic.woff2 create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Bold.ttf create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Bold.woff create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Italic.ttf create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Italic.woff create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_SansSerif-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Script-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Script-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Script-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Size1-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Size1-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Size1-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Size2-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Size2-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Size2-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Size3-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Size3-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Size3-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Size4-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Size4-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Size4-Regular.woff2 create mode 100644 docs/katex/fonts/KaTeX_Typewriter-Regular.ttf create mode 100644 docs/katex/fonts/KaTeX_Typewriter-Regular.woff create mode 100644 docs/katex/fonts/KaTeX_Typewriter-Regular.woff2 create mode 100644 docs/katex/katex.min.css create mode 100644 docs/katex/katex.min.js create mode 100644 docs/ldsctrlest-logo.png create mode 100644 docs/manifest.json create mode 100644 docs/mermaid.min.js create mode 100644 docs/sitemap.xml create mode 100644 docs/svg/calendar.svg create mode 100644 docs/svg/edit.svg create mode 100644 docs/svg/menu.svg create mode 100644 docs/svg/toc.svg create mode 100644 docs/svg/translate.svg create mode 100644 docs/tags/index.html create mode 100644 docs/tags/index.xml create mode 100644 docs/tags/page/1/index.html diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 00000000..9a84d21a --- /dev/null +++ b/docs/404.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="en"> + + <head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=""> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/404.html"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="404 Page not found"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>404 Page not found | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + + + <style> + .not-found { + text-align: center; + } + .not-found h1 { + margin: .25em 0 0 0; + opacity: .25; + font-size: 40vmin; + } + </style> + </head> + + <body> + <main class="flex justify-center not-found"> + <div> + <h1>404</h1> + <h2>Page Not Found</h2> + <h3> + <a href="/lds-ctrl-est/">LDS C&amp;E</a> + </h3> + </div> + </main> + + + </body> + + </html> diff --git a/docs/acknowledgements/index.html b/docs/acknowledgements/index.html new file mode 100644 index 00000000..10d7b2c5 --- /dev/null +++ b/docs/acknowledgements/index.html @@ -0,0 +1,244 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Acknowledgements + # + +Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/acknowledgements/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="LDS C&E"> + <meta property="og:description" content="Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> +<title>Acknowledgements | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"class=active><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Acknowledgements</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#acknowledgements">Acknowledgements</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="acknowledgements"> + Acknowledgements + <a class="anchor" href="#acknowledgements">#</a> +</h1> +<p>Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#acknowledgements">Acknowledgements</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css b/docs/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css new file mode 100644 index 00000000..6dc6c4a7 --- /dev/null +++ b/docs/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css @@ -0,0 +1 @@ +@charset "UTF-8";:root{--gray-100:#f8f9fa;--gray-200:#e9ecef;--gray-500:#adb5bd;--color-link:#0055bb;--color-visited-link:#8440f1;--body-background:white;--body-font-color:black;--icon-filter:none;--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}@media(prefers-color-scheme:dark){:root{--gray-100:rgba(255, 255, 255, 0.1);--gray-200:rgba(255, 255, 255, 0.2);--gray-500:rgba(255, 255, 255, 0.5);--color-link:#84b2ff;--color-visited-link:#b88dff;--body-background:#343a40;--body-font-color:#e9ecef;--icon-filter:brightness(0) invert(1);--hint-color-info:#6bf;--hint-color-warning:#fd6;--hint-color-danger:#f66}}/*!normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css*/html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}.flex{display:flex}.flex-auto{flex:auto}.flex-even{flex:1 1}.flex-wrap{flex-wrap:wrap}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.align-center{align-items:center}.mx-auto{margin:0 auto}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.hidden{display:none}input.toggle{height:0;width:0;overflow:hidden;opacity:0;position:absolute}.clearfix::after{content:"";display:table;clear:both}html{font-size:16px;scroll-behavior:smooth;touch-action:manipulation}body{min-width:20rem;color:var(--body-font-color);background:var(--body-background);letter-spacing:.33px;font-weight:400;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box}body *{box-sizing:inherit}h1,h2,h3,h4,h5{font-weight:400}a{text-decoration:none;color:var(--color-link)}img{vertical-align:baseline}:focus{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}aside nav ul{padding:0;margin:0;list-style:none}aside nav ul li{margin:1em 0;position:relative}aside nav ul a{display:block}aside nav ul a:hover{opacity:.5}aside nav ul ul{padding-inline-start:1rem}ul.pagination{display:flex;justify-content:center;list-style-type:none}ul.pagination .page-item a{padding:1rem}.container{max-width:80rem;margin:0 auto}.book-icon{filter:var(--icon-filter)}.book-brand{margin-top:0}.book-brand img{height:1.5em;width:auto;vertical-align:middle;margin-inline-end:.5rem}.book-menu{flex:0 0 16rem;font-size:.875rem}.book-menu .book-menu-content{width:16rem;padding:1rem;background:var(--body-background);position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-menu a,.book-menu label{color:inherit;cursor:pointer;word-wrap:break-word}.book-menu a.active{color:var(--color-link)}.book-menu input.toggle+label+ul{display:none}.book-menu input.toggle:checked+label+ul{display:block}.book-menu input.toggle+label::after{content:"▸"}.book-menu input.toggle:checked+label::after{content:"▾"}body[dir=rtl] .book-menu input.toggle+label::after{content:"◂"}body[dir=rtl] .book-menu input.toggle:checked+label::after{content:"▾"}.book-section-flat{margin-bottom:2rem}.book-section-flat:not(:first-child){margin-top:2rem}.book-section-flat>a,.book-section-flat>span,.book-section-flat>label{font-weight:bolder}.book-section-flat>ul{padding-inline-start:0}.book-page{min-width:20rem;flex-grow:1;padding:1rem}.book-post{margin-bottom:3rem}.book-header{display:none;margin-bottom:1rem}.book-header label{line-height:0}.book-header img.book-icon{height:1.5em;width:1.5em}.book-search{position:relative;margin:1rem 0;border-bottom:1px solid transparent}.book-search input{width:100%;padding:.5rem;border:0;border-radius:.25rem;background:var(--gray-100);color:var(--body-font-color)}.book-search input:required+.book-search-spinner{display:block}.book-search .book-search-spinner{position:absolute;top:0;margin:.5rem;margin-inline-start:calc(100% - 1.5rem);width:1rem;height:1rem;border:1px solid transparent;border-top-color:var(--body-font-color);border-radius:50%;animation:spin 1s ease infinite}@keyframes spin{100%{transform:rotate(360deg)}}.book-search small{opacity:.5}.book-toc{flex:0 0 16rem;font-size:.75rem}.book-toc .book-toc-content{width:16rem;padding:1rem;position:fixed;top:0;bottom:0;overflow-x:hidden;overflow-y:auto}.book-toc img{height:1em;width:1em}.book-toc nav>ul>li:first-child{margin-top:0}.book-footer{padding-top:1rem;font-size:.875rem}.book-footer img{height:1em;width:1em;margin-inline-end:.5rem}.book-comments{margin-top:1rem}.book-languages{position:relative;overflow:visible;padding:1rem;margin:-1rem}.book-languages ul{margin:0;padding:0;list-style:none}.book-languages ul li{white-space:nowrap;cursor:pointer}.book-languages:hover .book-languages-list,.book-languages:focus .book-languages-list,.book-languages:focus-within .book-languages-list{display:block}.book-languages .book-languages-list{display:none;position:absolute;bottom:100%;left:0;padding:.5rem 0;background:var(--body-background);box-shadow:0 0 .25rem rgba(0,0,0,.1)}.book-languages .book-languages-list li img{opacity:.25}.book-languages .book-languages-list li.active img,.book-languages .book-languages-list li:hover img{opacity:initial}.book-languages .book-languages-list a{color:inherit;padding:.5rem 1rem}.book-home{padding:1rem}.book-menu-content,.book-toc-content,.book-page,.book-header aside,.markdown{transition:.2s ease-in-out;transition-property:transform,margin,opacity,visibility;will-change:transform,margin,opacity}@media screen and (max-width:56rem){#menu-control,#toc-control{display:inline}.book-menu{visibility:hidden;margin-inline-start:-16rem;font-size:16px;z-index:1}.book-toc{display:none}.book-header{display:block}#menu-control:focus~main label[for=menu-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#menu-control:checked~main .book-menu{visibility:initial}#menu-control:checked~main .book-menu .book-menu-content{transform:translateX(16rem);box-shadow:0 0 .5rem rgba(0,0,0,.1)}#menu-control:checked~main .book-page{opacity:.25}#menu-control:checked~main .book-menu-overlay{display:block;position:absolute;top:0;bottom:0;left:0;right:0}#toc-control:focus~main label[for=toc-control]{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}#toc-control:checked~main .book-header aside{display:block}body[dir=rtl] #menu-control:checked~main .book-menu .book-menu-content{transform:translateX(-16rem)}}@media screen and (min-width:80rem){.book-page,.book-menu .book-menu-content,.book-toc .book-toc-content{padding:2rem 1rem}}@font-face{font-family:roboto;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-regular.woff2)format("woff2"),url(fonts/roboto-v27-latin-regular.woff)format("woff")}@font-face{font-family:roboto;font-style:normal;font-weight:700;font-display:swap;src:local(""),url(fonts/roboto-v27-latin-700.woff2)format("woff2"),url(fonts/roboto-v27-latin-700.woff)format("woff")}@font-face{font-family:roboto mono;font-style:normal;font-weight:400;font-display:swap;src:local(""),url(fonts/roboto-mono-v13-latin-regular.woff2)format("woff2"),url(fonts/roboto-mono-v13-latin-regular.woff)format("woff")}body{font-family:roboto,sans-serif}code{font-family:roboto mono,monospace}@media print{.book-menu,.book-footer,.book-toc{display:none}.book-header,.book-header aside{display:block}main{display:block!important}}.markdown{line-height:1.6}.markdown>:first-child{margin-top:0}.markdown h1,.markdown h2,.markdown h3,.markdown h4,.markdown h5,.markdown h6{font-weight:400;line-height:1;margin-top:1.5em;margin-bottom:1rem}.markdown h1 a.anchor,.markdown h2 a.anchor,.markdown h3 a.anchor,.markdown h4 a.anchor,.markdown h5 a.anchor,.markdown h6 a.anchor{opacity:0;font-size:.75em;vertical-align:middle;text-decoration:none}.markdown h1:hover a.anchor,.markdown h1 a.anchor:focus,.markdown h2:hover a.anchor,.markdown h2 a.anchor:focus,.markdown h3:hover a.anchor,.markdown h3 a.anchor:focus,.markdown h4:hover a.anchor,.markdown h4 a.anchor:focus,.markdown h5:hover a.anchor,.markdown h5 a.anchor:focus,.markdown h6:hover a.anchor,.markdown h6 a.anchor:focus{opacity:initial}.markdown h4,.markdown h5,.markdown h6{font-weight:bolder}.markdown h5{font-size:.875em}.markdown h6{font-size:.75em}.markdown b,.markdown optgroup,.markdown strong{font-weight:bolder}.markdown a{text-decoration:none}.markdown a:hover{text-decoration:underline}.markdown a:visited{color:var(--color-visited-link)}.markdown img{max-width:100%}.markdown code{padding:0 .25rem;background:var(--gray-200);border-radius:.25rem;font-size:.875em}.markdown pre{padding:1rem;background:var(--gray-100);border-radius:.25rem;overflow-x:auto}.markdown pre code{padding:0;background:0 0}.markdown blockquote{margin:1rem 0;padding:.5rem 1rem .5rem .75rem;border-inline-start:.25rem solid var(--gray-200);border-radius:.25rem}.markdown blockquote :first-child{margin-top:0}.markdown blockquote :last-child{margin-bottom:0}.markdown table{overflow:auto;display:block;border-spacing:0;border-collapse:collapse;margin-top:1rem;margin-bottom:1rem}.markdown table tr th,.markdown table tr td{padding:.5rem 1rem;border:1px solid var(--gray-200)}.markdown table tr:nth-child(2n){background:var(--gray-100)}.markdown hr{height:1px;border:none;background:var(--gray-200)}.markdown ul,.markdown ol{padding-inline-start:2rem}.markdown dl dt{font-weight:bolder;margin-top:1rem}.markdown dl dd{margin-inline-start:0;margin-bottom:1rem}.markdown .highlight table tr td:nth-child(1) pre{margin:0;padding-inline-end:0}.markdown .highlight table tr td:nth-child(2) pre{margin:0;padding-inline-start:0}.markdown details{padding:1rem;border:1px solid var(--gray-200);border-radius:.25rem}.markdown details summary{line-height:1;padding:1rem;margin:-1rem;cursor:pointer}.markdown details[open] summary{margin-bottom:0}.markdown figure{margin:1rem 0}.markdown figure figcaption p{margin-top:0}.markdown-inner>:first-child{margin-top:0}.markdown-inner>:last-child{margin-bottom:0}.markdown .book-expand{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden}.markdown .book-expand .book-expand-head{background:var(--gray-100);padding:.5rem 1rem;cursor:pointer}.markdown .book-expand .book-expand-content{display:none;padding:1rem}.markdown .book-expand input[type=checkbox]:checked+.book-expand-content{display:block}.markdown .book-tabs{margin-top:1rem;margin-bottom:1rem;border:1px solid var(--gray-200);border-radius:.25rem;overflow:hidden;display:flex;flex-wrap:wrap}.markdown .book-tabs label{display:inline-block;padding:.5rem 1rem;border-bottom:1px transparent;cursor:pointer}.markdown .book-tabs .book-tabs-content{order:999;width:100%;border-top:1px solid var(--gray-100);padding:1rem;display:none}.markdown .book-tabs input[type=radio]:checked+label{border-bottom:1px solid var(--color-link)}.markdown .book-tabs input[type=radio]:checked+label+.book-tabs-content{display:block}.markdown .book-tabs input[type=radio]:focus+label{outline-style:auto;outline-color:currentColor;outline-color:-webkit-focus-ring-color}.markdown .book-columns{margin-left:-1rem;margin-right:-1rem}.markdown .book-columns>div{margin:1rem 0;min-width:10rem;padding:0 1rem}.markdown a.book-btn{display:inline-block;font-size:.875rem;color:var(--color-link);line-height:2rem;padding:0 1rem;border:1px solid var(--color-link);border-radius:.25rem;cursor:pointer}.markdown a.book-btn:hover{text-decoration:none}.markdown .book-hint.info{border-color:#6bf;background-color:rgba(102,187,255,.1)}.markdown .book-hint.warning{border-color:#fd6;background-color:rgba(255,221,102,.1)}.markdown .book-hint.danger{border-color:#f66;background-color:rgba(255,102,102,.1)} \ No newline at end of file diff --git a/docs/categories/index.html b/docs/categories/index.html new file mode 100644 index 00000000..742e621f --- /dev/null +++ b/docs/categories/index.html @@ -0,0 +1,269 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=""> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/categories/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Categories"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Categories | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/categories/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Categories</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + <nav> + <ul> + + + <li class="book-section-flat"> + <strong>Categories</strong> + <ul> + + </ul> + </li> + + + + <li class="book-section-flat"> + <strong>Tags</strong> + <ul> + + </ul> + </li> + + + </ul> +</nav> + + + </aside> + + + </header> + + + + + + + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + <nav> + <ul> + + + <li class="book-section-flat"> + <strong>Categories</strong> + <ul> + + </ul> + </li> + + + + <li class="book-section-flat"> + <strong>Tags</strong> + <ul> + + </ul> + </li> + + + </ul> +</nav> + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/categories/index.xml b/docs/categories/index.xml new file mode 100644 index 00000000..fd3dd1ae --- /dev/null +++ b/docs/categories/index.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Categories on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/categories/</link> + <description>Recent content in Categories on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/categories/index.xml" rel="self" type="application/rss+xml" /> + </channel> +</rss> diff --git a/docs/categories/page/1/index.html b/docs/categories/page/1/index.html new file mode 100644 index 00000000..e450a64a --- /dev/null +++ b/docs/categories/page/1/index.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <title>https://stanley-rozell.github.io/lds-ctrl-est/categories/</title> + <link rel="canonical" href="https://stanley-rozell.github.io/lds-ctrl-est/categories/"> + <meta name="robots" content="noindex"> + <meta charset="utf-8"> + <meta http-equiv="refresh" content="0; url=https://stanley-rozell.github.io/lds-ctrl-est/categories/"> + </head> +</html> diff --git a/docs/classlds_1_1_controller__inherit__graph.png b/docs/classlds_1_1_controller__inherit__graph.png new file mode 100644 index 0000000000000000000000000000000000000000..427b48c1a675780036d82e997dbfd37ee2ab36d8 GIT binary patch literal 28819 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 literal 0 HcmV?d00001 diff --git a/docs/classlds_1_1_system__inherit__graph.png b/docs/classlds_1_1_system__inherit__graph.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f034a30c84f4f65688c6a1445309b21d37c434 GIT binary patch literal 7184 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 literal 0 HcmV?d00001 diff --git a/docs/docs/api/classes/classlds_1_1_controller/index.html b/docs/docs/api/classes/classlds_1_1_controller/index.html new file mode 100644 index 00000000..904ab535 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_controller/index.html @@ -0,0 +1,1482 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + lds::Controller + # + +More&hellip; +Inherited by lds::SwitchedController&lt; System &gt;, lds::gaussian::Controller, lds::poisson::Controller + + Public Functions + # + + + + + + Name + + + + + + Controller() =defaultConstructs a new Controller. + + + + Controller(const System &amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)Constructs a new Controller. + + + + Controller(System &amp;&amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)Constructs a new Controller by moving the system object. + + + const Vector &amp; + Control(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)updates control signal (single-step) + + + const Vector &amp; + ControlOutputReference(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)updates control signal, given previously-set (single-step) + + + const System &amp; + sys() const + + + const Matrix &amp; + Kc() constGet state feedback controller gain. + + + const Matrix &amp; + Kc_inty() constGet integral controller gain. + + + const Matrix &amp; + Kc_u() constGet input feedback controller gain. + + + const Vector &amp; + g_design() constGet input gain used in controller design. + + + const Vector &amp; + u_ref() constGet reference input. + + + const Vector &amp; + x_ref() constGet reference state. + + + const Vector &amp; + y_ref() constGet reference output. + + + size_t + control_type() constGet controller type. + + + data_t + tau_awu() constGet time constant of anti-integral-windup. + + + data_t + u_lb() constGet control lower bound. + + + data_t + u_ub() constGet control upper bound. + + + void + set_sys(const System &amp; sys)Set system. + + + void + set_g_design(const Vector &amp; g_design)Set input gain used in controller design (g_design) + + + void + set_u_ref(const Vector &amp; u_ref)Set reference input (u_ref) + + + void + set_x_ref(const Vector &amp; x_ref)Set reference state (x_ref) + + + virtual void + set_y_ref(const Vector &amp; y_ref)Set reference output (y_ref) + + + void + set_Kc(const Matrix &amp; Kc)Set state controller gain. + + + void + set_Kc_inty(const Matrix &amp; Kc_inty)Set integral controller gain. + + + void + set_Kc_u(const Matrix &amp; Kc_u)Set input controller gain. + + + void + set_tau_awu(data_t tau)Set time constant of anti-integral-windup. + + + void + set_control_type(size_t control_type)Sets the control type. + + + void + set_u_lb(data_t u_lb)sets control lower bound + + + void + set_u_ub(data_t u_ub)Sets control upper bound. + + + void + Reset()reset system and control variables. + + + void + Print()prints variables to stdout + + + + + Protected Attributes + # + + + + + + Name + + + + + System + sys_ underlying LDS + + + Vector + u_ control signal + + + Vector + u_return_ control signal that is returned to user + + + Vector + g_design_ input gain of the system used for controller design + + + Vector + u_ref_ reference input + + + Vector + u_ref_prev_ reference input at previous time step + + + Vector + x_ref_ reference state + + + Vector + y_ref_ reference output + + + Vector + cx_ref_ + + + Matrix + Kc_ state controller gain + + + Matrix + Kc_u_ input controller gain (optional when control updates \deltaU) + + + Matrix + Kc_inty_ integral controller gain + + + Vector + du_ref_ + + + Vector + dv_ref_ + + + Vector + v_ref_ + + + Vector + dv_ + + + Vector + v_ Control after g inversion (e.g., control in physical units) + + + Vector + int_e_ integrated error + + + Vector + int_e_awu_adjust_ anti-windup adjustment to intE + + + Vector + u_sat_ control signal after saturation (for antiWindup) + + + bool + do_control_prev_ + + + bool + do_lock_control_prev_ + + + bool + u_saturated_ whether control signal has reached saturation limits + + + data_t + u_lb_ lower bound on control + + + data_t + u_ub_ upper bound on control + + + data_t + tau_awu_ antiwindup time constant + + + data_t + k_awu_ + + + data_t + t_since_control_onset_ time since control epoch onset + + + size_t + control_type_ controller type + + + + + Detailed Description + # + +template &lt;typename System &gt; +class lds::Controller; + + + + Public Function Details + # + + + Controller + # + +Controller() =default + + + Controller + # + +inline Controller( + const System &amp; sys, + data_t u_lb, + data_t u_ub, + size_t control_type =0 +) +Parameters:"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::Controller"> + <meta property="og:description" content="lds::Controller # More… +Inherited by lds::SwitchedController&lt; System &gt;, lds::gaussian::Controller, lds::poisson::Controller +Public Functions # Name Controller() =default +Constructs a new Controller. Controller(const System &amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0) +Constructs a new Controller. Controller(System &amp;&amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0) +Constructs a new Controller by moving the system object. const Vector &amp; Control(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true) +updates control signal (single-step) const Vector &amp; ControlOutputReference(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true) +updates control signal, given previously-set (single-step) const System &amp; sys() const const Matrix &amp; Kc() const +Get state feedback controller gain. const Matrix &amp; Kc_inty() const +Get integral controller gain. const Matrix &amp; Kc_u() const +Get input feedback controller gain. const Vector &amp; g_design() const +Get input gain used in controller design. const Vector &amp; u_ref() const +Get reference input. const Vector &amp; x_ref() const +Get reference state. const Vector &amp; y_ref() const +Get reference output. size_t control_type() const +Get controller type. data_t tau_awu() const +Get time constant of anti-integral-windup. data_t u_lb() const +Get control lower bound. data_t u_ub() const +Get control upper bound. void set_sys(const System &amp; sys) +Set system. void set_g_design(const Vector &amp; g_design) +Set input gain used in controller design (g_design) void set_u_ref(const Vector &amp; u_ref) +Set reference input (u_ref) void set_x_ref(const Vector &amp; x_ref) +Set reference state (x_ref) virtual void set_y_ref(const Vector &amp; y_ref) +Set reference output (y_ref) void set_Kc(const Matrix &amp; Kc) +Set state controller gain. void set_Kc_inty(const Matrix &amp; Kc_inty) +Set integral controller gain. void set_Kc_u(const Matrix &amp; Kc_u) +Set input controller gain. void set_tau_awu(data_t tau) +Set time constant of anti-integral-windup. void set_control_type(size_t control_type) +Sets the control type. void set_u_lb(data_t u_lb) +sets control lower bound void set_u_ub(data_t u_ub) +Sets control upper bound. void Reset() +reset system and control variables. void Print() +prints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template &lt;typename System &gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System &amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::Controller | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::Controller</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldscontroller">lds::Controller</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#controller"><strong>Controller</strong></a></li> + <li><a href="#controller-1"><strong>Controller</strong></a></li> + <li><a href="#controller-2"><strong>Controller</strong></a></li> + <li><a href="#control"><strong>Control</strong></a></li> + <li><a href="#controloutputreference"><strong>ControlOutputReference</strong></a></li> + <li><a href="#sys"><strong>sys</strong></a></li> + <li><a href="#kc"><strong>Kc</strong></a></li> + <li><a href="#kc_"><strong>Kc_inty</strong></a></li> + <li><a href="#kc_-1"><strong>Kc_u</strong></a></li> + <li><a href="#g_"><strong>g_design</strong></a></li> + <li><a href="#u_"><strong>u_ref</strong></a></li> + <li><a href="#x_"><strong>x_ref</strong></a></li> + <li><a href="#y_"><strong>y_ref</strong></a></li> + <li><a href="#control_"><strong>control_type</strong></a></li> + <li><a href="#tau_"><strong>tau_awu</strong></a></li> + <li><a href="#u_-1"><strong>u_lb</strong></a></li> + <li><a href="#u_-2"><strong>u_ub</strong></a></li> + <li><a href="#set_"><strong>set_sys</strong></a></li> + <li><a href="#set_-1"><strong>set_g_design</strong></a></li> + <li><a href="#set_-2"><strong>set_u_ref</strong></a></li> + <li><a href="#set_-3"><strong>set_x_ref</strong></a></li> + <li><a href="#set_-4"><strong>set_y_ref</strong></a></li> + <li><a href="#set_-5"><strong>set_Kc</strong></a></li> + <li><a href="#set_-6"><strong>set_Kc_inty</strong></a></li> + <li><a href="#set_-7"><strong>set_Kc_u</strong></a></li> + <li><a href="#set_-8"><strong>set_tau_awu</strong></a></li> + <li><a href="#set_-9"><strong>set_control_type</strong></a></li> + <li><a href="#set_-10"><strong>set_u_lb</strong></a></li> + <li><a href="#set_-11"><strong>set_u_ub</strong></a></li> + <li><a href="#reset"><strong>Reset</strong></a></li> + <li><a href="#print"><strong>Print</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#sys_"><strong>sys_</strong></a></li> + <li><a href="#u_-3"><strong>u_</strong></a></li> + <li><a href="#u_-4"><strong>u_return_</strong></a></li> + <li><a href="#g_-1"><strong>g_design_</strong></a></li> + <li><a href="#u_-5"><strong>u_ref_</strong></a></li> + <li><a href="#u_-6"><strong>u_ref_prev_</strong></a></li> + <li><a href="#x_-1"><strong>x_ref_</strong></a></li> + <li><a href="#y_-1"><strong>y_ref_</strong></a></li> + <li><a href="#cx_"><strong>cx_ref_</strong></a></li> + <li><a href="#kc_-2"><strong>Kc_</strong></a></li> + <li><a href="#kc_-3"><strong>Kc_u_</strong></a></li> + <li><a href="#kc_-4"><strong>Kc_inty_</strong></a></li> + <li><a href="#du_"><strong>du_ref_</strong></a></li> + <li><a href="#dv_"><strong>dv_ref_</strong></a></li> + <li><a href="#v_"><strong>v_ref_</strong></a></li> + <li><a href="#dv_-1"><strong>dv_</strong></a></li> + <li><a href="#v_-1"><strong>v_</strong></a></li> + <li><a href="#int_"><strong>int_e_</strong></a></li> + <li><a href="#int_-1"><strong>int_e_awu_adjust_</strong></a></li> + <li><a href="#u_-7"><strong>u_sat_</strong></a></li> + <li><a href="#do_"><strong>do_control_prev_</strong></a></li> + <li><a href="#do_-1"><strong>do_lock_control_prev_</strong></a></li> + <li><a href="#u_-8"><strong>u_saturated_</strong></a></li> + <li><a href="#u_-9"><strong>u_lb_</strong></a></li> + <li><a href="#u_-10"><strong>u_ub_</strong></a></li> + <li><a href="#tau_-1"><strong>tau_awu_</strong></a></li> + <li><a href="#k_"><strong>k_awu_</strong></a></li> + <li><a href="#t_"><strong>t_since_control_onset_</strong></a></li> + <li><a href="#control_-1"><strong>control_type_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldscontroller"> + lds::Controller + <a class="anchor" href="#ldscontroller">#</a> +</h1> +<p><a href="#detailed-description">More&hellip;</a></p> +<p>Inherited by <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController&lt; System &gt;</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/">lds::gaussian::Controller</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/">lds::poisson::Controller</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;&amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a> by moving the system object.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control">Control</a></strong>(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step)</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference">ControlOutputReference</a></strong>(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step)</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys">sys</a></strong>() const</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc">Kc</a></strong>() const<br>Get state feedback controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty">Kc_inty</a></strong>() const<br>Get integral controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u">Kc_u</a></strong>() const<br>Get input feedback controller gain.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design">g_design</a></strong>() const<br>Get input gain used in controller design.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref">u_ref</a></strong>() const<br>Get reference input.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref">x_ref</a></strong>() const<br>Get reference state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref">y_ref</a></strong>() const<br>Get reference output.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type">control_type</a></strong>() const<br>Get controller type.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu">tau_awu</a></strong>() const<br>Get time constant of anti-integral-windup.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb">u_lb</a></strong>() const<br>Get control lower bound.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub">u_ub</a></strong>() const<br>Get control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys">set_sys</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys)<br>Set system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design">set_g_design</a></strong>(const Vector &amp; g_design)<br>Set input gain used in controller design (g_design)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref">set_u_ref</a></strong>(const Vector &amp; u_ref)<br>Set reference input (u_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref">set_x_ref</a></strong>(const Vector &amp; x_ref)<br>Set reference state (x_ref)</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref">set_y_ref</a></strong>(const Vector &amp; y_ref)<br>Set reference output (y_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc">set_Kc</a></strong>(const Matrix &amp; Kc)<br>Set state controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const Matrix &amp; Kc_inty)<br>Set integral controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u">set_Kc_u</a></strong>(const Matrix &amp; Kc_u)<br>Set input controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu">set_tau_awu</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tau)<br>Set time constant of anti-integral-windup.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type">set_control_type</a></strong>(size_t control_type)<br>Sets the control type.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb">set_u_lb</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb)<br>sets control lower bound</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub">set_u_ub</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub)<br>Sets control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset">Reset</a></strong>()<br>reset system and control variables.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print">Print</a></strong>()<br>prints variables to stdout</td> + </tr> + </tbody> +</table> +<h2 id="protected-attributes"> + Protected Attributes + <a class="anchor" href="#protected-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-">sys_</a></strong> <br>underlying LDS</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-">u_</a></strong> <br>control signal</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-">u_return_</a></strong> <br>control signal that is <em>returned</em> to user</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-">g_design_</a></strong> <br>input gain of the system used for controller design</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-">u_ref_</a></strong> <br>reference input</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-">u_ref_prev_</a></strong> <br>reference input at previous time step</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-">x_ref_</a></strong> <br>reference state</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-">y_ref_</a></strong> <br>reference output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-">cx_ref_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-">Kc_</a></strong> <br>state controller gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-">Kc_u_</a></strong> <br>input controller gain (optional when control updates \deltaU)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-">Kc_inty_</a></strong> <br>integral controller gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-">du_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-">dv_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-">v_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-">dv_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-">v_</a></strong> <br>Control after g inversion (e.g., control in physical units)</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-">int_e_</a></strong> <br>integrated error</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-">int_e_awu_adjust_</a></strong> <br>anti-windup adjustment to intE</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-">u_sat_</a></strong> <br>control signal after saturation (for antiWindup)</td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-">do_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-">do_lock_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-">u_saturated_</a></strong> <br>whether control signal has reached saturation limits</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-">u_lb_</a></strong> <br>lower bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-">u_ub_</a></strong> <br>upper bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-">tau_awu_</a></strong> <br>antiwindup time constant</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-">k_awu_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-">t_since_control_onset_</a></strong> <br>time since control epoch onset</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-">control_type_</a></strong> <br>controller type</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>Controller; +</span></span></code></pre></div><hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="controller"> + <strong>Controller</strong> + <a class="anchor" href="#controller">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Controller() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="controller-1"> + <strong>Controller</strong> + <a class="anchor" href="#controller-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> Controller( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> System <span style="color:#f92672">&amp;</span> sys, +</span></span><span style="display:flex;"><span> data_t u_lb, +</span></span><span style="display:flex;"><span> data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>sys</strong> <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> (derived from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a>)</li> +<li><strong>u_lb</strong> lower bound on control (u)</li> +<li><strong>u_ub</strong> upper bound on control (u)</li> +<li><strong>control_type</strong> [optional] control type bit mask</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></strong> type derived from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></li> +</ul> +<hr> +<h3 id="controller-2"> + <strong>Controller</strong> + <a class="anchor" href="#controller-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> Controller( +</span></span><span style="display:flex;"><span> System <span style="color:#f92672">&amp;&amp;</span> sys, +</span></span><span style="display:flex;"><span> data_t u_lb, +</span></span><span style="display:flex;"><span> data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>sys</strong> <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> (derived from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a>)</li> +<li><strong>u_lb</strong> lower bound on control (u)</li> +<li><strong>u_ub</strong> upper bound on control (u)</li> +<li><strong>control_type</strong> [optional] control type bit mask</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></strong> type derived from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></li> +</ul> +<hr> +<h3 id="control"> + <strong>Control</strong> + <a class="anchor" href="#control">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> Control( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> z, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_control <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_lock_control <span style="color:#f92672">=</span>false, +</span></span><span style="display:flex;"><span> data_t sigma_soft_start <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> data_t sigma_u_noise <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset <span style="color:#f92672">=</span>true +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>z</strong> measurement</li> +<li><strong>do_control</strong> [optional] whether to update control (true) or simply feed through u_ref (false)</li> +<li><strong>do_lock_control</strong> [optional] whether to lock control at its current value</li> +<li><strong>sigma_soft_start</strong> [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true)</li> +<li><strong>sigma_u_noise</strong> [optional] standard deviation (sigma) of Gaussian noise added on top of control signal</li> +<li><strong>do_reset_at_control_onset</strong> [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true)</li> +</ul> +<p><strong>Return</strong>: updated control signal</p> +<p>Updates the control signal (single-step). This is the most flexible option, but requires user to have set the controller&rsquo;s y_ref, x_ref, and u_ref variables.</p> +<hr> +<h3 id="controloutputreference"> + <strong>ControlOutputReference</strong> + <a class="anchor" href="#controloutputreference">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> ControlOutputReference( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> z, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_control <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_estimation <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_lock_control <span style="color:#f92672">=</span>false, +</span></span><span style="display:flex;"><span> data_t sigma_soft_start <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> data_t sigma_u_noise <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset <span style="color:#f92672">=</span>true +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>z</strong> measurement</li> +<li><strong>do_control</strong> [optional] whether to update control (true) or simply feed through u_ref (false)</li> +<li><strong>do_estimation</strong> [optional] whether to update state estimate (if false, effectively open-loop control)</li> +<li><strong>do_lock_control</strong> [optional] whether to lock control at its current value</li> +<li><strong>sigma_soft_start</strong> [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true)</li> +<li><strong>sigma_u_noise</strong> [optional] standard deviation (sigma) of Gaussian noise added on top of control signal</li> +<li><strong>do_reset_at_control_onset</strong> [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true)</li> +</ul> +<p><strong>Return</strong>: updated control signal</p> +<p>Updates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.</p> +<hr> +<h3 id="sys"> + <strong>sys</strong> + <a class="anchor" href="#sys">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> System <span style="color:#f92672">&amp;</span> sys() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="kc"> + <strong>Kc</strong> + <a class="anchor" href="#kc">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Kc() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="kc_"> + <strong>Kc_inty</strong> + <a class="anchor" href="#kc_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Kc_inty() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="kc_-1"> + <strong>Kc_u</strong> + <a class="anchor" href="#kc_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Kc_u() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="g_"> + <strong>g_design</strong> + <a class="anchor" href="#g_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> g_design() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="u_"> + <strong>u_ref</strong> + <a class="anchor" href="#u_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> u_ref() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="x_"> + <strong>x_ref</strong> + <a class="anchor" href="#x_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x_ref() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="y_"> + <strong>y_ref</strong> + <a class="anchor" href="#y_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> y_ref() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="control_"> + <strong>control_type</strong> + <a class="anchor" href="#control_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t control_type() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="tau_"> + <strong>tau_awu</strong> + <a class="anchor" href="#tau_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> data_t tau_awu() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="u_-1"> + <strong>u_lb</strong> + <a class="anchor" href="#u_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> data_t u_lb() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="u_-2"> + <strong>u_ub</strong> + <a class="anchor" href="#u_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> data_t u_ub() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="set_"> + <strong>set_sys</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_sys( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> System <span style="color:#f92672">&amp;</span> sys +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-1"> + <strong>set_g_design</strong> + <a class="anchor" href="#set_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_g_design( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> g_design +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-2"> + <strong>set_u_ref</strong> + <a class="anchor" href="#set_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_u_ref( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> u_ref +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-3"> + <strong>set_x_ref</strong> + <a class="anchor" href="#set_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_x_ref( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x_ref +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-4"> + <strong>set_y_ref</strong> + <a class="anchor" href="#set_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_y_ref( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> y_ref +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/#function-set-y-ref">lds::gaussian::Controller::set_y_ref</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/#function-set-y-ref">lds::gaussian::SwitchedController::set_y_ref</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/#function-set-y-ref">lds::poisson::Controller::set_y_ref</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/#function-set-y-ref">lds::poisson::SwitchedController::set_y_ref</a></p> +<hr> +<h3 id="set_-5"> + <strong>set_Kc</strong> + <a class="anchor" href="#set_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Kc +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-6"> + <strong>set_Kc_inty</strong> + <a class="anchor" href="#set_-6">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc_inty( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Kc_inty +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-7"> + <strong>set_Kc_u</strong> + <a class="anchor" href="#set_-7">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc_u( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Kc_u +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-8"> + <strong>set_tau_awu</strong> + <a class="anchor" href="#set_-8">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_tau_awu( +</span></span><span style="display:flex;"><span> data_t tau +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-9"> + <strong>set_control_type</strong> + <a class="anchor" href="#set_-9">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_control_type( +</span></span><span style="display:flex;"><span> size_t control_type +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>control_type</strong> control type bit mask</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></strong> type derived from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></li> +</ul> +<hr> +<h3 id="set_-10"> + <strong>set_u_lb</strong> + <a class="anchor" href="#set_-10">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_u_lb( +</span></span><span style="display:flex;"><span> data_t u_lb +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>u_lb</strong> control lower bound</li> +</ul> +<hr> +<h3 id="set_-11"> + <strong>set_u_ub</strong> + <a class="anchor" href="#set_-11">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_u_ub( +</span></span><span style="display:flex;"><span> data_t u_ub +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>u_ub</strong> control upper bound</li> +</ul> +<hr> +<h3 id="reset"> + <strong>Reset</strong> + <a class="anchor" href="#reset">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Reset() +</span></span></code></pre></div><hr> +<h3 id="print"> + <strong>Print</strong> + <a class="anchor" href="#print">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Print() +</span></span></code></pre></div><hr> +<h2 id="protected-attribute-details"> + Protected Attribute Details + <a class="anchor" href="#protected-attribute-details">#</a> +</h2> +<h3 id="sys_"> + <strong>sys_</strong> + <a class="anchor" href="#sys_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>System sys_; +</span></span></code></pre></div><hr> +<h3 id="u_-3"> + <strong>u_</strong> + <a class="anchor" href="#u_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector u_; +</span></span></code></pre></div><hr> +<h3 id="u_-4"> + <strong>u_return_</strong> + <a class="anchor" href="#u_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector u_return_; +</span></span></code></pre></div><hr> +<h3 id="g_-1"> + <strong>g_design_</strong> + <a class="anchor" href="#g_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector g_design_; +</span></span></code></pre></div><hr> +<h3 id="u_-5"> + <strong>u_ref_</strong> + <a class="anchor" href="#u_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector u_ref_; +</span></span></code></pre></div><hr> +<h3 id="u_-6"> + <strong>u_ref_prev_</strong> + <a class="anchor" href="#u_-6">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector u_ref_prev_; +</span></span></code></pre></div><hr> +<h3 id="x_-1"> + <strong>x_ref_</strong> + <a class="anchor" href="#x_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector x_ref_; +</span></span></code></pre></div><hr> +<h3 id="y_-1"> + <strong>y_ref_</strong> + <a class="anchor" href="#y_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector y_ref_; +</span></span></code></pre></div><hr> +<h3 id="cx_"> + <strong>cx_ref_</strong> + <a class="anchor" href="#cx_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector cx_ref_; +</span></span></code></pre></div><hr> +<h3 id="kc_-2"> + <strong>Kc_</strong> + <a class="anchor" href="#kc_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Kc_; +</span></span></code></pre></div><hr> +<h3 id="kc_-3"> + <strong>Kc_u_</strong> + <a class="anchor" href="#kc_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Kc_u_; +</span></span></code></pre></div><hr> +<h3 id="kc_-4"> + <strong>Kc_inty_</strong> + <a class="anchor" href="#kc_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Kc_inty_; +</span></span></code></pre></div><hr> +<h3 id="du_"> + <strong>du_ref_</strong> + <a class="anchor" href="#du_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector du_ref_; +</span></span></code></pre></div><hr> +<h3 id="dv_"> + <strong>dv_ref_</strong> + <a class="anchor" href="#dv_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector dv_ref_; +</span></span></code></pre></div><hr> +<h3 id="v_"> + <strong>v_ref_</strong> + <a class="anchor" href="#v_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector v_ref_; +</span></span></code></pre></div><hr> +<h3 id="dv_-1"> + <strong>dv_</strong> + <a class="anchor" href="#dv_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector dv_; +</span></span></code></pre></div><hr> +<h3 id="v_-1"> + <strong>v_</strong> + <a class="anchor" href="#v_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector v_; +</span></span></code></pre></div><hr> +<h3 id="int_"> + <strong>int_e_</strong> + <a class="anchor" href="#int_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector int_e_; +</span></span></code></pre></div><hr> +<h3 id="int_-1"> + <strong>int_e_awu_adjust_</strong> + <a class="anchor" href="#int_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector int_e_awu_adjust_; +</span></span></code></pre></div><hr> +<h3 id="u_-7"> + <strong>u_sat_</strong> + <a class="anchor" href="#u_-7">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector u_sat_; +</span></span></code></pre></div><hr> +<h3 id="do_"> + <strong>do_control_prev_</strong> + <a class="anchor" href="#do_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> do_control_prev_ <span style="color:#f92672">=</span> false; +</span></span></code></pre></div><hr> +<h3 id="do_-1"> + <strong>do_lock_control_prev_</strong> + <a class="anchor" href="#do_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> do_lock_control_prev_ <span style="color:#f92672">=</span> false; +</span></span></code></pre></div><hr> +<h3 id="u_-8"> + <strong>u_saturated_</strong> + <a class="anchor" href="#u_-8">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> u_saturated_ <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> false; +</span></span></code></pre></div><hr> +<h3 id="u_-9"> + <strong>u_lb_</strong> + <a class="anchor" href="#u_-9">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t u_lb_ {}; +</span></span></code></pre></div><hr> +<h3 id="u_-10"> + <strong>u_ub_</strong> + <a class="anchor" href="#u_-10">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t u_ub_ {}; +</span></span></code></pre></div><hr> +<h3 id="tau_-1"> + <strong>tau_awu_</strong> + <a class="anchor" href="#tau_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t tau_awu_ {}; +</span></span></code></pre></div><hr> +<h3 id="k_"> + <strong>k_awu_</strong> + <a class="anchor" href="#k_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t k_awu_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span></code></pre></div><hr> +<h3 id="t_"> + <strong>t_since_control_onset_</strong> + <a class="anchor" href="#t_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t t_since_control_onset_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span></code></pre></div><hr> +<h3 id="control_-1"> + <strong>control_type_</strong> + <a class="anchor" href="#control_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t control_type_ {}; +</span></span></code></pre></div><hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldscontroller">lds::Controller</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#controller"><strong>Controller</strong></a></li> + <li><a href="#controller-1"><strong>Controller</strong></a></li> + <li><a href="#controller-2"><strong>Controller</strong></a></li> + <li><a href="#control"><strong>Control</strong></a></li> + <li><a href="#controloutputreference"><strong>ControlOutputReference</strong></a></li> + <li><a href="#sys"><strong>sys</strong></a></li> + <li><a href="#kc"><strong>Kc</strong></a></li> + <li><a href="#kc_"><strong>Kc_inty</strong></a></li> + <li><a href="#kc_-1"><strong>Kc_u</strong></a></li> + <li><a href="#g_"><strong>g_design</strong></a></li> + <li><a href="#u_"><strong>u_ref</strong></a></li> + <li><a href="#x_"><strong>x_ref</strong></a></li> + <li><a href="#y_"><strong>y_ref</strong></a></li> + <li><a href="#control_"><strong>control_type</strong></a></li> + <li><a href="#tau_"><strong>tau_awu</strong></a></li> + <li><a href="#u_-1"><strong>u_lb</strong></a></li> + <li><a href="#u_-2"><strong>u_ub</strong></a></li> + <li><a href="#set_"><strong>set_sys</strong></a></li> + <li><a href="#set_-1"><strong>set_g_design</strong></a></li> + <li><a href="#set_-2"><strong>set_u_ref</strong></a></li> + <li><a href="#set_-3"><strong>set_x_ref</strong></a></li> + <li><a href="#set_-4"><strong>set_y_ref</strong></a></li> + <li><a href="#set_-5"><strong>set_Kc</strong></a></li> + <li><a href="#set_-6"><strong>set_Kc_inty</strong></a></li> + <li><a href="#set_-7"><strong>set_Kc_u</strong></a></li> + <li><a href="#set_-8"><strong>set_tau_awu</strong></a></li> + <li><a href="#set_-9"><strong>set_control_type</strong></a></li> + <li><a href="#set_-10"><strong>set_u_lb</strong></a></li> + <li><a href="#set_-11"><strong>set_u_ub</strong></a></li> + <li><a href="#reset"><strong>Reset</strong></a></li> + <li><a href="#print"><strong>Print</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#sys_"><strong>sys_</strong></a></li> + <li><a href="#u_-3"><strong>u_</strong></a></li> + <li><a href="#u_-4"><strong>u_return_</strong></a></li> + <li><a href="#g_-1"><strong>g_design_</strong></a></li> + <li><a href="#u_-5"><strong>u_ref_</strong></a></li> + <li><a href="#u_-6"><strong>u_ref_prev_</strong></a></li> + <li><a href="#x_-1"><strong>x_ref_</strong></a></li> + <li><a href="#y_-1"><strong>y_ref_</strong></a></li> + <li><a href="#cx_"><strong>cx_ref_</strong></a></li> + <li><a href="#kc_-2"><strong>Kc_</strong></a></li> + <li><a href="#kc_-3"><strong>Kc_u_</strong></a></li> + <li><a href="#kc_-4"><strong>Kc_inty_</strong></a></li> + <li><a href="#du_"><strong>du_ref_</strong></a></li> + <li><a href="#dv_"><strong>dv_ref_</strong></a></li> + <li><a href="#v_"><strong>v_ref_</strong></a></li> + <li><a href="#dv_-1"><strong>dv_</strong></a></li> + <li><a href="#v_-1"><strong>v_</strong></a></li> + <li><a href="#int_"><strong>int_e_</strong></a></li> + <li><a href="#int_-1"><strong>int_e_awu_adjust_</strong></a></li> + <li><a href="#u_-7"><strong>u_sat_</strong></a></li> + <li><a href="#do_"><strong>do_control_prev_</strong></a></li> + <li><a href="#do_-1"><strong>do_lock_control_prev_</strong></a></li> + <li><a href="#u_-8"><strong>u_saturated_</strong></a></li> + <li><a href="#u_-9"><strong>u_lb_</strong></a></li> + <li><a href="#u_-10"><strong>u_ub_</strong></a></li> + <li><a href="#tau_-1"><strong>tau_awu_</strong></a></li> + <li><a href="#k_"><strong>k_awu_</strong></a></li> + <li><a href="#t_"><strong>t_since_control_onset_</strong></a></li> + <li><a href="#control_-1"><strong>control_type_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_e_m/index.html b/docs/docs/api/classes/classlds_1_1_e_m/index.html new file mode 100644 index 00000000..32710fad --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_e_m/index.html @@ -0,0 +1,1229 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + lds::EM + # + +More&hellip; +Inherited by lds::gaussian::FitEM, lds::poisson::FitEM + + Public Functions + # + + + + + + Name + + + + + + EM() =defaultConstructs a new EMFit type. + + + + EM(size_t n_x, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train)Constructs a new EMFit type. + + + + EM(const Fit &amp; fit0, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train)Constructs a new EMFit type. + + + virtual + ~EM() =default + + + const Fit &amp; + Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)Runs fitting by Expectation(E)-Maximization(M) + + + std::tuple&lt; UniformMatrixList&lt; kMatFreeDim2 &gt;, UniformMatrixList&lt; kMatFreeDim2 &gt; &gt; + ReturnData()Returns the input/output data to caller. + + + const std::vector&lt; Matrix &gt; &amp; + x() constgets estimated state (over time) + + + const std::vector&lt; Matrix &gt; &amp; + y() constgets estimated output (over time) + + + const Matrix &amp; + sum_E_x_t_x_t() constgets state-input covariance + + + const Matrix &amp; + sum_E_xu_tm1_xu_tm1() constgets state-input covariance (t-minus-1) + + + const Matrix &amp; + sum_E_xu_t_xu_tm1() constgets single lag state-input covariance + + + size_t + n_t_tot()total number of time samples + + + const Vector &amp; + theta() constgets parameters updated in M step + + + + + Protected Functions + # + + + + + + Name + + + + + void + Expectation(bool force_common_initial =false)Expectation step. + + + void + Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)Maximization step. + + + void + MaximizeDynamics() + + + void + MaximizeQ() + + + void + MaximizeInitial() + + + virtual void + MaximizeOutput() =0 + + + virtual void + MaximizeMeasurement() =0 + + + void + Smooth(bool force_common_initial)get smoothed estimates + + + virtual void + RecurseKe(Matrix &amp; Ke, Cube &amp; P_pre, Cube &amp; P_post, size_t t) =0recursively update estimator gain Ke + + + void + Reset()reset to initial conditions + + + void + InitVars()Initializes the variables. + + + Vector + UpdateTheta()updates parameter list, theta + + + + + Protected Attributes + # + + + + + + Name + + + + + UniformMatrixList&lt; kMatFreeDim2 &gt; + u_ input training data + + + UniformMatrixList&lt; kMatFreeDim2 &gt; + z_ measurement training data + + + std::vector&lt; Matrix &gt; + x_ state estimate + + + std::vector&lt; Cube &gt; + P_ state estimate cov + + + std::vector&lt; Cube &gt; + P_t_tm1_ single-lag state covariance + + + std::vector&lt; Matrix &gt; + y_ output estimate + + + Matrix + diag_y_ + + + Matrix + sum_E_x_t_x_t_ state covariance (current time) + + + Matrix + sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) + + + Matrix + sum_E_xu_t_xu_tm1_ single lag state-input covariance + + + Fit + fit_ + + + Vector + theta_ + + + data_t + dt_ sample period + + + size_t + n_u_ number of inputs + + + size_t + n_x_ number of states + + + size_t + n_y_ number of outputs + + + size_t + n_trials_ number of input/output data sequences + + + std::vector&lt; size_t &gt; + n_t_ number of time steps + + + size_t + n_t_tot_ total number of time steps across trials + + + + + Detailed Description + # + +template &lt;typename Fit &gt; +class lds::EM; + + + + Public Function Details + # + + + EM + # + +EM() =default + + + EM + # + +EM( + size_t n_x, + data_t dt, + UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, + UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train +) +Parameters:"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::EM"> + <meta property="og:description" content="lds::EM # More… +Inherited by lds::gaussian::FitEM, lds::poisson::FitEM +Public Functions # Name EM() =default +Constructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train) +Constructs a new EMFit type. EM(const Fit &amp; fit0, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train) +Constructs a new EMFit type. virtual ~EM() =default const Fit &amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2) +Runs fitting by Expectation(E)-Maximization(M) std::tuple&lt; UniformMatrixList&lt; kMatFreeDim2 &gt;, UniformMatrixList&lt; kMatFreeDim2 &gt; &gt; ReturnData() +Returns the input/output data to caller. const std::vector&lt; Matrix &gt; &amp; x() const +gets estimated state (over time) const std::vector&lt; Matrix &gt; &amp; y() const +gets estimated output (over time) const Matrix &amp; sum_E_x_t_x_t() const +gets state-input covariance const Matrix &amp; sum_E_xu_tm1_xu_tm1() const +gets state-input covariance (t-minus-1) const Matrix &amp; sum_E_xu_t_xu_tm1() const +gets single lag state-input covariance size_t n_t_tot() +total number of time samples const Vector &amp; theta() const +gets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false) +Expectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false) +Maximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial) +get smoothed estimates virtual void RecurseKe(Matrix &amp; Ke, Cube &amp; P_pre, Cube &amp; P_post, size_t t) =0 +recursively update estimator gain Ke void Reset() +reset to initial conditions void InitVars() +Initializes the variables. Vector UpdateTheta() +updates parameter list, theta Protected Attributes # Name UniformMatrixList&lt; kMatFreeDim2 &gt; u_ input training data UniformMatrixList&lt; kMatFreeDim2 &gt; z_ measurement training data std::vector&lt; Matrix &gt; x_ state estimate std::vector&lt; Cube &gt; P_ state estimate cov std::vector&lt; Cube &gt; P_t_tm1_ single-lag state covariance std::vector&lt; Matrix &gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector&lt; size_t &gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template &lt;typename Fit &gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train ) Parameters:"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::EM | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::EM</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsem">lds::EM</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#em"><strong>EM</strong></a></li> + <li><a href="#em-1"><strong>EM</strong></a></li> + <li><a href="#em-2"><strong>EM</strong></a></li> + <li><a href="#heading"><strong>~EM</strong></a></li> + <li><a href="#run"><strong>Run</strong></a></li> + <li><a href="#returndata"><strong>ReturnData</strong></a></li> + <li><a href="#x"><strong>x</strong></a></li> + <li><a href="#y"><strong>y</strong></a></li> + <li><a href="#sum_"><strong>sum_E_x_t_x_t</strong></a></li> + <li><a href="#sum_-1"><strong>sum_E_xu_tm1_xu_tm1</strong></a></li> + <li><a href="#sum_-2"><strong>sum_E_xu_t_xu_tm1</strong></a></li> + <li><a href="#n_"><strong>n_t_tot</strong></a></li> + <li><a href="#theta"><strong>theta</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#expectation"><strong>Expectation</strong></a></li> + <li><a href="#maximization"><strong>Maximization</strong></a></li> + <li><a href="#maximizedynamics"><strong>MaximizeDynamics</strong></a></li> + <li><a href="#maximizeq"><strong>MaximizeQ</strong></a></li> + <li><a href="#maximizeinitial"><strong>MaximizeInitial</strong></a></li> + <li><a href="#maximizeoutput"><strong>MaximizeOutput</strong></a></li> + <li><a href="#maximizemeasurement"><strong>MaximizeMeasurement</strong></a></li> + <li><a href="#smooth"><strong>Smooth</strong></a></li> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + <li><a href="#reset"><strong>Reset</strong></a></li> + <li><a href="#initvars"><strong>InitVars</strong></a></li> + <li><a href="#updatetheta"><strong>UpdateTheta</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#u_"><strong>u_</strong></a></li> + <li><a href="#z_"><strong>z_</strong></a></li> + <li><a href="#x_"><strong>x_</strong></a></li> + <li><a href="#p_"><strong>P_</strong></a></li> + <li><a href="#p_-1"><strong>P_t_tm1_</strong></a></li> + <li><a href="#y_"><strong>y_</strong></a></li> + <li><a href="#diag_"><strong>diag_y_</strong></a></li> + <li><a href="#sum_-3"><strong>sum_E_x_t_x_t_</strong></a></li> + <li><a href="#sum_-4"><strong>sum_E_xu_tm1_xu_tm1_</strong></a></li> + <li><a href="#sum_-5"><strong>sum_E_xu_t_xu_tm1_</strong></a></li> + <li><a href="#fit_"><strong>fit_</strong></a></li> + <li><a href="#theta_"><strong>theta_</strong></a></li> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#n_-1"><strong>n_u_</strong></a></li> + <li><a href="#n_-2"><strong>n_x_</strong></a></li> + <li><a href="#n_-3"><strong>n_y_</strong></a></li> + <li><a href="#n_-4"><strong>n_trials_</strong></a></li> + <li><a href="#n_-5"><strong>n_t_</strong></a></li> + <li><a href="#n_-6"><strong>n_t_tot_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsem"> + lds::EM + <a class="anchor" href="#ldsem">#</a> +</h1> +<p><a href="#detailed-description">More&hellip;</a></p> +<p>Inherited by <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/">lds::gaussian::FitEM</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/">lds::poisson::FitEM</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>(size_t n_x, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> &amp; fit0, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em">~EM</a></strong>() =default</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run">Run</a></strong>(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M)</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata">ReturnData</a></strong>()<br>Returns the input/output data to caller.</td> + </tr> + <tr> + <td>const std::vector&lt; Matrix &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x">x</a></strong>() const<br>gets estimated state (over time)</td> + </tr> + <tr> + <td>const std::vector&lt; Matrix &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y">y</a></strong>() const<br>gets estimated output (over time)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t">sum_E_x_t_x_t</a></strong>() const<br>gets state-input covariance</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1">sum_E_xu_tm1_xu_tm1</a></strong>() const<br>gets state-input covariance (t-minus-1)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1">sum_E_xu_t_xu_tm1</a></strong>() const<br>gets single lag state-input covariance</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot">n_t_tot</a></strong>()<br>total number of time samples</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta">theta</a></strong>() const<br>gets parameters updated in M step</td> + </tr> + </tbody> +</table> +<h2 id="protected-functions"> + Protected Functions + <a class="anchor" href="#protected-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation">Expectation</a></strong>(bool force_common_initial =false)<br>Expectation step.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization">Maximization</a></strong>(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics">MaximizeDynamics</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq">MaximizeQ</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial">MaximizeInitial</a></strong>()</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeoutput">MaximizeOutput</a></strong>() =0</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizemeasurement">MaximizeMeasurement</a></strong>() =0</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth">Smooth</a></strong>(bool force_common_initial)<br>get smoothed estimates</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-recurseke">RecurseKe</a></strong>(Matrix &amp; Ke, Cube &amp; P_pre, Cube &amp; P_post, size_t t) =0<br>recursively update estimator gain Ke</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset">Reset</a></strong>()<br>reset to initial conditions</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars">InitVars</a></strong>()<br>Initializes the variables.</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta">UpdateTheta</a></strong>()<br>updates parameter list, theta</td> + </tr> + </tbody> +</table> +<h2 id="protected-attributes"> + Protected Attributes + <a class="anchor" href="#protected-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-">u_</a></strong> <br>input training data</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-">z_</a></strong> <br>measurement training data</td> + </tr> + <tr> + <td>std::vector&lt; Matrix &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-">x_</a></strong> <br>state estimate</td> + </tr> + <tr> + <td>std::vector&lt; Cube &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-">P_</a></strong> <br>state estimate cov</td> + </tr> + <tr> + <td>std::vector&lt; Cube &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-">P_t_tm1_</a></strong> <br>single-lag state covariance</td> + </tr> + <tr> + <td>std::vector&lt; Matrix &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-">y_</a></strong> <br>output estimate</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-">diag_y_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-">sum_E_x_t_x_t_</a></strong> <br>state covariance (current time)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-">sum_E_xu_tm1_xu_tm1_</a></strong> <br>state-input covariance (t-minus-1)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-">sum_E_xu_t_xu_tm1_</a></strong> <br>single lag state-input covariance</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-">fit_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-">theta_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-">n_trials_</a></strong> <br>number of input/output data sequences</td> + </tr> + <tr> + <td>std::vector&lt; size_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-">n_t_</a></strong> <br>number of time steps</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-">n_t_tot_</a></strong> <br>total number of time steps across trials</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>EM; +</span></span></code></pre></div><hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="em"> + <strong>EM</strong> + <a class="anchor" href="#em">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>EM() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="em-1"> + <strong>EM</strong> + <a class="anchor" href="#em-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>EM( +</span></span><span style="display:flex;"><span> size_t n_x, +</span></span><span style="display:flex;"><span> data_t dt, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> z_train +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_x</strong> number of states</li> +<li><strong>dt</strong> sample period</li> +<li><strong>u_train</strong> input training data</li> +<li><strong>z_train</strong> measurement training data</li> +</ul> +<hr> +<h3 id="em-2"> + <strong>EM</strong> + <a class="anchor" href="#em-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>EM( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Fit <span style="color:#f92672">&amp;</span> fit0, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> z_train +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>fit0</strong> initial fit</li> +<li><strong>u_train</strong> input training data</li> +<li><strong>z_train</strong> measurement training data</li> +</ul> +<hr> +<h3 id="heading"> + <strong>~EM</strong> + <a class="anchor" href="#heading">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#f92672">~</span>EM() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="run"> + <strong>Run</strong> + <a class="anchor" href="#run">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> Fit <span style="color:#f92672">&amp;</span> Run( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_dynamics <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_Q <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_init <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_output <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_measurement <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> size_t max_iter <span style="color:#f92672">=</span><span style="color:#ae81ff">100</span>, +</span></span><span style="display:flex;"><span> data_t tol <span style="color:#f92672">=</span><span style="color:#ae81ff">1e-2</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>calc_dynamics</strong> [optional] whether to calculate dynamics (A, B)</li> +<li><strong>calc_Q</strong> [optional] whether to calculate process noise covariance</li> +<li><strong>calc_init</strong> [optional] whether to calculate initial conditions</li> +<li><strong>calc_output</strong> [optional] whether to calculate output function</li> +<li><strong>calc_measurement</strong> [optional] whether to calculate parameters for measurement/observation law</li> +<li><strong>max_iter</strong> max number of iterations</li> +<li><strong>tol</strong> convergence tolerance (max fractional abs change)</li> +</ul> +<p><strong>Return</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a></p> +<hr> +<h3 id="returndata"> + <strong>ReturnData</strong> + <a class="anchor" href="#returndata">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> std<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span>, UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&gt;</span> ReturnData() +</span></span></code></pre></div><p><strong>Return</strong>: tuple(input data, output data)</p> +<hr> +<h3 id="x"> + <strong>x</strong> + <a class="anchor" href="#x">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Matrix <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> x() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="y"> + <strong>y</strong> + <a class="anchor" href="#y">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Matrix <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> y() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="sum_"> + <strong>sum_E_x_t_x_t</strong> + <a class="anchor" href="#sum_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> sum_E_x_t_x_t() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="sum_-1"> + <strong>sum_E_xu_tm1_xu_tm1</strong> + <a class="anchor" href="#sum_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> sum_E_xu_tm1_xu_tm1() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="sum_-2"> + <strong>sum_E_xu_t_xu_tm1</strong> + <a class="anchor" href="#sum_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> sum_E_xu_t_xu_tm1() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="n_"> + <strong>n_t_tot</strong> + <a class="anchor" href="#n_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t n_t_tot() +</span></span></code></pre></div><hr> +<h3 id="theta"> + <strong>theta</strong> + <a class="anchor" href="#theta">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> theta() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h2 id="protected-function-details"> + Protected Function Details + <a class="anchor" href="#protected-function-details">#</a> +</h2> +<h3 id="expectation"> + <strong>Expectation</strong> + <a class="anchor" href="#expectation">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Expectation( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> force_common_initial <span style="color:#f92672">=</span>false +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>force_common_initial</strong> whether to force common initial condition for all trials</li> +</ul> +<hr> +<h3 id="maximization"> + <strong>Maximization</strong> + <a class="anchor" href="#maximization">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Maximization( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_dynamics <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_Q <span style="color:#f92672">=</span>true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_init <span style="color:#f92672">=</span>false, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_output <span style="color:#f92672">=</span>false, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_measurement <span style="color:#f92672">=</span>false +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>calc_dynamics</strong> [optional] whether to caclulate dynamics (A, B)</li> +<li><strong>calc_Q</strong> [optional] whether to calculate process noise covariance</li> +<li><strong>calc_init</strong> [optional] whether to calculate initial conditions</li> +<li><strong>calc_output</strong> [optional] whether to calculate output function</li> +<li><strong>calc_measurement</strong> [optional] whether to calculate parameters for measurement/observation law</li> +</ul> +<hr> +<h3 id="maximizedynamics"> + <strong>MaximizeDynamics</strong> + <a class="anchor" href="#maximizedynamics">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> MaximizeDynamics() +</span></span></code></pre></div><hr> +<h3 id="maximizeq"> + <strong>MaximizeQ</strong> + <a class="anchor" href="#maximizeq">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> MaximizeQ() +</span></span></code></pre></div><hr> +<h3 id="maximizeinitial"> + <strong>MaximizeInitial</strong> + <a class="anchor" href="#maximizeinitial">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> MaximizeInitial() +</span></span></code></pre></div><hr> +<h3 id="maximizeoutput"> + <strong>MaximizeOutput</strong> + <a class="anchor" href="#maximizeoutput">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> MaximizeOutput() <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/#function-maximizeoutput">lds::gaussian::FitEM::MaximizeOutput</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/#function-maximizeoutput">lds::poisson::FitEM::MaximizeOutput</a></p> +<hr> +<h3 id="maximizemeasurement"> + <strong>MaximizeMeasurement</strong> + <a class="anchor" href="#maximizemeasurement">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> MaximizeMeasurement() <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/#function-maximizemeasurement">lds::gaussian::FitEM::MaximizeMeasurement</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/#function-maximizemeasurement">lds::poisson::FitEM::MaximizeMeasurement</a></p> +<hr> +<h3 id="smooth"> + <strong>Smooth</strong> + <a class="anchor" href="#smooth">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Smooth( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> force_common_initial +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>force_common_initial</strong> whether to force common initial conditions</li> +</ul> +<hr> +<h3 id="recurseke"> + <strong>RecurseKe</strong> + <a class="anchor" href="#recurseke">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> RecurseKe( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> Ke, +</span></span><span style="display:flex;"><span> Cube <span style="color:#f92672">&amp;</span> P_pre, +</span></span><span style="display:flex;"><span> Cube <span style="color:#f92672">&amp;</span> P_post, +</span></span><span style="display:flex;"><span> size_t t +</span></span><span style="display:flex;"><span>) <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>Ke</strong> estimator gain</li> +<li><strong>P_pre</strong> cov of predicted state est.</li> +<li><strong>P_post</strong> cov of postior sate est.</li> +<li><strong>t</strong> time</li> +</ul> +<p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/#function-recurseke">lds::gaussian::FitEM::RecurseKe</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/#function-recurseke">lds::poisson::FitEM::RecurseKe</a></p> +<hr> +<h3 id="reset"> + <strong>Reset</strong> + <a class="anchor" href="#reset">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Reset() +</span></span></code></pre></div><hr> +<h3 id="initvars"> + <strong>InitVars</strong> + <a class="anchor" href="#initvars">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> InitVars() +</span></span></code></pre></div><hr> +<h3 id="updatetheta"> + <strong>UpdateTheta</strong> + <a class="anchor" href="#updatetheta">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector UpdateTheta() +</span></span></code></pre></div><p><strong>Return</strong>: parameter list</p> +<hr> +<h2 id="protected-attribute-details"> + Protected Attribute Details + <a class="anchor" href="#protected-attribute-details">#</a> +</h2> +<h3 id="u_"> + <strong>u_</strong> + <a class="anchor" href="#u_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> u_; +</span></span></code></pre></div><hr> +<h3 id="z_"> + <strong>z_</strong> + <a class="anchor" href="#z_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> z_; +</span></span></code></pre></div><hr> +<h3 id="x_"> + <strong>x_</strong> + <a class="anchor" href="#x_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Matrix <span style="color:#f92672">&gt;</span> x_; +</span></span></code></pre></div><hr> +<h3 id="p_"> + <strong>P_</strong> + <a class="anchor" href="#p_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Cube <span style="color:#f92672">&gt;</span> P_; +</span></span></code></pre></div><hr> +<h3 id="p_-1"> + <strong>P_t_tm1_</strong> + <a class="anchor" href="#p_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Cube <span style="color:#f92672">&gt;</span> P_t_tm1_; +</span></span></code></pre></div><hr> +<h3 id="y_"> + <strong>y_</strong> + <a class="anchor" href="#y_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Matrix <span style="color:#f92672">&gt;</span> y_; +</span></span></code></pre></div><hr> +<h3 id="diag_"> + <strong>diag_y_</strong> + <a class="anchor" href="#diag_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix diag_y_; +</span></span></code></pre></div><hr> +<h3 id="sum_-3"> + <strong>sum_E_x_t_x_t_</strong> + <a class="anchor" href="#sum_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix sum_E_x_t_x_t_; +</span></span></code></pre></div><hr> +<h3 id="sum_-4"> + <strong>sum_E_xu_tm1_xu_tm1_</strong> + <a class="anchor" href="#sum_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix sum_E_xu_tm1_xu_tm1_; +</span></span></code></pre></div><hr> +<h3 id="sum_-5"> + <strong>sum_E_xu_t_xu_tm1_</strong> + <a class="anchor" href="#sum_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix sum_E_xu_t_xu_tm1_; +</span></span></code></pre></div><hr> +<h3 id="fit_"> + <strong>fit_</strong> + <a class="anchor" href="#fit_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Fit fit_; +</span></span></code></pre></div><hr> +<h3 id="theta_"> + <strong>theta_</strong> + <a class="anchor" href="#theta_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector theta_; +</span></span></code></pre></div><hr> +<h3 id="dt_"> + <strong>dt_</strong> + <a class="anchor" href="#dt_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t dt_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-1"> + <strong>n_u_</strong> + <a class="anchor" href="#n_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_u_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-2"> + <strong>n_x_</strong> + <a class="anchor" href="#n_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_x_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-3"> + <strong>n_y_</strong> + <a class="anchor" href="#n_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_y_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-4"> + <strong>n_trials_</strong> + <a class="anchor" href="#n_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_trials_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-5"> + <strong>n_t_</strong> + <a class="anchor" href="#n_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> size_t <span style="color:#f92672">&gt;</span> n_t_; +</span></span></code></pre></div><hr> +<h3 id="n_-6"> + <strong>n_t_tot_</strong> + <a class="anchor" href="#n_-6">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_t_tot_ {}; +</span></span></code></pre></div><hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsem">lds::EM</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#em"><strong>EM</strong></a></li> + <li><a href="#em-1"><strong>EM</strong></a></li> + <li><a href="#em-2"><strong>EM</strong></a></li> + <li><a href="#heading"><strong>~EM</strong></a></li> + <li><a href="#run"><strong>Run</strong></a></li> + <li><a href="#returndata"><strong>ReturnData</strong></a></li> + <li><a href="#x"><strong>x</strong></a></li> + <li><a href="#y"><strong>y</strong></a></li> + <li><a href="#sum_"><strong>sum_E_x_t_x_t</strong></a></li> + <li><a href="#sum_-1"><strong>sum_E_xu_tm1_xu_tm1</strong></a></li> + <li><a href="#sum_-2"><strong>sum_E_xu_t_xu_tm1</strong></a></li> + <li><a href="#n_"><strong>n_t_tot</strong></a></li> + <li><a href="#theta"><strong>theta</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#expectation"><strong>Expectation</strong></a></li> + <li><a href="#maximization"><strong>Maximization</strong></a></li> + <li><a href="#maximizedynamics"><strong>MaximizeDynamics</strong></a></li> + <li><a href="#maximizeq"><strong>MaximizeQ</strong></a></li> + <li><a href="#maximizeinitial"><strong>MaximizeInitial</strong></a></li> + <li><a href="#maximizeoutput"><strong>MaximizeOutput</strong></a></li> + <li><a href="#maximizemeasurement"><strong>MaximizeMeasurement</strong></a></li> + <li><a href="#smooth"><strong>Smooth</strong></a></li> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + <li><a href="#reset"><strong>Reset</strong></a></li> + <li><a href="#initvars"><strong>InitVars</strong></a></li> + <li><a href="#updatetheta"><strong>UpdateTheta</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#u_"><strong>u_</strong></a></li> + <li><a href="#z_"><strong>z_</strong></a></li> + <li><a href="#x_"><strong>x_</strong></a></li> + <li><a href="#p_"><strong>P_</strong></a></li> + <li><a href="#p_-1"><strong>P_t_tm1_</strong></a></li> + <li><a href="#y_"><strong>y_</strong></a></li> + <li><a href="#diag_"><strong>diag_y_</strong></a></li> + <li><a href="#sum_-3"><strong>sum_E_x_t_x_t_</strong></a></li> + <li><a href="#sum_-4"><strong>sum_E_xu_tm1_xu_tm1_</strong></a></li> + <li><a href="#sum_-5"><strong>sum_E_xu_t_xu_tm1_</strong></a></li> + <li><a href="#fit_"><strong>fit_</strong></a></li> + <li><a href="#theta_"><strong>theta_</strong></a></li> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#n_-1"><strong>n_u_</strong></a></li> + <li><a href="#n_-2"><strong>n_x_</strong></a></li> + <li><a href="#n_-3"><strong>n_y_</strong></a></li> + <li><a href="#n_-4"><strong>n_trials_</strong></a></li> + <li><a href="#n_-5"><strong>n_t_</strong></a></li> + <li><a href="#n_-6"><strong>n_t_tot_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_fit/index.html b/docs/docs/api/classes/classlds_1_1_fit/index.html new file mode 100644 index 00000000..5b8c45d8 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_fit/index.html @@ -0,0 +1,907 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="LDS Fit Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::Fit"> + <meta property="og:description" content="LDS Fit Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::Fit | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::Fit</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsfit">lds::Fit</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#fit"><strong>Fit</strong></a></li> + <li><a href="#fit-1"><strong>Fit</strong></a></li> + <li><a href="#heading"><strong>~Fit</strong></a></li> + <li><a href="#n_"><strong>n_u</strong></a></li> + <li><a href="#n_-1"><strong>n_x</strong></a></li> + <li><a href="#n_-2"><strong>n_y</strong></a></li> + <li><a href="#dt"><strong>dt</strong></a></li> + <li><a href="#a"><strong>A</strong></a></li> + <li><a href="#b"><strong>B</strong></a></li> + <li><a href="#g"><strong>g</strong></a></li> + <li><a href="#m"><strong>m</strong></a></li> + <li><a href="#q"><strong>Q</strong></a></li> + <li><a href="#x0"><strong>x0</strong></a></li> + <li><a href="#p0"><strong>P0</strong></a></li> + <li><a href="#c"><strong>C</strong></a></li> + <li><a href="#d"><strong>d</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + <li><a href="#set_"><strong>set_A</strong></a></li> + <li><a href="#set_-1"><strong>set_B</strong></a></li> + <li><a href="#set_-2"><strong>set_g</strong></a></li> + <li><a href="#set_-3"><strong>set_m</strong></a></li> + <li><a href="#set_-4"><strong>set_Q</strong></a></li> + <li><a href="#set_-5"><strong>set_R</strong></a></li> + <li><a href="#set_-6"><strong>set_x0</strong></a></li> + <li><a href="#set_-7"><strong>set_P0</strong></a></li> + <li><a href="#set_-8"><strong>set_C</strong></a></li> + <li><a href="#set_-9"><strong>set_d</strong></a></li> + <li><a href="#f"><strong>f</strong></a></li> + <li><a href="#f-1"><strong>f</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#a_"><strong>A_</strong></a></li> + <li><a href="#b_"><strong>B_</strong></a></li> + <li><a href="#g_"><strong>g_</strong></a></li> + <li><a href="#m_"><strong>m_</strong></a></li> + <li><a href="#q_"><strong>Q_</strong></a></li> + <li><a href="#c_"><strong>C_</strong></a></li> + <li><a href="#d_"><strong>d_</strong></a></li> + <li><a href="#r_"><strong>R_</strong></a></li> + <li><a href="#x0_"><strong>x0_</strong></a></li> + <li><a href="#p0_"><strong>P0_</strong></a></li> + <li><a href="#n_-3"><strong>n_u_</strong></a></li> + <li><a href="#n_-4"><strong>n_x_</strong></a></li> + <li><a href="#n_-5"><strong>n_y_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsfit"> + lds::Fit + <a class="anchor" href="#ldsfit">#</a> +</h1> +<p>LDS <a href="">Fit</a> Type. +<br /> <code>#include &lt;lds_fit.h&gt;</code></p> +<p>Inherited by <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">lds::gaussian::Fit</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">lds::poisson::Fit</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-fit">Fit</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-fit">Fit</a></strong>(size_t n_u, size_t n_x, size_t n_y, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a>.</td> + </tr> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-~fit">~Fit</a></strong>() =default</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-u">n_u</a></strong>() const<br>gets number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-x">n_x</a></strong>() const<br>gets number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-y">n_y</a></strong>() const<br>gets number of outputs</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-dt">dt</a></strong>() const<br>gets sample period</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-a">A</a></strong>() const<br>gets state matrix</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-b">B</a></strong>() const<br>gets input matrix</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-g">g</a></strong>() const<br>gets input gain</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-m">m</a></strong>() const<br>gets process disturbance</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-q">Q</a></strong>() const<br>gets process noise covariance</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-x0">x0</a></strong>() const<br>gets initial state estimate</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-p0">P0</a></strong>() const<br>gets covariance of initial state estimate</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-c">C</a></strong>() const<br>gets output matrix</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-d">d</a></strong>() const<br>gets output bias</td> + </tr> + <tr> + <td>virtual const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-r">R</a></strong>() const =0</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-a">set_A</a></strong>(const Matrix &amp; A)<br>sets state matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-b">set_B</a></strong>(const Matrix &amp; B)<br>sets input matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-g">set_g</a></strong>(const Vector &amp; g)<br>sets input gain/conversion factor</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-m">set_m</a></strong>(const Vector &amp; m)<br>sets process disturbance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-q">set_Q</a></strong>(const Matrix &amp; Q)<br>sets process noise covariance</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-r">set_R</a></strong>(const Matrix &amp; R) =0<br>sets output noise covariance (if any)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-x0">set_x0</a></strong>(const Vector &amp; x0)<br>sets initial state estimate</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-p0">set_P0</a></strong>(const Matrix &amp; P0)<br>sets initial state estimate covariance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-c">set_C</a></strong>(const Matrix &amp; C)<br>sets output matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-d">set_d</a></strong>(const Vector &amp; d)<br>sets output bias</td> + </tr> + <tr> + <td>View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f">f</a></strong>(Matrix &amp; x, const Matrix &amp; u, size_t t)<br>system dynamics function</td> + </tr> + <tr> + <td>View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f">f</a></strong>(Matrix &amp; x_pre, const Matrix &amp; x_post, const Matrix &amp; u, size_t t)<br>system dynamics function</td> + </tr> + <tr> + <td>virtual View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-h">h</a></strong>(Matrix &amp; y, const Matrix &amp; x, size_t t) =0<br>output function</td> + </tr> + </tbody> +</table> +<h2 id="protected-attributes"> + Protected Attributes + <a class="anchor" href="#protected-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-a-">A_</a></strong> <br>state matrix</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-b-">B_</a></strong> <br>input matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-g-">g_</a></strong> <br>input gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-m-">m_</a></strong> <br>process noise mean</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-q-">Q_</a></strong> <br>process noise cov</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-c-">C_</a></strong> <br>output matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-d-">d_</a></strong> <br>output bias</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-r-">R_</a></strong> <br>measurement noise</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-x0-">x0_</a></strong> <br>initial state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-p0-">P0_</a></strong> <br>initial covar</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="fit"> + <strong>Fit</strong> + <a class="anchor" href="#fit">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Fit() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="fit-1"> + <strong>Fit</strong> + <a class="anchor" href="#fit-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Fit( +</span></span><span style="display:flex;"><span> size_t n_u, +</span></span><span style="display:flex;"><span> size_t n_x, +</span></span><span style="display:flex;"><span> size_t n_y, +</span></span><span style="display:flex;"><span> data_t dt +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_u</strong> number of inputs</li> +<li><strong>n_x</strong> number of states</li> +<li><strong>n_y</strong> number of outputs</li> +<li><strong>dt</strong> sample period</li> +</ul> +<hr> +<h3 id="heading"> + <strong>~Fit</strong> + <a class="anchor" href="#heading">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#f92672">~</span>Fit() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="n_"> + <strong>n_u</strong> + <a class="anchor" href="#n_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t n_u() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="n_-1"> + <strong>n_x</strong> + <a class="anchor" href="#n_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t n_x() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="n_-2"> + <strong>n_y</strong> + <a class="anchor" href="#n_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t n_y() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="dt"> + <strong>dt</strong> + <a class="anchor" href="#dt">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> data_t dt() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="a"> + <strong>A</strong> + <a class="anchor" href="#a">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> A() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="b"> + <strong>B</strong> + <a class="anchor" href="#b">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> B() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="g"> + <strong>g</strong> + <a class="anchor" href="#g">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> g() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="m"> + <strong>m</strong> + <a class="anchor" href="#m">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> m() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="q"> + <strong>Q</strong> + <a class="anchor" href="#q">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="x0"> + <strong>x0</strong> + <a class="anchor" href="#x0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x0() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="p0"> + <strong>P0</strong> + <a class="anchor" href="#p0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P0() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="c"> + <strong>C</strong> + <a class="anchor" href="#c">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> C() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="d"> + <strong>d</strong> + <a class="anchor" href="#d">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> d() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="r"> + <strong>R</strong> + <a class="anchor" href="#r">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-r">lds::gaussian::Fit::R</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-r">lds::poisson::Fit::R</a></p> +<hr> +<h3 id="set_"> + <strong>set_A</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_A( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> A +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-1"> + <strong>set_B</strong> + <a class="anchor" href="#set_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_B( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> B +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-2"> + <strong>set_g</strong> + <a class="anchor" href="#set_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_g( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> g +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-3"> + <strong>set_m</strong> + <a class="anchor" href="#set_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_m( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> m +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-4"> + <strong>set_Q</strong> + <a class="anchor" href="#set_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Q( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-5"> + <strong>set_R</strong> + <a class="anchor" href="#set_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_R( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R +</span></span><span style="display:flex;"><span>) <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-set-r">lds::gaussian::Fit::set_R</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-set-r">lds::poisson::Fit::set_R</a></p> +<hr> +<h3 id="set_-6"> + <strong>set_x0</strong> + <a class="anchor" href="#set_-6">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_x0( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-7"> + <strong>set_P0</strong> + <a class="anchor" href="#set_-7">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_P0( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-8"> + <strong>set_C</strong> + <a class="anchor" href="#set_-8">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_C( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> C +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-9"> + <strong>set_d</strong> + <a class="anchor" href="#set_-9">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_d( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> d +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="f"> + <strong>f</strong> + <a class="anchor" href="#f">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> View f( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> x, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> u, +</span></span><span style="display:flex;"><span> size_t t +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>x</strong> state estimate (over time)</li> +<li><strong>u</strong> input (over time)</li> +<li><strong>t</strong> time index</li> +</ul> +<p><strong>Return</strong>: view of updated state</p> +<hr> +<h3 id="f-1"> + <strong>f</strong> + <a class="anchor" href="#f-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> View f( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> x_pre, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> x_post, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> u, +</span></span><span style="display:flex;"><span> size_t t +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>x_pre</strong> predicted state est.</li> +<li><strong>x_post</strong> posterior state est.</li> +<li><strong>u</strong> input (over time)</li> +<li><strong>t</strong> time index</li> +</ul> +<p><strong>Return</strong>: view of predicted state</p> +<hr> +<h3 id="h"> + <strong>h</strong> + <a class="anchor" href="#h">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> View h( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> y, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> x, +</span></span><span style="display:flex;"><span> size_t t +</span></span><span style="display:flex;"><span>) <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>y</strong> output estimate (over time)</li> +<li><strong>x</strong> state estimate (over time)</li> +<li><strong>t</strong> time index</li> +</ul> +<p><strong>Return</strong>: output</p> +<p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-h">lds::gaussian::Fit::h</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-h">lds::poisson::Fit::h</a></p> +<hr> +<h2 id="protected-attribute-details"> + Protected Attribute Details + <a class="anchor" href="#protected-attribute-details">#</a> +</h2> +<h3 id="dt_"> + <strong>dt_</strong> + <a class="anchor" href="#dt_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t dt_ {}; +</span></span></code></pre></div><hr> +<h3 id="a_"> + <strong>A_</strong> + <a class="anchor" href="#a_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix A_; +</span></span></code></pre></div><hr> +<h3 id="b_"> + <strong>B_</strong> + <a class="anchor" href="#b_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix B_; +</span></span></code></pre></div><hr> +<h3 id="g_"> + <strong>g_</strong> + <a class="anchor" href="#g_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector g_; +</span></span></code></pre></div><hr> +<h3 id="m_"> + <strong>m_</strong> + <a class="anchor" href="#m_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector m_; +</span></span></code></pre></div><hr> +<h3 id="q_"> + <strong>Q_</strong> + <a class="anchor" href="#q_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Q_; +</span></span></code></pre></div><hr> +<h3 id="c_"> + <strong>C_</strong> + <a class="anchor" href="#c_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix C_; +</span></span></code></pre></div><hr> +<h3 id="d_"> + <strong>d_</strong> + <a class="anchor" href="#d_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector d_; +</span></span></code></pre></div><hr> +<h3 id="r_"> + <strong>R_</strong> + <a class="anchor" href="#r_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix R_; +</span></span></code></pre></div><hr> +<h3 id="x0_"> + <strong>x0_</strong> + <a class="anchor" href="#x0_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector x0_; +</span></span></code></pre></div><hr> +<h3 id="p0_"> + <strong>P0_</strong> + <a class="anchor" href="#p0_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix P0_; +</span></span></code></pre></div><hr> +<h3 id="n_-3"> + <strong>n_u_</strong> + <a class="anchor" href="#n_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_u_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-4"> + <strong>n_x_</strong> + <a class="anchor" href="#n_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_x_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-5"> + <strong>n_y_</strong> + <a class="anchor" href="#n_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_y_ {}; +</span></span></code></pre></div><hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsfit">lds::Fit</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#fit"><strong>Fit</strong></a></li> + <li><a href="#fit-1"><strong>Fit</strong></a></li> + <li><a href="#heading"><strong>~Fit</strong></a></li> + <li><a href="#n_"><strong>n_u</strong></a></li> + <li><a href="#n_-1"><strong>n_x</strong></a></li> + <li><a href="#n_-2"><strong>n_y</strong></a></li> + <li><a href="#dt"><strong>dt</strong></a></li> + <li><a href="#a"><strong>A</strong></a></li> + <li><a href="#b"><strong>B</strong></a></li> + <li><a href="#g"><strong>g</strong></a></li> + <li><a href="#m"><strong>m</strong></a></li> + <li><a href="#q"><strong>Q</strong></a></li> + <li><a href="#x0"><strong>x0</strong></a></li> + <li><a href="#p0"><strong>P0</strong></a></li> + <li><a href="#c"><strong>C</strong></a></li> + <li><a href="#d"><strong>d</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + <li><a href="#set_"><strong>set_A</strong></a></li> + <li><a href="#set_-1"><strong>set_B</strong></a></li> + <li><a href="#set_-2"><strong>set_g</strong></a></li> + <li><a href="#set_-3"><strong>set_m</strong></a></li> + <li><a href="#set_-4"><strong>set_Q</strong></a></li> + <li><a href="#set_-5"><strong>set_R</strong></a></li> + <li><a href="#set_-6"><strong>set_x0</strong></a></li> + <li><a href="#set_-7"><strong>set_P0</strong></a></li> + <li><a href="#set_-8"><strong>set_C</strong></a></li> + <li><a href="#set_-9"><strong>set_d</strong></a></li> + <li><a href="#f"><strong>f</strong></a></li> + <li><a href="#f-1"><strong>f</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#a_"><strong>A_</strong></a></li> + <li><a href="#b_"><strong>B_</strong></a></li> + <li><a href="#g_"><strong>g_</strong></a></li> + <li><a href="#m_"><strong>m_</strong></a></li> + <li><a href="#q_"><strong>Q_</strong></a></li> + <li><a href="#c_"><strong>C_</strong></a></li> + <li><a href="#d_"><strong>d_</strong></a></li> + <li><a href="#r_"><strong>R_</strong></a></li> + <li><a href="#x0_"><strong>x0_</strong></a></li> + <li><a href="#p0_"><strong>P0_</strong></a></li> + <li><a href="#n_-3"><strong>n_u_</strong></a></li> + <li><a href="#n_-4"><strong>n_x_</strong></a></li> + <li><a href="#n_-5"><strong>n_y_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_s_s_i_d/index.html b/docs/docs/api/classes/classlds_1_1_s_s_i_d/index.html new file mode 100644 index 00000000..ba0faa6e --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_s_s_i_d/index.html @@ -0,0 +1,896 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + lds::SSID + # + +More&hellip; +Inherited by lds::gaussian::FitSSID, lds::poisson::FitSSID + + Public Functions + # + + + + + + Name + + + + + + SSID() =defaultConstructs a new SSIDFit type. + + + + SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train, const Vector &amp; d =Vector(1).fill(-kInf))Constructs a new SSIDFit type. + + + std::tuple&lt; Fit, Vector &gt; + Run(SSIDWt ssid_wt)Runs fitting by subspace identification (SSID) + + + std::tuple&lt; UniformMatrixList&lt; kMatFreeDim2 &gt;, UniformMatrixList&lt; kMatFreeDim2 &gt; &gt; + ReturnData()Returns the I/O data to caller. + + + + + Protected Functions + # + + + + + + Name + + + + + void + CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)Using periods of silence in inputs (u), calculates the output \ bias (d) + + + void + CreateHankelDataMat()Creates the block-hankel I/O data matrix. + + + virtual void + DecomposeData() =0Decompose data to lower-triangular matrix (used in Solve) + + + void + CalcSVD(SSIDWt wt)performs the singular value decomposition (SVD) + + + void + Solve(data_t wt_dc)solves for LDS parameters + + + void + RecomputeExtObs()recompute extended observability matrix from estimates of A, C + + + + + Protected Attributes + # + + + + + + Name + + + + + UniformMatrixList&lt; kMatFreeDim2 &gt; + u_ input training data + + + UniformMatrixList&lt; kMatFreeDim2 &gt; + z_ measurement training data + + + Matrix + D_ block-Hankel I/O data matrix + + + Fit + fit_ fit + + + Matrix + g_dc_ I/O gain @ DC. + + + data_t + dt_ sample period + + + size_t + n_u_ number of inputs + + + size_t + n_x_ number of states + + + size_t + n_y_ number of outputs + + + size_t + n_h_ + + + size_t + n_trials_ number of input/output data sequences + + + std::vector&lt; size_t &gt; + n_t_ number of time steps + + + size_t + n_t_tot_ total number of time steps across trials + + + Matrix + L_ lower triangle decomp of covariance matrix + + + Vector + s_ singular values + + + Matrix + ext_obs_t_ extended observability matrix + + + + + Detailed Description + # + +template &lt;typename Fit &gt; +class lds::SSID; + + + + Public Function Details + # + + + SSID + # + +SSID() =default + + + SSID + # + +SSID( + size_t n_x, + size_t n_h, + data_t dt, + UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, + UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train, + const Vector &amp; d =Vector(1).fill(-kInf) +) +Parameters:"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::SSID"> + <meta property="og:description" content="lds::SSID # More… +Inherited by lds::gaussian::FitSSID, lds::poisson::FitSSID +Public Functions # Name SSID() =default +Constructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train, const Vector &amp; d =Vector(1).fill(-kInf)) +Constructs a new SSIDFit type. std::tuple&lt; Fit, Vector &gt; Run(SSIDWt ssid_wt) +Runs fitting by subspace identification (SSID) std::tuple&lt; UniformMatrixList&lt; kMatFreeDim2 &gt;, UniformMatrixList&lt; kMatFreeDim2 &gt; &gt; ReturnData() +Returns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001) +Using periods of silence in inputs (u), calculates the output \ bias (d) void CreateHankelDataMat() +Creates the block-hankel I/O data matrix. virtual void DecomposeData() =0 +Decompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt) +performs the singular value decomposition (SVD) void Solve(data_t wt_dc) +solves for LDS parameters void RecomputeExtObs() +recompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList&lt; kMatFreeDim2 &gt; u_ input training data UniformMatrixList&lt; kMatFreeDim2 &gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector&lt; size_t &gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template &lt;typename Fit &gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; u_train, UniformMatrixList&lt; kMatFreeDim2 &gt; &amp;&amp; z_train, const Vector &amp; d =Vector(1).fill(-kInf) ) Parameters:"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::SSID | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::SSID</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsssid">lds::SSID</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#ssid"><strong>SSID</strong></a></li> + <li><a href="#ssid-1"><strong>SSID</strong></a></li> + <li><a href="#run"><strong>Run</strong></a></li> + <li><a href="#returndata"><strong>ReturnData</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#calcd"><strong>CalcD</strong></a></li> + <li><a href="#createhankeldatamat"><strong>CreateHankelDataMat</strong></a></li> + <li><a href="#decomposedata"><strong>DecomposeData</strong></a></li> + <li><a href="#calcsvd"><strong>CalcSVD</strong></a></li> + <li><a href="#solve"><strong>Solve</strong></a></li> + <li><a href="#recomputeextobs"><strong>RecomputeExtObs</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#u_"><strong>u_</strong></a></li> + <li><a href="#z_"><strong>z_</strong></a></li> + <li><a href="#d_"><strong>D_</strong></a></li> + <li><a href="#fit_"><strong>fit_</strong></a></li> + <li><a href="#g_"><strong>g_dc_</strong></a></li> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#n_"><strong>n_u_</strong></a></li> + <li><a href="#n_-1"><strong>n_x_</strong></a></li> + <li><a href="#n_-2"><strong>n_y_</strong></a></li> + <li><a href="#n_-3"><strong>n_h_</strong></a></li> + <li><a href="#n_-4"><strong>n_trials_</strong></a></li> + <li><a href="#n_-5"><strong>n_t_</strong></a></li> + <li><a href="#n_-6"><strong>n_t_tot_</strong></a></li> + <li><a href="#l_"><strong>L_</strong></a></li> + <li><a href="#s_"><strong>s_</strong></a></li> + <li><a href="#ext_"><strong>ext_obs_t_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsssid"> + lds::SSID + <a class="anchor" href="#ldsssid">#</a> +</h1> +<p><a href="#detailed-description">More&hellip;</a></p> +<p>Inherited by <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/">lds::gaussian::FitSSID</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/">lds::poisson::FitSSID</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid">SSID</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid">SSID</a></strong>(size_t n_x, size_t n_h, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train, const Vector &amp; d =Vector(1).fill(-kInf))<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> type.</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a>, Vector &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run">Run</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt">SSIDWt</a> ssid_wt)<br>Runs fitting by subspace identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>)</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata">ReturnData</a></strong>()<br>Returns the I/O data to caller.</td> + </tr> + </tbody> +</table> +<h2 id="protected-functions"> + Protected Functions + <a class="anchor" href="#protected-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd">CalcD</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> t_silence =0.1, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat">CreateHankelDataMat</a></strong>()<br>Creates the block-hankel I/O data matrix.</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-decomposedata">DecomposeData</a></strong>() =0<br>Decompose data to lower-triangular matrix (used in Solve)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd">CalcSVD</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt">SSIDWt</a> wt)<br>performs the singular value decomposition (SVD)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve">Solve</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> wt_dc)<br>solves for LDS parameters</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs">RecomputeExtObs</a></strong>()<br>recompute extended observability matrix from estimates of A, C</td> + </tr> + </tbody> +</table> +<h2 id="protected-attributes"> + Protected Attributes + <a class="anchor" href="#protected-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-">u_</a></strong> <br>input training data</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-">z_</a></strong> <br>measurement training data</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-">D_</a></strong> <br>block-Hankel I/O data matrix</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-">fit_</a></strong> <br>fit</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-">g_dc_</a></strong> <br>I/O gain @ DC.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-">n_h_</a></strong></td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-">n_trials_</a></strong> <br>number of input/output data sequences</td> + </tr> + <tr> + <td>std::vector&lt; size_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-">n_t_</a></strong> <br>number of time steps</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-">n_t_tot_</a></strong> <br>total number of time steps across trials</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-">L_</a></strong> <br>lower triangle decomp of covariance matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-">s_</a></strong> <br>singular values</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-">ext_obs_t_</a></strong> <br>extended observability matrix</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>SSID; +</span></span></code></pre></div><hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="ssid"> + <strong>SSID</strong> + <a class="anchor" href="#ssid">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>SSID() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="ssid-1"> + <strong>SSID</strong> + <a class="anchor" href="#ssid-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>SSID( +</span></span><span style="display:flex;"><span> size_t n_x, +</span></span><span style="display:flex;"><span> size_t n_h, +</span></span><span style="display:flex;"><span> data_t dt, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> z_train, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> d <span style="color:#f92672">=</span>Vector(<span style="color:#ae81ff">1</span>).fill(<span style="color:#f92672">-</span>kInf) +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_x</strong> number of states</li> +<li><strong>n_h</strong> size of block-hankel data matrix</li> +<li><strong>dt</strong> sample period</li> +<li><strong>u_train</strong> input training data</li> +<li><strong>z_train</strong> measurement training data</li> +<li><strong>d</strong> output bias</li> +</ul> +<hr> +<h3 id="run"> + <strong>Run</strong> + <a class="anchor" href="#run">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span> Fit, Vector <span style="color:#f92672">&gt;</span> Run( +</span></span><span style="display:flex;"><span> SSIDWt ssid_wt +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>ssid_wt</strong> weight for singular value decomp</li> +</ul> +<p><strong>Return</strong>: tuple (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a>, singular values)</p> +<hr> +<h3 id="returndata"> + <strong>ReturnData</strong> + <a class="anchor" href="#returndata">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> std<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span>, UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&gt;</span> ReturnData() +</span></span></code></pre></div><p><strong>Return</strong>: tuple(input data, output data)</p> +<hr> +<h2 id="protected-function-details"> + Protected Function Details + <a class="anchor" href="#protected-function-details">#</a> +</h2> +<h3 id="calcd"> + <strong>CalcD</strong> + <a class="anchor" href="#calcd">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> CalcD( +</span></span><span style="display:flex;"><span> data_t t_silence <span style="color:#f92672">=</span><span style="color:#ae81ff">0.1</span>, +</span></span><span style="display:flex;"><span> data_t thresh_silence <span style="color:#f92672">=</span><span style="color:#ae81ff">0.001</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>t_silence</strong> threshold on period of time that qualifies as &ldquo;silence&rdquo;</li> +<li><strong>thresh_silence</strong> threshold on input amplitude u that qualifies as &ldquo;silence&rdquo;</li> +</ul> +<hr> +<h3 id="createhankeldatamat"> + <strong>CreateHankelDataMat</strong> + <a class="anchor" href="#createhankeldatamat">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> CreateHankelDataMat() +</span></span></code></pre></div><p>Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.</p> +<hr> +<h3 id="decomposedata"> + <strong>DecomposeData</strong> + <a class="anchor" href="#decomposedata">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> DecomposeData() <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/#function-decomposedata">lds::gaussian::FitSSID::DecomposeData</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/#function-decomposedata">lds::poisson::FitSSID::DecomposeData</a></p> +<hr> +<h3 id="calcsvd"> + <strong>CalcSVD</strong> + <a class="anchor" href="#calcsvd">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> CalcSVD( +</span></span><span style="display:flex;"><span> SSIDWt wt +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>ssid_wt</strong> weight for SVD</li> +</ul> +<hr> +<h3 id="solve"> + <strong>Solve</strong> + <a class="anchor" href="#solve">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Solve( +</span></span><span style="display:flex;"><span> data_t wt_dc +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>wt_dc</strong> weight placed on getting correct DC I/O gain</li> +</ul> +<hr> +<h3 id="recomputeextobs"> + <strong>RecomputeExtObs</strong> + <a class="anchor" href="#recomputeextobs">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> RecomputeExtObs() +</span></span></code></pre></div><hr> +<h2 id="protected-attribute-details"> + Protected Attribute Details + <a class="anchor" href="#protected-attribute-details">#</a> +</h2> +<h3 id="u_"> + <strong>u_</strong> + <a class="anchor" href="#u_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> u_; +</span></span></code></pre></div><hr> +<h3 id="z_"> + <strong>z_</strong> + <a class="anchor" href="#z_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> z_; +</span></span></code></pre></div><hr> +<h3 id="d_"> + <strong>D_</strong> + <a class="anchor" href="#d_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix D_; +</span></span></code></pre></div><hr> +<h3 id="fit_"> + <strong>fit_</strong> + <a class="anchor" href="#fit_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Fit fit_; +</span></span></code></pre></div><hr> +<h3 id="g_"> + <strong>g_dc_</strong> + <a class="anchor" href="#g_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix g_dc_; +</span></span></code></pre></div><hr> +<h3 id="dt_"> + <strong>dt_</strong> + <a class="anchor" href="#dt_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t dt_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_"> + <strong>n_u_</strong> + <a class="anchor" href="#n_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_u_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-1"> + <strong>n_x_</strong> + <a class="anchor" href="#n_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_x_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-2"> + <strong>n_y_</strong> + <a class="anchor" href="#n_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_y_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-3"> + <strong>n_h_</strong> + <a class="anchor" href="#n_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_h_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-4"> + <strong>n_trials_</strong> + <a class="anchor" href="#n_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_trials_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-5"> + <strong>n_t_</strong> + <a class="anchor" href="#n_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> size_t <span style="color:#f92672">&gt;</span> n_t_; +</span></span></code></pre></div><hr> +<h3 id="n_-6"> + <strong>n_t_tot_</strong> + <a class="anchor" href="#n_-6">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_t_tot_ {}; +</span></span></code></pre></div><hr> +<h3 id="l_"> + <strong>L_</strong> + <a class="anchor" href="#l_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix L_; +</span></span></code></pre></div><hr> +<h3 id="s_"> + <strong>s_</strong> + <a class="anchor" href="#s_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector s_; +</span></span></code></pre></div><hr> +<h3 id="ext_"> + <strong>ext_obs_t_</strong> + <a class="anchor" href="#ext_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix ext_obs_t_; +</span></span></code></pre></div><hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsssid">lds::SSID</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#ssid"><strong>SSID</strong></a></li> + <li><a href="#ssid-1"><strong>SSID</strong></a></li> + <li><a href="#run"><strong>Run</strong></a></li> + <li><a href="#returndata"><strong>ReturnData</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#calcd"><strong>CalcD</strong></a></li> + <li><a href="#createhankeldatamat"><strong>CreateHankelDataMat</strong></a></li> + <li><a href="#decomposedata"><strong>DecomposeData</strong></a></li> + <li><a href="#calcsvd"><strong>CalcSVD</strong></a></li> + <li><a href="#solve"><strong>Solve</strong></a></li> + <li><a href="#recomputeextobs"><strong>RecomputeExtObs</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#u_"><strong>u_</strong></a></li> + <li><a href="#z_"><strong>z_</strong></a></li> + <li><a href="#d_"><strong>D_</strong></a></li> + <li><a href="#fit_"><strong>fit_</strong></a></li> + <li><a href="#g_"><strong>g_dc_</strong></a></li> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#n_"><strong>n_u_</strong></a></li> + <li><a href="#n_-1"><strong>n_x_</strong></a></li> + <li><a href="#n_-2"><strong>n_y_</strong></a></li> + <li><a href="#n_-3"><strong>n_h_</strong></a></li> + <li><a href="#n_-4"><strong>n_trials_</strong></a></li> + <li><a href="#n_-5"><strong>n_t_</strong></a></li> + <li><a href="#n_-6"><strong>n_t_tot_</strong></a></li> + <li><a href="#l_"><strong>L_</strong></a></li> + <li><a href="#s_"><strong>s_</strong></a></li> + <li><a href="#ext_"><strong>ext_obs_t_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_switched_controller/index.html b/docs/docs/api/classes/classlds_1_1_switched_controller/index.html new file mode 100644 index 00000000..135042e1 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_switched_controller/index.html @@ -0,0 +1,846 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="SwitchedController Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::SwitchedController"> + <meta property="og:description" content="SwitchedController Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::SwitchedController | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::SwitchedController</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsswitchedcontroller">lds::SwitchedController</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#switchedcontroller"><strong>SwitchedController</strong></a></li> + <li><a href="#switchedcontroller-1"><strong>SwitchedController</strong></a></li> + <li><a href="#switchedcontroller-2"><strong>SwitchedController</strong></a></li> + <li><a href="#switch"><strong>Switch</strong></a></li> + <li><a href="#set_"><strong>set_Kc</strong></a></li> + <li><a href="#set_-1"><strong>set_Kc</strong></a></li> + <li><a href="#set_-2"><strong>set_Kc_inty</strong></a></li> + <li><a href="#set_-3"><strong>set_Kc_inty</strong></a></li> + <li><a href="#set_-4"><strong>set_Kc_u</strong></a></li> + <li><a href="#set_-5"><strong>set_Kc_u</strong></a></li> + <li><a href="#set_-6"><strong>set_g_design</strong></a></li> + <li><a href="#set_-7"><strong>set_g_design</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#systems_"><strong>systems_</strong></a></li> + <li><a href="#n_"><strong>n_sys_</strong></a></li> + <li><a href="#idx_"><strong>idx_</strong></a></li> + <li><a href="#kc_"><strong>Kc_list_</strong></a></li> + <li><a href="#kc_-1"><strong>Kc_inty_list_</strong></a></li> + <li><a href="#kc_-2"><strong>Kc_u_list_</strong></a></li> + <li><a href="#g_"><strong>g_design_list_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsswitchedcontroller"> + lds::SwitchedController + <a class="anchor" href="#ldsswitchedcontroller">#</a> +</h1> +<p><a href="">SwitchedController</a> Type. <a href="#detailed-description">More&hellip;</a></p> +<p><br /> <code>#include &lt;lds_sctrl.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></p> +<p>Inherited by <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/">lds::gaussian::SwitchedController</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/">lds::poisson::SwitchedController</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>(const std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp; systems, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>(std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp;&amp; systems, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a> (moves systems).</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switch">Switch</a></strong>(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc">set_Kc</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc)<br>sets state feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc">set_Kc</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc)<br>sets state feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc_inty)<br>sets integral feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc_inty)<br>sets integral feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u">set_Kc_u</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc_u)<br>sets input feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u">set_Kc_u</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc_u)<br>sets input feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design">set_g_design</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp; g)<br>sets input gain used during controller design</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design">set_g_design</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp;&amp; g)<br>sets input gain used during controller design (moving)</td> + </tr> + </tbody> +</table> +<h2 id="protected-attributes"> + Protected Attributes + <a class="anchor" href="#protected-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-systems-">systems_</a></strong> <br>underlying sub-systems which are switched between</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-n-sys-">n_sys_</a></strong> <br>number of systems</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-idx-">idx_</a></strong> <br>current system/controller index.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-list-">Kc_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-inty-list-">Kc_inty_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-u-list-">Kc_u_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-g-design-list-">g_design_list_</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;&amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a> by moving the system object.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control">Control</a></strong>(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step)</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference">ControlOutputReference</a></strong>(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step)</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys">sys</a></strong>() const</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc">Kc</a></strong>() const<br>Get state feedback controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty">Kc_inty</a></strong>() const<br>Get integral controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u">Kc_u</a></strong>() const<br>Get input feedback controller gain.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design">g_design</a></strong>() const<br>Get input gain used in controller design.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref">u_ref</a></strong>() const<br>Get reference input.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref">x_ref</a></strong>() const<br>Get reference state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref">y_ref</a></strong>() const<br>Get reference output.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type">control_type</a></strong>() const<br>Get controller type.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu">tau_awu</a></strong>() const<br>Get time constant of anti-integral-windup.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb">u_lb</a></strong>() const<br>Get control lower bound.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub">u_ub</a></strong>() const<br>Get control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys">set_sys</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys)<br>Set system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref">set_u_ref</a></strong>(const Vector &amp; u_ref)<br>Set reference input (u_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref">set_x_ref</a></strong>(const Vector &amp; x_ref)<br>Set reference state (x_ref)</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref">set_y_ref</a></strong>(const Vector &amp; y_ref)<br>Set reference output (y_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu">set_tau_awu</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tau)<br>Set time constant of anti-integral-windup.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type">set_control_type</a></strong>(size_t control_type)<br>Sets the control type.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb">set_u_lb</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb)<br>sets control lower bound</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub">set_u_ub</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub)<br>Sets control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset">Reset</a></strong>()<br>reset system and control variables.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print">Print</a></strong>()<br>prints variables to stdout</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-">sys_</a></strong> <br>underlying LDS</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-">u_</a></strong> <br>control signal</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-">u_return_</a></strong> <br>control signal that is <em>returned</em> to user</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-">g_design_</a></strong> <br>input gain of the system used for controller design</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-">u_ref_</a></strong> <br>reference input</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-">u_ref_prev_</a></strong> <br>reference input at previous time step</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-">x_ref_</a></strong> <br>reference state</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-">y_ref_</a></strong> <br>reference output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-">cx_ref_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-">Kc_</a></strong> <br>state controller gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-">Kc_u_</a></strong> <br>input controller gain (optional when control updates \deltaU)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-">Kc_inty_</a></strong> <br>integral controller gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-">du_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-">dv_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-">v_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-">dv_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-">v_</a></strong> <br>Control after g inversion (e.g., control in physical units)</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-">int_e_</a></strong> <br>integrated error</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-">int_e_awu_adjust_</a></strong> <br>anti-windup adjustment to intE</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-">u_sat_</a></strong> <br>control signal after saturation (for antiWindup)</td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-">do_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-">do_lock_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-">u_saturated_</a></strong> <br>whether control signal has reached saturation limits</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-">u_lb_</a></strong> <br>lower bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-">u_ub_</a></strong> <br>upper bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-">tau_awu_</a></strong> <br>antiwindup time constant</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-">k_awu_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-">t_since_control_onset_</a></strong> <br>time since control epoch onset</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-">control_type_</a></strong> <br>controller type</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>SwitchedController; +</span></span></code></pre></div><hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="switchedcontroller"> + <strong>SwitchedController</strong> + <a class="anchor" href="#switchedcontroller">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>SwitchedController() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="switchedcontroller-1"> + <strong>SwitchedController</strong> + <a class="anchor" href="#switchedcontroller-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> SwitchedController( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> System <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> systems, +</span></span><span style="display:flex;"><span> data_t u_lb, +</span></span><span style="display:flex;"><span> data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>systems</strong> vector of sub-systems</li> +<li><strong>u_lb</strong> lower bound on control (u)</li> +<li><strong>u_ub</strong> upper bound on control (u)</li> +<li><strong>control_type</strong> [optional] control type bit mask</li> +</ul> +<hr> +<h3 id="switchedcontroller-2"> + <strong>SwitchedController</strong> + <a class="anchor" href="#switchedcontroller-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> SwitchedController( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> System <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> systems, +</span></span><span style="display:flex;"><span> data_t u_lb, +</span></span><span style="display:flex;"><span> data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>systems</strong> vector of sub-systems</li> +<li><strong>u_lb</strong> lower bound on control (u)</li> +<li><strong>u_ub</strong> upper bound on control (u)</li> +<li><strong>control_type</strong> [optional] control type bit mask</li> +</ul> +<hr> +<h3 id="switch"> + <strong>Switch</strong> + <a class="anchor" href="#switch">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Switch( +</span></span><span style="display:flex;"><span> size_t idx, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_force_switch <span style="color:#f92672">=</span>false +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>idx</strong> index</li> +<li><strong>do_force_switch</strong> whether to force a system switch even if already there.</li> +</ul> +<hr> +<h3 id="set_"> + <strong>set_Kc</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> <span style="color:#f92672">&amp;</span> Kc +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-1"> + <strong>set_Kc</strong> + <a class="anchor" href="#set_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> <span style="color:#f92672">&amp;&amp;</span> Kc +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-2"> + <strong>set_Kc_inty</strong> + <a class="anchor" href="#set_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc_inty( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> <span style="color:#f92672">&amp;</span> Kc_inty +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-3"> + <strong>set_Kc_inty</strong> + <a class="anchor" href="#set_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc_inty( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> <span style="color:#f92672">&amp;&amp;</span> Kc_inty +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-4"> + <strong>set_Kc_u</strong> + <a class="anchor" href="#set_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc_u( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> <span style="color:#f92672">&amp;</span> Kc_u +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-5"> + <strong>set_Kc_u</strong> + <a class="anchor" href="#set_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Kc_u( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> <span style="color:#f92672">&amp;&amp;</span> Kc_u +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-6"> + <strong>set_g_design</strong> + <a class="anchor" href="#set_-6">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_g_design( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformVectorList <span style="color:#f92672">&amp;</span> g +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-7"> + <strong>set_g_design</strong> + <a class="anchor" href="#set_-7">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_g_design( +</span></span><span style="display:flex;"><span> UniformVectorList <span style="color:#f92672">&amp;&amp;</span> g +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h2 id="protected-attribute-details"> + Protected Attribute Details + <a class="anchor" href="#protected-attribute-details">#</a> +</h2> +<h3 id="systems_"> + <strong>systems_</strong> + <a class="anchor" href="#systems_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> System <span style="color:#f92672">&gt;</span> systems_; +</span></span></code></pre></div><hr> +<h3 id="n_"> + <strong>n_sys_</strong> + <a class="anchor" href="#n_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t n_sys_ {}; +</span></span></code></pre></div><hr> +<h3 id="idx_"> + <strong>idx_</strong> + <a class="anchor" href="#idx_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>size_t idx_ {}; +</span></span></code></pre></div><hr> +<h3 id="kc_"> + <strong>Kc_list_</strong> + <a class="anchor" href="#kc_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList Kc_list_; +</span></span></code></pre></div><hr> +<h3 id="kc_-1"> + <strong>Kc_inty_list_</strong> + <a class="anchor" href="#kc_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList Kc_inty_list_; +</span></span></code></pre></div><hr> +<h3 id="kc_-2"> + <strong>Kc_u_list_</strong> + <a class="anchor" href="#kc_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList Kc_u_list_; +</span></span></code></pre></div><hr> +<h3 id="g_"> + <strong>g_design_list_</strong> + <a class="anchor" href="#g_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformVectorList g_design_list_; +</span></span></code></pre></div><hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsswitchedcontroller">lds::SwitchedController</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#switchedcontroller"><strong>SwitchedController</strong></a></li> + <li><a href="#switchedcontroller-1"><strong>SwitchedController</strong></a></li> + <li><a href="#switchedcontroller-2"><strong>SwitchedController</strong></a></li> + <li><a href="#switch"><strong>Switch</strong></a></li> + <li><a href="#set_"><strong>set_Kc</strong></a></li> + <li><a href="#set_-1"><strong>set_Kc</strong></a></li> + <li><a href="#set_-2"><strong>set_Kc_inty</strong></a></li> + <li><a href="#set_-3"><strong>set_Kc_inty</strong></a></li> + <li><a href="#set_-4"><strong>set_Kc_u</strong></a></li> + <li><a href="#set_-5"><strong>set_Kc_u</strong></a></li> + <li><a href="#set_-6"><strong>set_g_design</strong></a></li> + <li><a href="#set_-7"><strong>set_g_design</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#systems_"><strong>systems_</strong></a></li> + <li><a href="#n_"><strong>n_sys_</strong></a></li> + <li><a href="#idx_"><strong>idx_</strong></a></li> + <li><a href="#kc_"><strong>Kc_list_</strong></a></li> + <li><a href="#kc_-1"><strong>Kc_inty_list_</strong></a></li> + <li><a href="#kc_-2"><strong>Kc_u_list_</strong></a></li> + <li><a href="#g_"><strong>g_design_list_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_system/index.html b/docs/docs/api/classes/classlds_1_1_system/index.html new file mode 100644 index 00000000..a486ae9b --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_system/index.html @@ -0,0 +1,1324 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Linear Dynamical System Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::System"> + <meta property="og:description" content="Linear Dynamical System Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::System | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::System</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldssystem">lds::System</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#public-attributes">Public Attributes</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#system"><strong>System</strong></a></li> + <li><a href="#system-1"><strong>System</strong></a></li> + <li><a href="#heading"><strong>~System</strong></a></li> + <li><a href="#filter"><strong>Filter</strong></a></li> + <li><a href="#simulate"><strong>Simulate</strong></a></li> + <li><a href="#f"><strong>f</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#h_"><strong>h_</strong></a></li> + <li><a href="#n_"><strong>n_u</strong></a></li> + <li><a href="#n_-1"><strong>n_x</strong></a></li> + <li><a href="#n_-2"><strong>n_y</strong></a></li> + <li><a href="#dt"><strong>dt</strong></a></li> + <li><a href="#x"><strong>x</strong></a></li> + <li><a href="#p"><strong>P</strong></a></li> + <li><a href="#m"><strong>m</strong></a></li> + <li><a href="#p_"><strong>P_m</strong></a></li> + <li><a href="#cx"><strong>cx</strong></a></li> + <li><a href="#y"><strong>y</strong></a></li> + <li><a href="#x0"><strong>x0</strong></a></li> + <li><a href="#m0"><strong>m0</strong></a></li> + <li><a href="#a"><strong>A</strong></a></li> + <li><a href="#b"><strong>B</strong></a></li> + <li><a href="#g"><strong>g</strong></a></li> + <li><a href="#c"><strong>C</strong></a></li> + <li><a href="#d"><strong>d</strong></a></li> + <li><a href="#ke"><strong>Ke</strong></a></li> + <li><a href="#ke_"><strong>Ke_m</strong></a></li> + <li><a href="#q"><strong>Q</strong></a></li> + <li><a href="#q_"><strong>Q_m</strong></a></li> + <li><a href="#p0"><strong>P0</strong></a></li> + <li><a href="#p0_"><strong>P0_m</strong></a></li> + <li><a href="#set_"><strong>set_A</strong></a></li> + <li><a href="#set_-1"><strong>set_B</strong></a></li> + <li><a href="#set_-2"><strong>set_m</strong></a></li> + <li><a href="#set_-3"><strong>set_g</strong></a></li> + <li><a href="#set_-4"><strong>set_Q</strong></a></li> + <li><a href="#set_-5"><strong>set_Q_m</strong></a></li> + <li><a href="#set_-6"><strong>set_x0</strong></a></li> + <li><a href="#set_-7"><strong>set_P0</strong></a></li> + <li><a href="#set_-8"><strong>set_P0_m</strong></a></li> + <li><a href="#set_-9"><strong>set_C</strong></a></li> + <li><a href="#set_-10"><strong>set_d</strong></a></li> + <li><a href="#set_-11"><strong>set_x</strong></a></li> + <li><a href="#reset"><strong>Reset</strong></a></li> + <li><a href="#nstep_"><strong>nstep_pred_block</strong></a></li> + <li><a href="#print"><strong>Print</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + <li><a href="#initvars"><strong>InitVars</strong></a></li> + </ul> + </li> + <li><a href="#public-attribute-details">Public Attribute Details</a> + <ul> + <li><a href="#do_"><strong>do_adapt_m</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#n_-3"><strong>n_x_</strong></a></li> + <li><a href="#n_-4"><strong>n_u_</strong></a></li> + <li><a href="#n_-5"><strong>n_y_</strong></a></li> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#x_"><strong>x_</strong></a></li> + <li><a href="#p_-1"><strong>P_</strong></a></li> + <li><a href="#m_"><strong>m_</strong></a></li> + <li><a href="#p_-2"><strong>P_m_</strong></a></li> + <li><a href="#cx_"><strong>cx_</strong></a></li> + <li><a href="#y_"><strong>y_</strong></a></li> + <li><a href="#z_"><strong>z_</strong></a></li> + <li><a href="#x0_"><strong>x0_</strong></a></li> + <li><a href="#p0_-1"><strong>P0_</strong></a></li> + <li><a href="#m0_"><strong>m0_</strong></a></li> + <li><a href="#p0_-2"><strong>P0_m_</strong></a></li> + <li><a href="#a_"><strong>A_</strong></a></li> + <li><a href="#b_"><strong>B_</strong></a></li> + <li><a href="#g_"><strong>g_</strong></a></li> + <li><a href="#q_-1"><strong>Q_</strong></a></li> + <li><a href="#q_-2"><strong>Q_m_</strong></a></li> + <li><a href="#c_"><strong>C_</strong></a></li> + <li><a href="#d_"><strong>d_</strong></a></li> + <li><a href="#ke_-1"><strong>Ke_</strong></a></li> + <li><a href="#ke_-2"><strong>Ke_m_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldssystem"> + lds::System + <a class="anchor" href="#ldssystem">#</a> +</h1> +<p>Linear Dynamical <a href="">System</a> Type. +<br /> <code>#include &lt;lds_sys.h&gt;</code></p> +<p>Inherited by <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">lds::gaussian::System</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">lds::poisson::System</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-system">System</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-system">System</a></strong>(size_t n_u, size_t n_x, size_t n_y, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> p0 =kDefaultP0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> q0 =kDefaultQ0)<br>constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></td> + </tr> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-~system">~System</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-filter">Filter</a></strong>(const Vector &amp; u_tm1, const Vector &amp; z)<br>Filter data to produce causal state estimates.</td> + </tr> + <tr> + <td>virtual const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-simulate">Simulate</a></strong>(const Vector &amp; u_tm1) =0<br>simulates system (single time step)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-f">f</a></strong>(const Vector &amp; u, bool do_add_noise =false)<br>system dynamics function</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h">h</a></strong>() =0<br>system output function</td> + </tr> + <tr> + <td>virtual Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h-">h_</a></strong>(Vector x) =0<br>system output function (stateless)</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-u">n_u</a></strong>() const<br>Get number of inputs.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-x">n_x</a></strong>() const<br>Get number of states.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-y">n_y</a></strong>() const<br>Get number of outputs.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-dt">dt</a></strong>() const<br>Get sample period.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x">x</a></strong>() const<br>Get current state.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p">P</a></strong>() const<br>Get covariance of state estimate.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m">m</a></strong>() const<br>Get current process disturbance/bias.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p-m">P_m</a></strong>() const<br>Get covariance of process disturbance estimate.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-cx">cx</a></strong>() const<br>Get C*x.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-y">y</a></strong>() const<br>Get output.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x0">x0</a></strong>() const<br>Get initial state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m0">m0</a></strong>() const<br>Get initial disturbance.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-a">A</a></strong>() const<br>Get state matrix.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-b">B</a></strong>() const<br>Get input matrix.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-g">g</a></strong>() const<br>Get input gain/conversion factor.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-c">C</a></strong>() const<br>Get output matrix.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-d">d</a></strong>() const<br>Get output bias.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke">Ke</a></strong>() const<br>Get estimator gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke-m">Ke_m</a></strong>() const<br>Get estimator gain for process disturbance (m)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q">Q</a></strong>()<br>Get process noise covariance.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q-m">Q_m</a></strong>()<br>Get process noise covariance of disturbance evoluation.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0">P0</a></strong>()<br>Get covariance of initial state.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0-m">P0_m</a></strong>()<br>Get covariance of initial process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-a">set_A</a></strong>(const Matrix &amp; A)<br>Set state matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-b">set_B</a></strong>(const Matrix &amp; B)<br>Set input matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-m">set_m</a></strong>(const Vector &amp; m, bool do_force_assign =false)<br>Set process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-g">set_g</a></strong>(const Vector &amp; g)<br>Set input gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q">set_Q</a></strong>(const Matrix &amp; Q)<br>Set process noise covariance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q-m">set_Q_m</a></strong>(const Matrix &amp; Q_m)<br>Set process noise covariance of disturbance evoluation.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x0">set_x0</a></strong>(const Vector &amp; x0)<br>Set initial state.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0">set_P0</a></strong>(const Matrix &amp; P0)<br>Set covariance of initial state.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0-m">set_P0_m</a></strong>(const Matrix &amp; P0_m)<br>Set covariance of initial process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-c">set_C</a></strong>(const Matrix &amp; C)<br>Set output matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-d">set_d</a></strong>(const Vector &amp; d)<br>Set output bias.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x">set_x</a></strong>(const Vector &amp; x)<br>Set state of system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-reset">Reset</a></strong>()<br>Reset system variables.</td> + </tr> + <tr> + <td>std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-nstep-pred-block">nstep_pred_block</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; u, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; z, size_t n_pred =1)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-print">Print</a></strong>()<br>Print system variables to stdout.</td> + </tr> + </tbody> +</table> +<h2 id="protected-functions"> + Protected Functions + <a class="anchor" href="#protected-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-recurseke">RecurseKe</a></strong>() =0<br>Recursively recalculate estimator gain (Ke)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-initvars">InitVars</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> p0 =kDefaultP0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> q0 =kDefaultQ0)</td> + </tr> + </tbody> +</table> +<h2 id="public-attributes"> + Public Attributes + <a class="anchor" href="#public-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-do-adapt-m">do_adapt_m</a></strong> <br>whether to adaptively estimate disturbance m</td> + </tr> + </tbody> +</table> +<h2 id="protected-attributes"> + Protected Attributes + <a class="anchor" href="#protected-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x-">x_</a></strong> <br>state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-">P_</a></strong> <br>covariance of state estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m-">m_</a></strong> <br>process disturbance</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-m-">P_m_</a></strong> <br>covariance of disturbance estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-cx-">cx_</a></strong> <br>C*x.</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-y-">y_</a></strong> <br>output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-z-">z_</a></strong> <br>measurement</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x0-">x0_</a></strong> <br>initial state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-">P0_</a></strong> <br>covariance of initial state estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m0-">m0_</a></strong> <br>initial process disturbance</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-m-">P0_m_</a></strong> <br>covariance of initial disturbance est.</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-a-">A_</a></strong> <br>state matrix</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-b-">B_</a></strong> <br>input matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-g-">g_</a></strong> <br>input gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-">Q_</a></strong> <br>covariance of process noise</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-m-">Q_m_</a></strong> <br>covariance of disturbance random walk</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-c-">C_</a></strong> <br>output matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-d-">d_</a></strong> <br>output bias</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-">Ke_</a></strong> <br>estimator gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-m-">Ke_m_</a></strong> <br>estimator gain for process disturbance</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="system"> + <strong>System</strong> + <a class="anchor" href="#system">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>System() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="system-1"> + <strong>System</strong> + <a class="anchor" href="#system-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>System( +</span></span><span style="display:flex;"><span> size_t n_u, +</span></span><span style="display:flex;"><span> size_t n_x, +</span></span><span style="display:flex;"><span> size_t n_y, +</span></span><span style="display:flex;"><span> data_t dt, +</span></span><span style="display:flex;"><span> data_t p0 <span style="color:#f92672">=</span>kDefaultP0, +</span></span><span style="display:flex;"><span> data_t q0 <span style="color:#f92672">=</span>kDefaultQ0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_u</strong> number of inputs</li> +<li><strong>n_x</strong> number of states</li> +<li><strong>n_y</strong> number of outputs</li> +<li><strong>dt</strong> sample period</li> +<li><strong>p0</strong> diagonal elements for state estimate covariance</li> +<li><strong>q0</strong> diagonal elements for process noise covariance</li> +</ul> +<hr> +<h3 id="heading"> + <strong>~System</strong> + <a class="anchor" href="#heading">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#f92672">~</span>System() +</span></span></code></pre></div><hr> +<h3 id="filter"> + <strong>Filter</strong> + <a class="anchor" href="#filter">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Filter( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> u_tm1, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> z +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>u_tm1</strong> input at t-minus-1</li> +<li><strong>z_t</strong> current measurement</li> +</ul> +<p>Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.</p> +<hr> +<h3 id="simulate"> + <strong>Simulate</strong> + <a class="anchor" href="#simulate">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> Simulate( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> u_tm1 +</span></span><span style="display:flex;"><span>) <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>u_tm1</strong> input at time t-1</li> +</ul> +<p><strong>Return</strong>: simulated measurement at time t</p> +<p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-simulate">lds::gaussian::System::Simulate</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-simulate">lds::poisson::System::Simulate</a></p> +<hr> +<h3 id="f"> + <strong>f</strong> + <a class="anchor" href="#f">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> f( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> u, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_add_noise <span style="color:#f92672">=</span>false +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>u</strong> input</li> +<li><strong>do_add_noise</strong> whether to add simulated process noise</li> +</ul> +<hr> +<h3 id="h"> + <strong>h</strong> + <a class="anchor" href="#h">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> h() <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h">lds::gaussian::System::h</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h">lds::poisson::System::h</a></p> +<hr> +<h3 id="h_"> + <strong>h_</strong> + <a class="anchor" href="#h_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> Vector h_( +</span></span><span style="display:flex;"><span> Vector x +</span></span><span style="display:flex;"><span>) <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>x_t</strong> state at time t</li> +</ul> +<p><strong>Return</strong>: predicted state at time t + 1</p> +<p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h-">lds::gaussian::System::h_</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h-">lds::poisson::System::h_</a></p> +<hr> +<h3 id="n_"> + <strong>n_u</strong> + <a class="anchor" href="#n_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t n_u() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="n_-1"> + <strong>n_x</strong> + <a class="anchor" href="#n_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t n_x() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="n_-2"> + <strong>n_y</strong> + <a class="anchor" href="#n_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t n_y() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="dt"> + <strong>dt</strong> + <a class="anchor" href="#dt">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> data_t dt() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="x"> + <strong>x</strong> + <a class="anchor" href="#x">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="p"> + <strong>P</strong> + <a class="anchor" href="#p">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="m"> + <strong>m</strong> + <a class="anchor" href="#m">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> m() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="p_"> + <strong>P_m</strong> + <a class="anchor" href="#p_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P_m() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="cx"> + <strong>cx</strong> + <a class="anchor" href="#cx">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> cx() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="y"> + <strong>y</strong> + <a class="anchor" href="#y">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> y() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="x0"> + <strong>x0</strong> + <a class="anchor" href="#x0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x0() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="m0"> + <strong>m0</strong> + <a class="anchor" href="#m0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> m0() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="a"> + <strong>A</strong> + <a class="anchor" href="#a">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> A() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="b"> + <strong>B</strong> + <a class="anchor" href="#b">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> B() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="g"> + <strong>g</strong> + <a class="anchor" href="#g">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> g() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="c"> + <strong>C</strong> + <a class="anchor" href="#c">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> C() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="d"> + <strong>d</strong> + <a class="anchor" href="#d">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> d() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="ke"> + <strong>Ke</strong> + <a class="anchor" href="#ke">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Ke() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="ke_"> + <strong>Ke_m</strong> + <a class="anchor" href="#ke_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Ke_m() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="q"> + <strong>Q</strong> + <a class="anchor" href="#q">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q() +</span></span></code></pre></div><hr> +<h3 id="q_"> + <strong>Q_m</strong> + <a class="anchor" href="#q_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q_m() +</span></span></code></pre></div><hr> +<h3 id="p0"> + <strong>P0</strong> + <a class="anchor" href="#p0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P0() +</span></span></code></pre></div><hr> +<h3 id="p0_"> + <strong>P0_m</strong> + <a class="anchor" href="#p0_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P0_m() +</span></span></code></pre></div><hr> +<h3 id="set_"> + <strong>set_A</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_A( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> A +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-1"> + <strong>set_B</strong> + <a class="anchor" href="#set_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_B( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> B +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-2"> + <strong>set_m</strong> + <a class="anchor" href="#set_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_m( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> m, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_force_assign <span style="color:#f92672">=</span>false +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-3"> + <strong>set_g</strong> + <a class="anchor" href="#set_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_g( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> g +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-4"> + <strong>set_Q</strong> + <a class="anchor" href="#set_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Q( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-5"> + <strong>set_Q_m</strong> + <a class="anchor" href="#set_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Q_m( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q_m +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-6"> + <strong>set_x0</strong> + <a class="anchor" href="#set_-6">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_x0( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-7"> + <strong>set_P0</strong> + <a class="anchor" href="#set_-7">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_P0( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-8"> + <strong>set_P0_m</strong> + <a class="anchor" href="#set_-8">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_P0_m( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> P0_m +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-9"> + <strong>set_C</strong> + <a class="anchor" href="#set_-9">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_C( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> C +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-10"> + <strong>set_d</strong> + <a class="anchor" href="#set_-10">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_d( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> d +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-11"> + <strong>set_x</strong> + <a class="anchor" href="#set_-11">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_x( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="reset"> + <strong>Reset</strong> + <a class="anchor" href="#reset">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Reset() +</span></span></code></pre></div><hr> +<h3 id="nstep_"> + <strong>nstep_pred_block</strong> + <a class="anchor" href="#nstep_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&gt;</span> nstep_pred_block( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> u, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> kMatFreeDim2 <span style="color:#f92672">&gt;</span> z, +</span></span><span style="display:flex;"><span> size_t n_pred <span style="color:#f92672">=</span><span style="color:#ae81ff">1</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="print"> + <strong>Print</strong> + <a class="anchor" href="#print">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Print() +</span></span></code></pre></div><hr> +<h2 id="protected-function-details"> + Protected Function Details + <a class="anchor" href="#protected-function-details">#</a> +</h2> +<h3 id="recurseke"> + <strong>RecurseKe</strong> + <a class="anchor" href="#recurseke">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> RecurseKe() <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span></code></pre></div><p><strong>Reimplemented by</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-recurseke">lds::gaussian::System::RecurseKe</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-recurseke">lds::poisson::System::RecurseKe</a></p> +<hr> +<h3 id="initvars"> + <strong>InitVars</strong> + <a class="anchor" href="#initvars">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> InitVars( +</span></span><span style="display:flex;"><span> data_t p0 <span style="color:#f92672">=</span>kDefaultP0, +</span></span><span style="display:flex;"><span> data_t q0 <span style="color:#f92672">=</span>kDefaultQ0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h2 id="public-attribute-details"> + Public Attribute Details + <a class="anchor" href="#public-attribute-details">#</a> +</h2> +<h3 id="do_"> + <strong>do_adapt_m</strong> + <a class="anchor" href="#do_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> do_adapt_m {}; +</span></span></code></pre></div><hr> +<h2 id="protected-attribute-details"> + Protected Attribute Details + <a class="anchor" href="#protected-attribute-details">#</a> +</h2> +<h3 id="n_-3"> + <strong>n_x_</strong> + <a class="anchor" href="#n_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>size_t n_x_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-4"> + <strong>n_u_</strong> + <a class="anchor" href="#n_-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>size_t n_u_ {}; +</span></span></code></pre></div><hr> +<h3 id="n_-5"> + <strong>n_y_</strong> + <a class="anchor" href="#n_-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>std<span style="color:#f92672">::</span>size_t n_y_ {}; +</span></span></code></pre></div><hr> +<h3 id="dt_"> + <strong>dt_</strong> + <a class="anchor" href="#dt_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>data_t dt_ {}; +</span></span></code></pre></div><hr> +<h3 id="x_"> + <strong>x_</strong> + <a class="anchor" href="#x_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector x_; +</span></span></code></pre></div><hr> +<h3 id="p_-1"> + <strong>P_</strong> + <a class="anchor" href="#p_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix P_; +</span></span></code></pre></div><hr> +<h3 id="m_"> + <strong>m_</strong> + <a class="anchor" href="#m_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector m_; +</span></span></code></pre></div><hr> +<h3 id="p_-2"> + <strong>P_m_</strong> + <a class="anchor" href="#p_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix P_m_; +</span></span></code></pre></div><hr> +<h3 id="cx_"> + <strong>cx_</strong> + <a class="anchor" href="#cx_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector cx_; +</span></span></code></pre></div><hr> +<h3 id="y_"> + <strong>y_</strong> + <a class="anchor" href="#y_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector y_; +</span></span></code></pre></div><hr> +<h3 id="z_"> + <strong>z_</strong> + <a class="anchor" href="#z_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector z_; +</span></span></code></pre></div><hr> +<h3 id="x0_"> + <strong>x0_</strong> + <a class="anchor" href="#x0_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector x0_; +</span></span></code></pre></div><hr> +<h3 id="p0_-1"> + <strong>P0_</strong> + <a class="anchor" href="#p0_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix P0_; +</span></span></code></pre></div><hr> +<h3 id="m0_"> + <strong>m0_</strong> + <a class="anchor" href="#m0_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector m0_; +</span></span></code></pre></div><hr> +<h3 id="p0_-2"> + <strong>P0_m_</strong> + <a class="anchor" href="#p0_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix P0_m_; +</span></span></code></pre></div><hr> +<h3 id="a_"> + <strong>A_</strong> + <a class="anchor" href="#a_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix A_; +</span></span></code></pre></div><hr> +<h3 id="b_"> + <strong>B_</strong> + <a class="anchor" href="#b_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix B_; +</span></span></code></pre></div><hr> +<h3 id="g_"> + <strong>g_</strong> + <a class="anchor" href="#g_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector g_; +</span></span></code></pre></div><hr> +<h3 id="q_-1"> + <strong>Q_</strong> + <a class="anchor" href="#q_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Q_; +</span></span></code></pre></div><hr> +<h3 id="q_-2"> + <strong>Q_m_</strong> + <a class="anchor" href="#q_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Q_m_; +</span></span></code></pre></div><hr> +<h3 id="c_"> + <strong>C_</strong> + <a class="anchor" href="#c_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix C_; +</span></span></code></pre></div><hr> +<h3 id="d_"> + <strong>d_</strong> + <a class="anchor" href="#d_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Vector d_; +</span></span></code></pre></div><hr> +<h3 id="ke_-1"> + <strong>Ke_</strong> + <a class="anchor" href="#ke_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Ke_; +</span></span></code></pre></div><hr> +<h3 id="ke_-2"> + <strong>Ke_m_</strong> + <a class="anchor" href="#ke_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix Ke_m_; +</span></span></code></pre></div><hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldssystem">lds::System</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#public-attributes">Public Attributes</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#system"><strong>System</strong></a></li> + <li><a href="#system-1"><strong>System</strong></a></li> + <li><a href="#heading"><strong>~System</strong></a></li> + <li><a href="#filter"><strong>Filter</strong></a></li> + <li><a href="#simulate"><strong>Simulate</strong></a></li> + <li><a href="#f"><strong>f</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#h_"><strong>h_</strong></a></li> + <li><a href="#n_"><strong>n_u</strong></a></li> + <li><a href="#n_-1"><strong>n_x</strong></a></li> + <li><a href="#n_-2"><strong>n_y</strong></a></li> + <li><a href="#dt"><strong>dt</strong></a></li> + <li><a href="#x"><strong>x</strong></a></li> + <li><a href="#p"><strong>P</strong></a></li> + <li><a href="#m"><strong>m</strong></a></li> + <li><a href="#p_"><strong>P_m</strong></a></li> + <li><a href="#cx"><strong>cx</strong></a></li> + <li><a href="#y"><strong>y</strong></a></li> + <li><a href="#x0"><strong>x0</strong></a></li> + <li><a href="#m0"><strong>m0</strong></a></li> + <li><a href="#a"><strong>A</strong></a></li> + <li><a href="#b"><strong>B</strong></a></li> + <li><a href="#g"><strong>g</strong></a></li> + <li><a href="#c"><strong>C</strong></a></li> + <li><a href="#d"><strong>d</strong></a></li> + <li><a href="#ke"><strong>Ke</strong></a></li> + <li><a href="#ke_"><strong>Ke_m</strong></a></li> + <li><a href="#q"><strong>Q</strong></a></li> + <li><a href="#q_"><strong>Q_m</strong></a></li> + <li><a href="#p0"><strong>P0</strong></a></li> + <li><a href="#p0_"><strong>P0_m</strong></a></li> + <li><a href="#set_"><strong>set_A</strong></a></li> + <li><a href="#set_-1"><strong>set_B</strong></a></li> + <li><a href="#set_-2"><strong>set_m</strong></a></li> + <li><a href="#set_-3"><strong>set_g</strong></a></li> + <li><a href="#set_-4"><strong>set_Q</strong></a></li> + <li><a href="#set_-5"><strong>set_Q_m</strong></a></li> + <li><a href="#set_-6"><strong>set_x0</strong></a></li> + <li><a href="#set_-7"><strong>set_P0</strong></a></li> + <li><a href="#set_-8"><strong>set_P0_m</strong></a></li> + <li><a href="#set_-9"><strong>set_C</strong></a></li> + <li><a href="#set_-10"><strong>set_d</strong></a></li> + <li><a href="#set_-11"><strong>set_x</strong></a></li> + <li><a href="#reset"><strong>Reset</strong></a></li> + <li><a href="#nstep_"><strong>nstep_pred_block</strong></a></li> + <li><a href="#print"><strong>Print</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + <li><a href="#initvars"><strong>InitVars</strong></a></li> + </ul> + </li> + <li><a href="#public-attribute-details">Public Attribute Details</a> + <ul> + <li><a href="#do_"><strong>do_adapt_m</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#n_-3"><strong>n_x_</strong></a></li> + <li><a href="#n_-4"><strong>n_u_</strong></a></li> + <li><a href="#n_-5"><strong>n_y_</strong></a></li> + <li><a href="#dt_"><strong>dt_</strong></a></li> + <li><a href="#x_"><strong>x_</strong></a></li> + <li><a href="#p_-1"><strong>P_</strong></a></li> + <li><a href="#m_"><strong>m_</strong></a></li> + <li><a href="#p_-2"><strong>P_m_</strong></a></li> + <li><a href="#cx_"><strong>cx_</strong></a></li> + <li><a href="#y_"><strong>y_</strong></a></li> + <li><a href="#z_"><strong>z_</strong></a></li> + <li><a href="#x0_"><strong>x0_</strong></a></li> + <li><a href="#p0_-1"><strong>P0_</strong></a></li> + <li><a href="#m0_"><strong>m0_</strong></a></li> + <li><a href="#p0_-2"><strong>P0_m_</strong></a></li> + <li><a href="#a_"><strong>A_</strong></a></li> + <li><a href="#b_"><strong>B_</strong></a></li> + <li><a href="#g_"><strong>g_</strong></a></li> + <li><a href="#q_-1"><strong>Q_</strong></a></li> + <li><a href="#q_-2"><strong>Q_m_</strong></a></li> + <li><a href="#c_"><strong>C_</strong></a></li> + <li><a href="#d_"><strong>d_</strong></a></li> + <li><a href="#ke_-1"><strong>Ke_</strong></a></li> + <li><a href="#ke_-2"><strong>Ke_m_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_uniform_matrix_list/index.html b/docs/docs/api/classes/classlds_1_1_uniform_matrix_list/index.html new file mode 100644 index 00000000..24431c6d --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_uniform_matrix_list/index.html @@ -0,0 +1,647 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + lds::UniformMatrixList + # + +More&hellip; +Inherits from std::vector&lt; Matrix &gt; + + Public Functions + # + + + + + + Name + + + + + + UniformMatrixList() =defaultConstructs a new UniformMatrixList. + + + + UniformMatrixList(const std::vector&lt; Matrix &gt; &amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0})Constructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. + + + + UniformMatrixList(std::vector&lt; Matrix &gt; &amp;&amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0})Constructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. + + + + UniformMatrixList(std::initializer_list&lt; Matrix &gt; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0})Constructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. + + + + UniformMatrixList(const UniformMatrixList&lt; D &gt; &amp; that)Constructs a new UniformMatrixList (copy). + + + + UniformMatrixList(UniformMatrixList&lt; D &gt; &amp;&amp; that)Constructs a new UniformMatrixList (move). + + + + ~UniformMatrixList() =defaultDestroys the object. + + + const std::array&lt; size_t, 2 &gt; &amp; + dim(size_t n =0) constgets dimensions of uniformly sized matrices + + + size_t + size()size of container + + + const Matrix &amp; + at(size_t n)gets reference to n^th element + + + void + Swap(Matrix &amp; that, size_t n)swaps input matrix with n^th matrix of list + + + UniformMatrixList&lt; D &gt; &amp; + operator=(const UniformMatrixList&lt; D &gt; &amp; that)assigns the contents (copy) + + + UniformMatrixList&lt; D &gt; &amp; + operator=(UniformMatrixList&lt; D &gt; &amp;&amp; that)assigns the contents (move) + + + void + append(const Matrix &amp; mat)appends a matrix to the list + + + + + Detailed Description + # + +template &lt;MatrixListFreeDim D =kMatFreeDimNone&gt; +class lds::UniformMatrixList; + + + + Public Function Details + # + + + UniformMatrixList + # + +UniformMatrixList() =default + + + UniformMatrixList + # + +explicit UniformMatrixList( + const std::vector&lt; Matrix &gt; &amp; mats, + std::array&lt; size_t, 2 &gt; dim ={0, 0} +) +Parameters:"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::UniformMatrixList"> + <meta property="og:description" content="lds::UniformMatrixList # More… +Inherits from std::vector&lt; Matrix &gt; +Public Functions # Name UniformMatrixList() =default +Constructs a new UniformMatrixList. UniformMatrixList(const std::vector&lt; Matrix &gt; &amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0}) +Constructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector&lt; Matrix &gt; &amp;&amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0}) +Constructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list&lt; Matrix &gt; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0}) +Constructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList&lt; D &gt; &amp; that) +Constructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList&lt; D &gt; &amp;&amp; that) +Constructs a new UniformMatrixList (move). ~UniformMatrixList() =default +Destroys the object. const std::array&lt; size_t, 2 &gt; &amp; dim(size_t n =0) const +gets dimensions of uniformly sized matrices size_t size() +size of container const Matrix &amp; at(size_t n) +gets reference to n^th element void Swap(Matrix &amp; that, size_t n) +swaps input matrix with n^th matrix of list UniformMatrixList&lt; D &gt; &amp; operator=(const UniformMatrixList&lt; D &gt; &amp; that) +assigns the contents (copy) UniformMatrixList&lt; D &gt; &amp; operator=(UniformMatrixList&lt; D &gt; &amp;&amp; that) +assigns the contents (move) void append(const Matrix &amp; mat) +appends a matrix to the list Detailed Description # template &lt;MatrixListFreeDim D =kMatFreeDimNone&gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector&lt; Matrix &gt; &amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0} ) Parameters:"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::UniformMatrixList | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::UniformMatrixList</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsuniformmatrixlist">lds::UniformMatrixList</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#uniformmatrixlist"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-1"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-2"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-3"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-4"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-5"><strong>UniformMatrixList</strong></a></li> + <li><a href="#heading"><strong>~UniformMatrixList</strong></a></li> + <li><a href="#dim"><strong>dim</strong></a></li> + <li><a href="#size"><strong>size</strong></a></li> + <li><a href="#at"><strong>at</strong></a></li> + <li><a href="#swap"><strong>Swap</strong></a></li> + <li><a href="#operator"><strong>operator=</strong></a></li> + <li><a href="#operator-1"><strong>operator=</strong></a></li> + <li><a href="#append"><strong>append</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsuniformmatrixlist"> + lds::UniformMatrixList + <a class="anchor" href="#ldsuniformmatrixlist">#</a> +</h1> +<p><a href="#detailed-description">More&hellip;</a></p> +<p>Inherits from std::vector&lt; Matrix &gt;</p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist">UniformMatrixList</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist">UniformMatrixList</a></strong>(const std::vector&lt; Matrix &gt; &amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0})<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a> by copying existing vector of Matrix if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist">UniformMatrixList</a></strong>(std::vector&lt; Matrix &gt; &amp;&amp; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0})<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a> by moving existing vector of Matrix if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist">UniformMatrixList</a></strong>(std::initializer_list&lt; Matrix &gt; mats, std::array&lt; size_t, 2 &gt; dim ={0, 0})<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a> from initializer_list of Matrix if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist">UniformMatrixList</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; D &gt; &amp; that)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a> (copy).</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist">UniformMatrixList</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; D &gt; &amp;&amp; that)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a> (move).</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-~uniformmatrixlist">~UniformMatrixList</a></strong>() =default<br>Destroys the object.</td> + </tr> + <tr> + <td>const std::array&lt; size_t, 2 &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-dim">dim</a></strong>(size_t n =0) const<br>gets dimensions of uniformly sized matrices</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-size">size</a></strong>()<br>size of container</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-at">at</a></strong>(size_t n)<br>gets reference to n^th element</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-swap">Swap</a></strong>(Matrix &amp; that, size_t n)<br>swaps input matrix with n^th matrix of list</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; D &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=">operator=</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; D &gt; &amp; that)<br>assigns the contents (copy)</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; D &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=">operator=</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; D &gt; &amp;&amp; that)<br>assigns the contents (move)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-append">append</a></strong>(const Matrix &amp; mat)<br>appends a matrix to the list</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D <span style="color:#f92672">=</span>kMatFreeDimNone<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>UniformMatrixList; +</span></span></code></pre></div><hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="uniformmatrixlist"> + <strong>UniformMatrixList</strong> + <a class="anchor" href="#uniformmatrixlist">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="uniformmatrixlist-1"> + <strong>UniformMatrixList</strong> + <a class="anchor" href="#uniformmatrixlist-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">explicit</span> UniformMatrixList( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Matrix <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">2</span> <span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span>{<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>} +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>mats</strong> input matrices</li> +<li><strong>dim</strong> dimensions</li> +</ul> +<hr> +<h3 id="uniformmatrixlist-2"> + <strong>UniformMatrixList</strong> + <a class="anchor" href="#uniformmatrixlist-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">explicit</span> UniformMatrixList( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Matrix <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">2</span> <span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span>{<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>} +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>mats</strong> input matrices</li> +<li><strong>dim</strong> dimensions</li> +</ul> +<hr> +<h3 id="uniformmatrixlist-3"> + <strong>UniformMatrixList</strong> + <a class="anchor" href="#uniformmatrixlist-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span> Matrix <span style="color:#f92672">&gt;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">2</span> <span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span>{<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>} +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>mats</strong> input matrices</li> +<li><strong>dim</strong> dimensions</li> +</ul> +<hr> +<h3 id="uniformmatrixlist-4"> + <strong>UniformMatrixList</strong> + <a class="anchor" href="#uniformmatrixlist-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;</span> D <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></li> +</ul> +<hr> +<h3 id="uniformmatrixlist-5"> + <strong>UniformMatrixList</strong> + <a class="anchor" href="#uniformmatrixlist-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformMatrixList( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> D <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></li> +</ul> +<hr> +<h3 id="heading"> + <strong>~UniformMatrixList</strong> + <a class="anchor" href="#heading">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#f92672">~</span>UniformMatrixList() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="dim"> + <strong>dim</strong> + <a class="anchor" href="#dim">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">2</span> <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> dim( +</span></span><span style="display:flex;"><span> size_t n <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n</strong> [optional] index in list of matrices</li> +</ul> +<p><strong>Return</strong>: dimensions</p> +<hr> +<h3 id="size"> + <strong>size</strong> + <a class="anchor" href="#size">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t size() +</span></span></code></pre></div><hr> +<h3 id="at"> + <strong>at</strong> + <a class="anchor" href="#at">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> at( +</span></span><span style="display:flex;"><span> size_t n +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="swap"> + <strong>Swap</strong> + <a class="anchor" href="#swap">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Swap( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> that, +</span></span><span style="display:flex;"><span> size_t n +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> input matrix</li> +<li><strong>n</strong> index where the matrix is moved</li> +</ul> +<hr> +<h3 id="operator"> + <strong>operator=</strong> + <a class="anchor" href="#operator">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformMatrixList<span style="color:#f92672">&lt;</span> D <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;</span> D <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></li> +</ul> +<p><strong>Return</strong>: reference to object</p> +<hr> +<h3 id="operator-1"> + <strong>operator=</strong> + <a class="anchor" href="#operator-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformMatrixList<span style="color:#f92672">&lt;</span> D <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span> D <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></li> +</ul> +<p><strong>Return</strong>: reference to object</p> +<hr> +<h3 id="append"> + <strong>append</strong> + <a class="anchor" href="#append">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> append( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> mat +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>mat</strong> input matrix</li> +</ul> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsuniformmatrixlist">lds::UniformMatrixList</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#uniformmatrixlist"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-1"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-2"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-3"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-4"><strong>UniformMatrixList</strong></a></li> + <li><a href="#uniformmatrixlist-5"><strong>UniformMatrixList</strong></a></li> + <li><a href="#heading"><strong>~UniformMatrixList</strong></a></li> + <li><a href="#dim"><strong>dim</strong></a></li> + <li><a href="#size"><strong>size</strong></a></li> + <li><a href="#at"><strong>at</strong></a></li> + <li><a href="#swap"><strong>Swap</strong></a></li> + <li><a href="#operator"><strong>operator=</strong></a></li> + <li><a href="#operator-1"><strong>operator=</strong></a></li> + <li><a href="#append"><strong>append</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_uniform_system_list/index.html b/docs/docs/api/classes/classlds_1_1_uniform_system_list/index.html new file mode 100644 index 00000000..9f4efc52 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_uniform_system_list/index.html @@ -0,0 +1,617 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + lds::UniformSystemList + # + +More&hellip; +Inherits from std::vector&lt; System &gt; + + Public Functions + # + + + + + + Name + + + + + + UniformSystemList() =defaultConstructs a new UniformSystemList. + + + + UniformSystemList(const std::vector&lt; System &gt; &amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0})Constructs a new UniformSystemList by copying existing vector of System if dimensions consistent. + + + + UniformSystemList(std::vector&lt; System &gt; &amp;&amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0})Constructs a new UniformSystemList by moving existing vector of System if dimensions consistent. + + + + UniformSystemList(std::initializer_list&lt; System &gt; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0})Constructs a new UniformSystemList from initializer_list of System if dimensions consistent. + + + + UniformSystemList(const UniformSystemList &amp; that)Constructs a new UniformSystemList (copy). + + + + UniformSystemList(UniformSystemList &amp;&amp; that)Constructs a new UniformSystemList (move). + + + + ~UniformSystemList() =defaultDestroys the object. + + + const std::array&lt; size_t, 3 &gt; &amp; + dim() constgets dimensions of the uniformly sized systems + + + size_t + size()size of container + + + const System &amp; + at(size_t n)gets reference to n^th element + + + void + Swap(System &amp; that, size_t n)swaps input system with n^th system of list + + + UniformSystemList &amp; + operator=(const UniformSystemList &amp; that)assigns the contents (copy) + + + UniformSystemList &amp; + operator=(UniformSystemList &amp;&amp; that)assigns the contents (move) + + + + + Detailed Description + # + +template &lt;typename System &gt; +class lds::UniformSystemList; + + + + Public Function Details + # + + + UniformSystemList + # + +UniformSystemList() =default + + + UniformSystemList + # + +explicit UniformSystemList( + const std::vector&lt; System &gt; &amp; systems, + std::array&lt; size_t, 3 &gt; dim ={0, 0, 0} +) +Parameters:"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::UniformSystemList"> + <meta property="og:description" content="lds::UniformSystemList # More… +Inherits from std::vector&lt; System &gt; +Public Functions # Name UniformSystemList() =default +Constructs a new UniformSystemList. UniformSystemList(const std::vector&lt; System &gt; &amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0}) +Constructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector&lt; System &gt; &amp;&amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0}) +Constructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list&lt; System &gt; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0}) +Constructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList &amp; that) +Constructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList &amp;&amp; that) +Constructs a new UniformSystemList (move). ~UniformSystemList() =default +Destroys the object. const std::array&lt; size_t, 3 &gt; &amp; dim() const +gets dimensions of the uniformly sized systems size_t size() +size of container const System &amp; at(size_t n) +gets reference to n^th element void Swap(System &amp; that, size_t n) +swaps input system with n^th system of list UniformSystemList &amp; operator=(const UniformSystemList &amp; that) +assigns the contents (copy) UniformSystemList &amp; operator=(UniformSystemList &amp;&amp; that) +assigns the contents (move) Detailed Description # template &lt;typename System &gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector&lt; System &gt; &amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0} ) Parameters:"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::UniformSystemList | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::UniformSystemList</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsuniformsystemlist">lds::UniformSystemList</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#uniformsystemlist"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-1"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-2"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-3"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-4"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-5"><strong>UniformSystemList</strong></a></li> + <li><a href="#heading"><strong>~UniformSystemList</strong></a></li> + <li><a href="#dim"><strong>dim</strong></a></li> + <li><a href="#size"><strong>size</strong></a></li> + <li><a href="#at"><strong>at</strong></a></li> + <li><a href="#swap"><strong>Swap</strong></a></li> + <li><a href="#operator"><strong>operator=</strong></a></li> + <li><a href="#operator-1"><strong>operator=</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsuniformsystemlist"> + lds::UniformSystemList + <a class="anchor" href="#ldsuniformsystemlist">#</a> +</h1> +<p><a href="#detailed-description">More&hellip;</a></p> +<p>Inherits from std::vector&lt; System &gt;</p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist">UniformSystemList</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist">UniformSystemList</a></strong>(const std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0})<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> by copying existing vector of <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist">UniformSystemList</a></strong>(std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp;&amp; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0})<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> by moving existing vector of <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist">UniformSystemList</a></strong>(std::initializer_list&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; systems, std::array&lt; size_t, 3 &gt; dim ={0, 0, 0})<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> from initializer_list of <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist">UniformSystemList</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> &amp; that)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> (copy).</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist">UniformSystemList</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> &amp;&amp; that)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> (move).</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-~uniformsystemlist">~UniformSystemList</a></strong>() =default<br>Destroys the object.</td> + </tr> + <tr> + <td>const std::array&lt; size_t, 3 &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-dim">dim</a></strong>() const<br>gets dimensions of the uniformly sized systems</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-size">size</a></strong>()<br>size of container</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-at">at</a></strong>(size_t n)<br>gets reference to n^th element</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-swap">Swap</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; that, size_t n)<br>swaps input system with n^th system of list</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=">operator=</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> &amp; that)<br>assigns the contents (copy)</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=">operator=</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a> &amp;&amp; that)<br>assigns the contents (move)</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>UniformSystemList; +</span></span></code></pre></div><hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="uniformsystemlist"> + <strong>UniformSystemList</strong> + <a class="anchor" href="#uniformsystemlist">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformSystemList() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="uniformsystemlist-1"> + <strong>UniformSystemList</strong> + <a class="anchor" href="#uniformsystemlist-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">explicit</span> UniformSystemList( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> System <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">3</span> <span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span>{<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>} +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>systems</strong> input systems</li> +<li><strong>dim</strong> dimensions (n_u, n_x, n_y)</li> +</ul> +<hr> +<h3 id="uniformsystemlist-2"> + <strong>UniformSystemList</strong> + <a class="anchor" href="#uniformsystemlist-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">explicit</span> UniformSystemList( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> System <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">3</span> <span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span>{<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>} +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>systems</strong> input systems</li> +<li><strong>dim</strong> dimensions (n_u, n_x, n_y)</li> +</ul> +<hr> +<h3 id="uniformsystemlist-3"> + <strong>UniformSystemList</strong> + <a class="anchor" href="#uniformsystemlist-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformSystemList( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span> System <span style="color:#f92672">&gt;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">3</span> <span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span>{<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>} +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>systems</strong> input systems</li> +<li><strong>dim</strong> dimensions (n_u, n_x, n_y)</li> +</ul> +<hr> +<h3 id="uniformsystemlist-4"> + <strong>UniformSystemList</strong> + <a class="anchor" href="#uniformsystemlist-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformSystemList( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformSystemList <span style="color:#f92672">&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a></li> +</ul> +<hr> +<h3 id="uniformsystemlist-5"> + <strong>UniformSystemList</strong> + <a class="anchor" href="#uniformsystemlist-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformSystemList( +</span></span><span style="display:flex;"><span> UniformSystemList <span style="color:#f92672">&amp;&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a></li> +</ul> +<hr> +<h3 id="heading"> + <strong>~UniformSystemList</strong> + <a class="anchor" href="#heading">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#f92672">~</span>UniformSystemList() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="dim"> + <strong>dim</strong> + <a class="anchor" href="#dim">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span> size_t, <span style="color:#ae81ff">3</span> <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> dim() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="size"> + <strong>size</strong> + <a class="anchor" href="#size">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t size() +</span></span></code></pre></div><hr> +<h3 id="at"> + <strong>at</strong> + <a class="anchor" href="#at">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> System <span style="color:#f92672">&amp;</span> at( +</span></span><span style="display:flex;"><span> size_t n +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="swap"> + <strong>Swap</strong> + <a class="anchor" href="#swap">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Swap( +</span></span><span style="display:flex;"><span> System <span style="color:#f92672">&amp;</span> that, +</span></span><span style="display:flex;"><span> size_t n +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> input system</li> +<li><strong>n</strong> index where the system is moved</li> +</ul> +<hr> +<h3 id="operator"> + <strong>operator=</strong> + <a class="anchor" href="#operator">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformSystemList <span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformSystemList <span style="color:#f92672">&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a></li> +</ul> +<p><strong>Return</strong>: reference to object</p> +<hr> +<h3 id="operator-1"> + <strong>operator=</strong> + <a class="anchor" href="#operator-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformSystemList <span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> UniformSystemList <span style="color:#f92672">&amp;&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">UniformSystemList</a></li> +</ul> +<p><strong>Return</strong>: reference to object</p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsuniformsystemlist">lds::UniformSystemList</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#uniformsystemlist"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-1"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-2"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-3"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-4"><strong>UniformSystemList</strong></a></li> + <li><a href="#uniformsystemlist-5"><strong>UniformSystemList</strong></a></li> + <li><a href="#heading"><strong>~UniformSystemList</strong></a></li> + <li><a href="#dim"><strong>dim</strong></a></li> + <li><a href="#size"><strong>size</strong></a></li> + <li><a href="#at"><strong>at</strong></a></li> + <li><a href="#swap"><strong>Swap</strong></a></li> + <li><a href="#operator"><strong>operator=</strong></a></li> + <li><a href="#operator-1"><strong>operator=</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1_uniform_vector_list/index.html b/docs/docs/api/classes/classlds_1_1_uniform_vector_list/index.html new file mode 100644 index 00000000..37d10876 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1_uniform_vector_list/index.html @@ -0,0 +1,600 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + lds::UniformVectorList + # + +Inherits from std::vector&lt; Vector &gt; + + Public Functions + # + + + + + + Name + + + + + + UniformVectorList() =defaultConstructs a new UniformVectorList. + + + + UniformVectorList(const std::vector&lt; Vector &gt; &amp; vecs, size_t dim =0)Constructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. + + + + UniformVectorList(std::vector&lt; Vector &gt; &amp;&amp; vecs, size_t dim =0)Constructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. + + + + UniformVectorList(std::initializer_list&lt; Vector &gt; vecs, size_t dim =0)Constructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. + + + + UniformVectorList(const UniformVectorList &amp; that)Constructs a new UniformVectorList (copy) + + + + UniformVectorList(UniformVectorList &amp;&amp; that)Constructs a new UniformVectorList (move) + + + + ~UniformVectorList() =defaultDestroys the object. + + + size_t + dim() constgets dimensions of the uniformly sized matrices + + + size_t + size()size of container + + + const Vector &amp; + at(size_t n)gets reference to n^th element + + + void + Swap(Vector &amp; that, size_t n)swaps input matrix with n^th vector of list + + + UniformVectorList &amp; + operator=(const UniformVectorList &amp; that)assigns the contents (copy) + + + UniformVectorList &amp; + operator=(UniformVectorList &amp;&amp; that)assigns the contents (move) + + + + + + + Public Function Details + # + + + UniformVectorList + # + +UniformVectorList() =default + + + UniformVectorList + # + +explicit UniformVectorList( + const std::vector&lt; Vector &gt; &amp; vecs, + size_t dim =0 +) +Parameters:"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::UniformVectorList"> + <meta property="og:description" content="lds::UniformVectorList # Inherits from std::vector&lt; Vector &gt; +Public Functions # Name UniformVectorList() =default +Constructs a new UniformVectorList. UniformVectorList(const std::vector&lt; Vector &gt; &amp; vecs, size_t dim =0) +Constructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector&lt; Vector &gt; &amp;&amp; vecs, size_t dim =0) +Constructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list&lt; Vector &gt; vecs, size_t dim =0) +Constructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList &amp; that) +Constructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList &amp;&amp; that) +Constructs a new UniformVectorList (move) ~UniformVectorList() =default +Destroys the object. size_t dim() const +gets dimensions of the uniformly sized matrices size_t size() +size of container const Vector &amp; at(size_t n) +gets reference to n^th element void Swap(Vector &amp; that, size_t n) +swaps input matrix with n^th vector of list UniformVectorList &amp; operator=(const UniformVectorList &amp; that) +assigns the contents (copy) UniformVectorList &amp; operator=(UniformVectorList &amp;&amp; that) +assigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector&lt; Vector &gt; &amp; vecs, size_t dim =0 ) Parameters:"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::UniformVectorList | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::UniformVectorList</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsuniformvectorlist">lds::UniformVectorList</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#uniformvectorlist"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-1"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-2"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-3"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-4"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-5"><strong>UniformVectorList</strong></a></li> + <li><a href="#heading"><strong>~UniformVectorList</strong></a></li> + <li><a href="#dim"><strong>dim</strong></a></li> + <li><a href="#size"><strong>size</strong></a></li> + <li><a href="#at"><strong>at</strong></a></li> + <li><a href="#swap"><strong>Swap</strong></a></li> + <li><a href="#operator"><strong>operator=</strong></a></li> + <li><a href="#operator-1"><strong>operator=</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsuniformvectorlist"> + lds::UniformVectorList + <a class="anchor" href="#ldsuniformvectorlist">#</a> +</h1> +<p>Inherits from std::vector&lt; Vector &gt;</p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist">UniformVectorList</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist">UniformVectorList</a></strong>(const std::vector&lt; Vector &gt; &amp; vecs, size_t dim =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> by copying existing vector of Vector if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist">UniformVectorList</a></strong>(std::vector&lt; Vector &gt; &amp;&amp; vecs, size_t dim =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> by moving existing vector of Vector if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist">UniformVectorList</a></strong>(std::initializer_list&lt; Vector &gt; vecs, size_t dim =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> from initializer_list of Vector if dimensions consistent.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist">UniformVectorList</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp; that)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> (copy)</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist">UniformVectorList</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp;&amp; that)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> (move)</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-~uniformvectorlist">~UniformVectorList</a></strong>() =default<br>Destroys the object.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-dim">dim</a></strong>() const<br>gets dimensions of the uniformly sized matrices</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-size">size</a></strong>()<br>size of container</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-at">at</a></strong>(size_t n)<br>gets reference to n^th element</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-swap">Swap</a></strong>(Vector &amp; that, size_t n)<br>swaps input matrix with n^th vector of list</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=">operator=</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp; that)<br>assigns the contents (copy)</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=">operator=</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp;&amp; that)<br>assigns the contents (move)</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="uniformvectorlist"> + <strong>UniformVectorList</strong> + <a class="anchor" href="#uniformvectorlist">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformVectorList() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="uniformvectorlist-1"> + <strong>UniformVectorList</strong> + <a class="anchor" href="#uniformvectorlist-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">explicit</span> UniformVectorList( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Vector <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> vecs, +</span></span><span style="display:flex;"><span> size_t dim <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>vecs</strong> input vectors</li> +<li><strong>dims</strong> dimension</li> +</ul> +<hr> +<h3 id="uniformvectorlist-2"> + <strong>UniformVectorList</strong> + <a class="anchor" href="#uniformvectorlist-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">explicit</span> UniformVectorList( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> Vector <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;&amp;</span> vecs, +</span></span><span style="display:flex;"><span> size_t dim <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>vecs</strong> input vectors</li> +<li><strong>dim</strong> dimension</li> +</ul> +<hr> +<h3 id="uniformvectorlist-3"> + <strong>UniformVectorList</strong> + <a class="anchor" href="#uniformvectorlist-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformVectorList( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span> Vector <span style="color:#f92672">&gt;</span> vecs, +</span></span><span style="display:flex;"><span> size_t dim <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>vecs</strong> input vectors</li> +<li><strong>dim</strong> dimension</li> +</ul> +<hr> +<h3 id="uniformvectorlist-4"> + <strong>UniformVectorList</strong> + <a class="anchor" href="#uniformvectorlist-4">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformVectorList( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformVectorList <span style="color:#f92672">&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a></li> +</ul> +<hr> +<h3 id="uniformvectorlist-5"> + <strong>UniformVectorList</strong> + <a class="anchor" href="#uniformvectorlist-5">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>UniformVectorList( +</span></span><span style="display:flex;"><span> UniformVectorList <span style="color:#f92672">&amp;&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a></li> +</ul> +<hr> +<h3 id="heading"> + <strong>~UniformVectorList</strong> + <a class="anchor" href="#heading">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#f92672">~</span>UniformVectorList() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="dim"> + <strong>dim</strong> + <a class="anchor" href="#dim">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t dim() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="size"> + <strong>size</strong> + <a class="anchor" href="#size">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> size_t size() +</span></span></code></pre></div><hr> +<h3 id="at"> + <strong>at</strong> + <a class="anchor" href="#at">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> at( +</span></span><span style="display:flex;"><span> size_t n +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="swap"> + <strong>Swap</strong> + <a class="anchor" href="#swap">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Swap( +</span></span><span style="display:flex;"><span> Vector <span style="color:#f92672">&amp;</span> that, +</span></span><span style="display:flex;"><span> size_t n +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> input vector</li> +<li><strong>n</strong> index where the vector is moved</li> +</ul> +<hr> +<h3 id="operator"> + <strong>operator=</strong> + <a class="anchor" href="#operator">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformVectorList <span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformVectorList <span style="color:#f92672">&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a></li> +</ul> +<p><strong>Return</strong>: reference to object</p> +<hr> +<h3 id="operator-1"> + <strong>operator=</strong> + <a class="anchor" href="#operator-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformVectorList <span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> UniformVectorList <span style="color:#f92672">&amp;&amp;</span> that +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>that</strong> another <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a></li> +</ul> +<p><strong>Return</strong>: reference to object</p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsuniformvectorlist">lds::UniformVectorList</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#uniformvectorlist"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-1"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-2"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-3"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-4"><strong>UniformVectorList</strong></a></li> + <li><a href="#uniformvectorlist-5"><strong>UniformVectorList</strong></a></li> + <li><a href="#heading"><strong>~UniformVectorList</strong></a></li> + <li><a href="#dim"><strong>dim</strong></a></li> + <li><a href="#size"><strong>size</strong></a></li> + <li><a href="#at"><strong>at</strong></a></li> + <li><a href="#swap"><strong>Swap</strong></a></li> + <li><a href="#operator"><strong>operator=</strong></a></li> + <li><a href="#operator-1"><strong>operator=</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1_controller/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1_controller/index.html new file mode 100644 index 00000000..84a2cbb8 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1gaussian_1_1_controller/index.html @@ -0,0 +1,560 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Gaussian-observation Controller Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::gaussian::Controller"> + <meta property="og:description" content="Gaussian-observation Controller Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::gaussian::Controller | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::gaussian::Controller</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussiancontroller">lds::gaussian::Controller</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsgaussiancontroller"> + lds::gaussian::Controller + <a class="anchor" href="#ldsgaussiancontroller">#</a> +</h1> +<p>Gaussian-observation <a href="">Controller</a> Type. +<br /> <code>#include &lt;lds_gaussian_ctrl.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/#function-set-y-ref">set_y_ref</a></strong>(const Vector &amp; y_ref) override<br>sets reference output</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;&amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a> by moving the system object.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control">Control</a></strong>(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step)</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference">ControlOutputReference</a></strong>(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step)</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys">sys</a></strong>() const</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc">Kc</a></strong>() const<br>Get state feedback controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty">Kc_inty</a></strong>() const<br>Get integral controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u">Kc_u</a></strong>() const<br>Get input feedback controller gain.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design">g_design</a></strong>() const<br>Get input gain used in controller design.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref">u_ref</a></strong>() const<br>Get reference input.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref">x_ref</a></strong>() const<br>Get reference state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref">y_ref</a></strong>() const<br>Get reference output.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type">control_type</a></strong>() const<br>Get controller type.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu">tau_awu</a></strong>() const<br>Get time constant of anti-integral-windup.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb">u_lb</a></strong>() const<br>Get control lower bound.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub">u_ub</a></strong>() const<br>Get control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys">set_sys</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys)<br>Set system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design">set_g_design</a></strong>(const Vector &amp; g_design)<br>Set input gain used in controller design (g_design)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref">set_u_ref</a></strong>(const Vector &amp; u_ref)<br>Set reference input (u_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref">set_x_ref</a></strong>(const Vector &amp; x_ref)<br>Set reference state (x_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc">set_Kc</a></strong>(const Matrix &amp; Kc)<br>Set state controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const Matrix &amp; Kc_inty)<br>Set integral controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u">set_Kc_u</a></strong>(const Matrix &amp; Kc_u)<br>Set input controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu">set_tau_awu</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tau)<br>Set time constant of anti-integral-windup.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type">set_control_type</a></strong>(size_t control_type)<br>Sets the control type.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb">set_u_lb</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb)<br>sets control lower bound</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub">set_u_ub</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub)<br>Sets control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset">Reset</a></strong>()<br>reset system and control variables.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print">Print</a></strong>()<br>prints variables to stdout</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-">sys_</a></strong> <br>underlying LDS</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-">u_</a></strong> <br>control signal</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-">u_return_</a></strong> <br>control signal that is <em>returned</em> to user</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-">g_design_</a></strong> <br>input gain of the system used for controller design</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-">u_ref_</a></strong> <br>reference input</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-">u_ref_prev_</a></strong> <br>reference input at previous time step</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-">x_ref_</a></strong> <br>reference state</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-">y_ref_</a></strong> <br>reference output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-">cx_ref_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-">Kc_</a></strong> <br>state controller gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-">Kc_u_</a></strong> <br>input controller gain (optional when control updates \deltaU)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-">Kc_inty_</a></strong> <br>integral controller gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-">du_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-">dv_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-">v_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-">dv_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-">v_</a></strong> <br>Control after g inversion (e.g., control in physical units)</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-">int_e_</a></strong> <br>integrated error</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-">int_e_awu_adjust_</a></strong> <br>anti-windup adjustment to intE</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-">u_sat_</a></strong> <br>control signal after saturation (for antiWindup)</td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-">do_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-">do_lock_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-">u_saturated_</a></strong> <br>whether control signal has reached saturation limits</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-">u_lb_</a></strong> <br>lower bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-">u_ub_</a></strong> <br>upper bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-">tau_awu_</a></strong> <br>antiwindup time constant</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-">k_awu_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-">t_since_control_onset_</a></strong> <br>time since control epoch onset</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-">control_type_</a></strong> <br>controller type</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="set_"> + <strong>set_y_ref</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_y_ref( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> y_ref +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref">lds::Controller::set_y_ref</a></p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussiancontroller">lds::gaussian::Controller</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit/index.html new file mode 100644 index 00000000..5a375533 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit/index.html @@ -0,0 +1,553 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS Fit Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::gaussian::Fit"> + <meta property="og:description" content="GLDS Fit Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::gaussian::Fit | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::gaussian::Fit</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianfit">lds::gaussian::Fit</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#fit"><strong>Fit</strong></a></li> + <li><a href="#fit-1"><strong>Fit</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + <li><a href="#set_"><strong>set_R</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsgaussianfit"> + lds::gaussian::Fit + <a class="anchor" href="#ldsgaussianfit">#</a> +</h1> +<p>GLDS <a href="">Fit</a> Type. +<br /> <code>#include &lt;lds_gaussian_fit.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-fit">Fit</a></strong>() =default</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-fit">Fit</a></strong>(size_t n_u, size_t n_x, size_t n_y, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">Fit</a>.</td> + </tr> + <tr> + <td>virtual const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-r">R</a></strong>() const override<br>gets measurement noise covariance</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-set-r">set_R</a></strong>(const Matrix &amp; R) override<br>sets measurement noise covariance</td> + </tr> + <tr> + <td>virtual View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/#function-h">h</a></strong>(Matrix &amp; y, const Matrix &amp; x, size_t t) override<br>output function</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-~fit">~Fit</a></strong>() =default</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-u">n_u</a></strong>() const<br>gets number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-x">n_x</a></strong>() const<br>gets number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-y">n_y</a></strong>() const<br>gets number of outputs</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-dt">dt</a></strong>() const<br>gets sample period</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-a">A</a></strong>() const<br>gets state matrix</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-b">B</a></strong>() const<br>gets input matrix</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-g">g</a></strong>() const<br>gets input gain</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-m">m</a></strong>() const<br>gets process disturbance</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-q">Q</a></strong>() const<br>gets process noise covariance</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-x0">x0</a></strong>() const<br>gets initial state estimate</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-p0">P0</a></strong>() const<br>gets covariance of initial state estimate</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-c">C</a></strong>() const<br>gets output matrix</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-d">d</a></strong>() const<br>gets output bias</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-a">set_A</a></strong>(const Matrix &amp; A)<br>sets state matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-b">set_B</a></strong>(const Matrix &amp; B)<br>sets input matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-g">set_g</a></strong>(const Vector &amp; g)<br>sets input gain/conversion factor</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-m">set_m</a></strong>(const Vector &amp; m)<br>sets process disturbance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-q">set_Q</a></strong>(const Matrix &amp; Q)<br>sets process noise covariance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-x0">set_x0</a></strong>(const Vector &amp; x0)<br>sets initial state estimate</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-p0">set_P0</a></strong>(const Matrix &amp; P0)<br>sets initial state estimate covariance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-c">set_C</a></strong>(const Matrix &amp; C)<br>sets output matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-d">set_d</a></strong>(const Vector &amp; d)<br>sets output bias</td> + </tr> + <tr> + <td>View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f">f</a></strong>(Matrix &amp; x, const Matrix &amp; u, size_t t)<br>system dynamics function</td> + </tr> + <tr> + <td>View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f">f</a></strong>(Matrix &amp; x_pre, const Matrix &amp; x_post, const Matrix &amp; u, size_t t)<br>system dynamics function</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-a-">A_</a></strong> <br>state matrix</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-b-">B_</a></strong> <br>input matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-g-">g_</a></strong> <br>input gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-m-">m_</a></strong> <br>process noise mean</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-q-">Q_</a></strong> <br>process noise cov</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-c-">C_</a></strong> <br>output matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-d-">d_</a></strong> <br>output bias</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-r-">R_</a></strong> <br>measurement noise</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-x0-">x0_</a></strong> <br>initial state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-p0-">P0_</a></strong> <br>initial covar</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="fit"> + <strong>Fit</strong> + <a class="anchor" href="#fit">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Fit() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="fit-1"> + <strong>Fit</strong> + <a class="anchor" href="#fit-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Fit( +</span></span><span style="display:flex;"><span> size_t n_u, +</span></span><span style="display:flex;"><span> size_t n_x, +</span></span><span style="display:flex;"><span> size_t n_y, +</span></span><span style="display:flex;"><span> data_t dt +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_u</strong> number of inputs</li> +<li><strong>n_x</strong> number of states</li> +<li><strong>n_y</strong> number of outputs</li> +<li><strong>dt</strong> sample period</li> +</ul> +<hr> +<h3 id="r"> + <strong>R</strong> + <a class="anchor" href="#r">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-r">lds::Fit::R</a></p> +<hr> +<h3 id="set_"> + <strong>set_R</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_R( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-r">lds::Fit::set_R</a></p> +<hr> +<h3 id="h"> + <strong>h</strong> + <a class="anchor" href="#h">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> View h( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> y, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> x, +</span></span><span style="display:flex;"><span> size_t t +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>y</strong> output estimate (over time)</li> +<li><strong>x</strong> state estimate (over time)</li> +<li><strong>t</strong> time index</li> +</ul> +<p><strong>Return</strong>: output</p> +<p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-h">lds::Fit::h</a></p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianfit">lds::gaussian::Fit</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#fit"><strong>Fit</strong></a></li> + <li><a href="#fit-1"><strong>Fit</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + <li><a href="#set_"><strong>set_R</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/index.html new file mode 100644 index 00000000..e26099c9 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/index.html @@ -0,0 +1,465 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS E-M Fit Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::gaussian::FitEM"> + <meta property="og:description" content="GLDS E-M Fit Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::gaussian::FitEM | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::gaussian::FitEM</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianfitem">lds::gaussian::FitEM</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsgaussianfitem"> + lds::gaussian::FitEM + <a class="anchor" href="#ldsgaussianfitem">#</a> +</h1> +<p>GLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">Fit</a> Type. <a href="#detailed-description">More&hellip;</a></p> +<p><br /> <code>#include &lt;lds_gaussian_fit_em.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></p> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>(size_t n_x, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> &amp; fit0, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em">~EM</a></strong>() =default</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run">Run</a></strong>(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M)</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata">ReturnData</a></strong>()<br>Returns the input/output data to caller.</td> + </tr> + <tr> + <td>const std::vector&lt; Matrix &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x">x</a></strong>() const<br>gets estimated state (over time)</td> + </tr> + <tr> + <td>const std::vector&lt; Matrix &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y">y</a></strong>() const<br>gets estimated output (over time)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t">sum_E_x_t_x_t</a></strong>() const<br>gets state-input covariance</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1">sum_E_xu_tm1_xu_tm1</a></strong>() const<br>gets state-input covariance (t-minus-1)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1">sum_E_xu_t_xu_tm1</a></strong>() const<br>gets single lag state-input covariance</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot">n_t_tot</a></strong>()<br>total number of time samples</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta">theta</a></strong>() const<br>gets parameters updated in M step</td> + </tr> + </tbody> +</table> +<p><strong>Protected Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation">Expectation</a></strong>(bool force_common_initial =false)<br>Expectation step.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization">Maximization</a></strong>(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics">MaximizeDynamics</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq">MaximizeQ</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial">MaximizeInitial</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth">Smooth</a></strong>(bool force_common_initial)<br>get smoothed estimates</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset">Reset</a></strong>()<br>reset to initial conditions</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars">InitVars</a></strong>()<br>Initializes the variables.</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta">UpdateTheta</a></strong>()<br>updates parameter list, theta</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-">u_</a></strong> <br>input training data</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-">z_</a></strong> <br>measurement training data</td> + </tr> + <tr> + <td>std::vector&lt; Matrix &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-">x_</a></strong> <br>state estimate</td> + </tr> + <tr> + <td>std::vector&lt; Cube &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-">P_</a></strong> <br>state estimate cov</td> + </tr> + <tr> + <td>std::vector&lt; Cube &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-">P_t_tm1_</a></strong> <br>single-lag state covariance</td> + </tr> + <tr> + <td>std::vector&lt; Matrix &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-">y_</a></strong> <br>output estimate</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-">diag_y_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-">sum_E_x_t_x_t_</a></strong> <br>state covariance (current time)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-">sum_E_xu_tm1_xu_tm1_</a></strong> <br>state-input covariance (t-minus-1)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-">sum_E_xu_t_xu_tm1_</a></strong> <br>single lag state-input covariance</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-">fit_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-">theta_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-">n_trials_</a></strong> <br>number of input/output data sequences</td> + </tr> + <tr> + <td>std::vector&lt; size_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-">n_t_</a></strong> <br>number of time steps</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-">n_t_tot_</a></strong> <br>total number of time steps across trials</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>FitEM; +</span></span></code></pre></div><pre tabindex="0"><code> This type is used in the process of fitting GLDS models by + expectation-maximization (EM). +</code></pre><hr> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianfitem">lds::gaussian::FitEM</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/index.html new file mode 100644 index 00000000..3d8a00c2 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/index.html @@ -0,0 +1,392 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Subspace Identification (SSID) for GLDS."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::gaussian::FitSSID"> + <meta property="og:description" content="Subspace Identification (SSID) for GLDS."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::gaussian::FitSSID | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::gaussian::FitSSID</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianfitssid">lds::gaussian::FitSSID</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsgaussianfitssid"> + lds::gaussian::FitSSID + <a class="anchor" href="#ldsgaussianfitssid">#</a> +</h1> +<p>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for GLDS. +<br /> <code>#include &lt;lds_gaussian_fit_ssid.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></p> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid">SSID</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid">SSID</a></strong>(size_t n_x, size_t n_h, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train, const Vector &amp; d =Vector(1).fill(-kInf))<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> type.</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a>, Vector &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run">Run</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt">SSIDWt</a> ssid_wt)<br>Runs fitting by subspace identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>)</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata">ReturnData</a></strong>()<br>Returns the I/O data to caller.</td> + </tr> + </tbody> +</table> +<p><strong>Protected Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd">CalcD</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> t_silence =0.1, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat">CreateHankelDataMat</a></strong>()<br>Creates the block-hankel I/O data matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd">CalcSVD</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt">SSIDWt</a> wt)<br>performs the singular value decomposition (SVD)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve">Solve</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> wt_dc)<br>solves for LDS parameters</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs">RecomputeExtObs</a></strong>()<br>recompute extended observability matrix from estimates of A, C</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-">u_</a></strong> <br>input training data</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-">z_</a></strong> <br>measurement training data</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-">D_</a></strong> <br>block-Hankel I/O data matrix</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-">fit_</a></strong> <br>fit</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-">g_dc_</a></strong> <br>I/O gain @ DC.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-">n_h_</a></strong></td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-">n_trials_</a></strong> <br>number of input/output data sequences</td> + </tr> + <tr> + <td>std::vector&lt; size_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-">n_t_</a></strong> <br>number of time steps</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-">n_t_tot_</a></strong> <br>total number of time steps across trials</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-">L_</a></strong> <br>lower triangle decomp of covariance matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-">s_</a></strong> <br>singular values</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-">ext_obs_t_</a></strong> <br>extended observability matrix</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianfitssid">lds::gaussian::FitSSID</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/index.html new file mode 100644 index 00000000..b20f1364 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/index.html @@ -0,0 +1,658 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Gaussian-observation SwitchedController Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::gaussian::SwitchedController"> + <meta property="og:description" content="Gaussian-observation SwitchedController Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::gaussian::SwitchedController | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::gaussian::SwitchedController</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianswitchedcontroller">lds::gaussian::SwitchedController</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsgaussianswitchedcontroller"> + lds::gaussian::SwitchedController + <a class="anchor" href="#ldsgaussianswitchedcontroller">#</a> +</h1> +<p>Gaussian-observation <a href="">SwitchedController</a> Type. +<br /> <code>#include &lt;lds_gaussian_sctrl.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController&lt; System &gt;</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/#function-set-y-ref">set_y_ref</a></strong>(const Vector &amp; y_ref) override<br>sets reference output</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>(const std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp; systems, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>(std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp;&amp; systems, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a> (moves systems).</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switch">Switch</a></strong>(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc">set_Kc</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc)<br>sets state feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc">set_Kc</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc)<br>sets state feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc_inty)<br>sets integral feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc_inty)<br>sets integral feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u">set_Kc_u</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc_u)<br>sets input feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u">set_Kc_u</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc_u)<br>sets input feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design">set_g_design</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp; g)<br>sets input gain used during controller design</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design">set_g_design</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp;&amp; g)<br>sets input gain used during controller design (moving)</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-systems-">systems_</a></strong> <br>underlying sub-systems which are switched between</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-n-sys-">n_sys_</a></strong> <br>number of systems</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-idx-">idx_</a></strong> <br>current system/controller index.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-list-">Kc_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-inty-list-">Kc_inty_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-u-list-">Kc_u_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-g-design-list-">g_design_list_</a></strong></td> + </tr> + </tbody> +</table> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;&amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a> by moving the system object.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control">Control</a></strong>(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step)</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference">ControlOutputReference</a></strong>(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step)</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys">sys</a></strong>() const</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc">Kc</a></strong>() const<br>Get state feedback controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty">Kc_inty</a></strong>() const<br>Get integral controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u">Kc_u</a></strong>() const<br>Get input feedback controller gain.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design">g_design</a></strong>() const<br>Get input gain used in controller design.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref">u_ref</a></strong>() const<br>Get reference input.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref">x_ref</a></strong>() const<br>Get reference state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref">y_ref</a></strong>() const<br>Get reference output.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type">control_type</a></strong>() const<br>Get controller type.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu">tau_awu</a></strong>() const<br>Get time constant of anti-integral-windup.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb">u_lb</a></strong>() const<br>Get control lower bound.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub">u_ub</a></strong>() const<br>Get control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys">set_sys</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys)<br>Set system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design">set_g_design</a></strong>(const Vector &amp; g_design)<br>Set input gain used in controller design (g_design)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref">set_u_ref</a></strong>(const Vector &amp; u_ref)<br>Set reference input (u_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref">set_x_ref</a></strong>(const Vector &amp; x_ref)<br>Set reference state (x_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc">set_Kc</a></strong>(const Matrix &amp; Kc)<br>Set state controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const Matrix &amp; Kc_inty)<br>Set integral controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u">set_Kc_u</a></strong>(const Matrix &amp; Kc_u)<br>Set input controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu">set_tau_awu</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tau)<br>Set time constant of anti-integral-windup.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type">set_control_type</a></strong>(size_t control_type)<br>Sets the control type.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb">set_u_lb</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb)<br>sets control lower bound</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub">set_u_ub</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub)<br>Sets control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset">Reset</a></strong>()<br>reset system and control variables.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print">Print</a></strong>()<br>prints variables to stdout</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-">sys_</a></strong> <br>underlying LDS</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-">u_</a></strong> <br>control signal</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-">u_return_</a></strong> <br>control signal that is <em>returned</em> to user</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-">g_design_</a></strong> <br>input gain of the system used for controller design</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-">u_ref_</a></strong> <br>reference input</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-">u_ref_prev_</a></strong> <br>reference input at previous time step</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-">x_ref_</a></strong> <br>reference state</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-">y_ref_</a></strong> <br>reference output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-">cx_ref_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-">Kc_</a></strong> <br>state controller gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-">Kc_u_</a></strong> <br>input controller gain (optional when control updates \deltaU)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-">Kc_inty_</a></strong> <br>integral controller gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-">du_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-">dv_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-">v_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-">dv_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-">v_</a></strong> <br>Control after g inversion (e.g., control in physical units)</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-">int_e_</a></strong> <br>integrated error</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-">int_e_awu_adjust_</a></strong> <br>anti-windup adjustment to intE</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-">u_sat_</a></strong> <br>control signal after saturation (for antiWindup)</td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-">do_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-">do_lock_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-">u_saturated_</a></strong> <br>whether control signal has reached saturation limits</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-">u_lb_</a></strong> <br>lower bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-">u_ub_</a></strong> <br>upper bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-">tau_awu_</a></strong> <br>antiwindup time constant</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-">k_awu_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-">t_since_control_onset_</a></strong> <br>time since control epoch onset</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-">control_type_</a></strong> <br>controller type</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="set_"> + <strong>set_y_ref</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_y_ref( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> y_ref +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref">lds::Controller::set_y_ref</a></p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussianswitchedcontroller">lds::gaussian::SwitchedController</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1gaussian_1_1_system/index.html b/docs/docs/api/classes/classlds_1_1gaussian_1_1_system/index.html new file mode 100644 index 00000000..6eedbf28 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1gaussian_1_1_system/index.html @@ -0,0 +1,855 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Gaussian LDS Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::gaussian::System"> + <meta property="og:description" content="Gaussian LDS Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::gaussian::System | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::gaussian::System</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussiansystem">lds::gaussian::System</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#system"><strong>System</strong></a></li> + <li><a href="#system-1"><strong>System</strong></a></li> + <li><a href="#simulate"><strong>Simulate</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + <li><a href="#set_"><strong>set_Q</strong></a></li> + <li><a href="#set_-1"><strong>set_R</strong></a></li> + <li><a href="#set_-2"><strong>set_Ke</strong></a></li> + <li><a href="#set_-3"><strong>set_Ke_m</strong></a></li> + <li><a href="#print"><strong>Print</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#h_"><strong>h_</strong></a></li> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#r_"><strong>R_</strong></a></li> + <li><a href="#do_"><strong>do_recurse_Ke_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsgaussiansystem"> + lds::gaussian::System + <a class="anchor" href="#ldsgaussiansystem">#</a> +</h1> +<p>Gaussian LDS Type. +<br /> <code>#include &lt;lds_gaussian_sys.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-system">System</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">System</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-system">System</a></strong>(std::size_t n_u, std::size_t n_x, std::size_t n_y, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> p0 =kDefaultP0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> q0 =kDefaultQ0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> r0 =kDefaultR0)<br>Constructs a new Gaussian <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">System</a>.</td> + </tr> + <tr> + <td>virtual const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-simulate">Simulate</a></strong>(const Vector &amp; u_tm1) override<br>Simulate system measurement.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-r">R</a></strong>() const<br>Get output noise covariance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-q">set_Q</a></strong>(const Matrix &amp; Q)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-r">set_R</a></strong>(const Matrix &amp; R)<br>Set output noise covariance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-ke">set_Ke</a></strong>(const Matrix &amp; Ke)<br>Set estimator gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-set-ke-m">set_Ke_m</a></strong>(const Matrix &amp; Ke_m)<br>Set disturbance estimator gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-print">Print</a></strong>()<br>Print system variables to stdout.</td> + </tr> + </tbody> +</table> +<h2 id="protected-functions"> + Protected Functions + <a class="anchor" href="#protected-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h">h</a></strong>() override<br><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">System</a> output function.</td> + </tr> + <tr> + <td>virtual Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-h-">h_</a></strong>(Vector x) override<br><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">System</a> output function: stateless.</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#function-recurseke">RecurseKe</a></strong>() override<br>Recursively update estimator gain.</td> + </tr> + </tbody> +</table> +<h2 id="protected-attributes"> + Protected Attributes + <a class="anchor" href="#protected-attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#variable-r-">R_</a></strong> <br>covariance of output noise</td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/#variable-do-recurse-ke-">do_recurse_Ke_</a></strong> <br>whether to recursively calculate estimator gain</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-~system">~System</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-filter">Filter</a></strong>(const Vector &amp; u_tm1, const Vector &amp; z)<br>Filter data to produce causal state estimates.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-f">f</a></strong>(const Vector &amp; u, bool do_add_noise =false)<br>system dynamics function</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-u">n_u</a></strong>() const<br>Get number of inputs.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-x">n_x</a></strong>() const<br>Get number of states.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-y">n_y</a></strong>() const<br>Get number of outputs.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-dt">dt</a></strong>() const<br>Get sample period.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x">x</a></strong>() const<br>Get current state.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p">P</a></strong>() const<br>Get covariance of state estimate.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m">m</a></strong>() const<br>Get current process disturbance/bias.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p-m">P_m</a></strong>() const<br>Get covariance of process disturbance estimate.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-cx">cx</a></strong>() const<br>Get C*x.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-y">y</a></strong>() const<br>Get output.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x0">x0</a></strong>() const<br>Get initial state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m0">m0</a></strong>() const<br>Get initial disturbance.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-a">A</a></strong>() const<br>Get state matrix.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-b">B</a></strong>() const<br>Get input matrix.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-g">g</a></strong>() const<br>Get input gain/conversion factor.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-c">C</a></strong>() const<br>Get output matrix.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-d">d</a></strong>() const<br>Get output bias.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke">Ke</a></strong>() const<br>Get estimator gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke-m">Ke_m</a></strong>() const<br>Get estimator gain for process disturbance (m)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q">Q</a></strong>()<br>Get process noise covariance.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q-m">Q_m</a></strong>()<br>Get process noise covariance of disturbance evoluation.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0">P0</a></strong>()<br>Get covariance of initial state.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0-m">P0_m</a></strong>()<br>Get covariance of initial process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-a">set_A</a></strong>(const Matrix &amp; A)<br>Set state matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-b">set_B</a></strong>(const Matrix &amp; B)<br>Set input matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-m">set_m</a></strong>(const Vector &amp; m, bool do_force_assign =false)<br>Set process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-g">set_g</a></strong>(const Vector &amp; g)<br>Set input gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q-m">set_Q_m</a></strong>(const Matrix &amp; Q_m)<br>Set process noise covariance of disturbance evoluation.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x0">set_x0</a></strong>(const Vector &amp; x0)<br>Set initial state.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0">set_P0</a></strong>(const Matrix &amp; P0)<br>Set covariance of initial state.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0-m">set_P0_m</a></strong>(const Matrix &amp; P0_m)<br>Set covariance of initial process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-c">set_C</a></strong>(const Matrix &amp; C)<br>Set output matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-d">set_d</a></strong>(const Vector &amp; d)<br>Set output bias.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x">set_x</a></strong>(const Vector &amp; x)<br>Set state of system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-reset">Reset</a></strong>()<br>Reset system variables.</td> + </tr> + <tr> + <td>std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-nstep-pred-block">nstep_pred_block</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; u, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; z, size_t n_pred =1)</td> + </tr> + </tbody> +</table> +<p><strong>Protected Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-initvars">InitVars</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> p0 =kDefaultP0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> q0 =kDefaultQ0)</td> + </tr> + </tbody> +</table> +<p><strong>Public Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-do-adapt-m">do_adapt_m</a></strong> <br>whether to adaptively estimate disturbance m</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x-">x_</a></strong> <br>state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-">P_</a></strong> <br>covariance of state estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m-">m_</a></strong> <br>process disturbance</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-m-">P_m_</a></strong> <br>covariance of disturbance estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-cx-">cx_</a></strong> <br>C*x.</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-y-">y_</a></strong> <br>output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-z-">z_</a></strong> <br>measurement</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x0-">x0_</a></strong> <br>initial state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-">P0_</a></strong> <br>covariance of initial state estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m0-">m0_</a></strong> <br>initial process disturbance</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-m-">P0_m_</a></strong> <br>covariance of initial disturbance est.</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-a-">A_</a></strong> <br>state matrix</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-b-">B_</a></strong> <br>input matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-g-">g_</a></strong> <br>input gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-">Q_</a></strong> <br>covariance of process noise</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-m-">Q_m_</a></strong> <br>covariance of disturbance random walk</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-c-">C_</a></strong> <br>output matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-d-">d_</a></strong> <br>output bias</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-">Ke_</a></strong> <br>estimator gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-m-">Ke_m_</a></strong> <br>estimator gain for process disturbance</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="system"> + <strong>System</strong> + <a class="anchor" href="#system">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>System() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="system-1"> + <strong>System</strong> + <a class="anchor" href="#system-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>System( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_u, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_x, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_y, +</span></span><span style="display:flex;"><span> data_t dt, +</span></span><span style="display:flex;"><span> data_t p0 <span style="color:#f92672">=</span>kDefaultP0, +</span></span><span style="display:flex;"><span> data_t q0 <span style="color:#f92672">=</span>kDefaultQ0, +</span></span><span style="display:flex;"><span> data_t r0 <span style="color:#f92672">=</span>kDefaultR0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_u</strong> number of inputs (u)</li> +<li><strong>n_x</strong> number of states (x)</li> +<li><strong>n_y</strong> number of outputs (y)</li> +<li><strong>dt</strong> sample period</li> +<li><strong>p0</strong> [optional] initial diagonal elements of state estimate covariance (P)</li> +<li><strong>q0</strong> [optional] initial diagonal elements of process noise covariance (Q)</li> +<li><strong>r0</strong> [optional] initial diagonal elements of output noise covariance (R)</li> +</ul> +<hr> +<h3 id="simulate"> + <strong>Simulate</strong> + <a class="anchor" href="#simulate">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> Simulate( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> u_tm1 +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>u_tm1</strong> input at t-1</li> +</ul> +<p><strong>Return</strong>: z measurement</p> +<p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-simulate">lds::System::Simulate</a></p> +<p>Simulate system and produce measurement</p> +<hr> +<h3 id="r"> + <strong>R</strong> + <a class="anchor" href="#r">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> +</span></span></code></pre></div><hr> +<h3 id="set_"> + <strong>set_Q</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Q( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-1"> + <strong>set_R</strong> + <a class="anchor" href="#set_-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_R( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-2"> + <strong>set_Ke</strong> + <a class="anchor" href="#set_-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Ke( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Ke +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="set_-3"> + <strong>set_Ke_m</strong> + <a class="anchor" href="#set_-3">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> set_Ke_m( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Ke_m +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><hr> +<h3 id="print"> + <strong>Print</strong> + <a class="anchor" href="#print">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Print() +</span></span></code></pre></div><hr> +<h2 id="protected-function-details"> + Protected Function Details + <a class="anchor" href="#protected-function-details">#</a> +</h2> +<h3 id="h"> + <strong>h</strong> + <a class="anchor" href="#h">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> h() <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h">lds::System::h</a></p> +<hr> +<h3 id="h_"> + <strong>h_</strong> + <a class="anchor" href="#h_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> Vector h_( +</span></span><span style="display:flex;"><span> Vector x +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h-">lds::System::h_</a></p> +<hr> +<h3 id="recurseke"> + <strong>RecurseKe</strong> + <a class="anchor" href="#recurseke">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> RecurseKe() <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-recurseke">lds::System::RecurseKe</a></p> +<hr> +<h2 id="protected-attribute-details"> + Protected Attribute Details + <a class="anchor" href="#protected-attribute-details">#</a> +</h2> +<h3 id="r_"> + <strong>R_</strong> + <a class="anchor" href="#r_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix R_; +</span></span></code></pre></div><hr> +<h3 id="do_"> + <strong>do_recurse_Ke_</strong> + <a class="anchor" href="#do_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">bool</span> do_recurse_Ke_ {}; +</span></span></code></pre></div><hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussiansystem">lds::gaussian::System</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#protected-attributes">Protected Attributes</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#system"><strong>System</strong></a></li> + <li><a href="#system-1"><strong>System</strong></a></li> + <li><a href="#simulate"><strong>Simulate</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + <li><a href="#set_"><strong>set_Q</strong></a></li> + <li><a href="#set_-1"><strong>set_R</strong></a></li> + <li><a href="#set_-2"><strong>set_Ke</strong></a></li> + <li><a href="#set_-3"><strong>set_Ke_m</strong></a></li> + <li><a href="#print"><strong>Print</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#h_"><strong>h_</strong></a></li> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + </ul> + </li> + <li><a href="#protected-attribute-details">Protected Attribute Details</a> + <ul> + <li><a href="#r_"><strong>R_</strong></a></li> + <li><a href="#do_"><strong>do_recurse_Ke_</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1_controller/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1_controller/index.html new file mode 100644 index 00000000..57715cc2 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1poisson_1_1_controller/index.html @@ -0,0 +1,560 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS Controller Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::poisson::Controller"> + <meta property="og:description" content="PLDS Controller Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::poisson::Controller | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::poisson::Controller</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissoncontroller">lds::poisson::Controller</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldspoissoncontroller"> + lds::poisson::Controller + <a class="anchor" href="#ldspoissoncontroller">#</a> +</h1> +<p>PLDS <a href="">Controller</a> Type. +<br /> <code>#include &lt;lds_poisson_ctrl.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/#function-set-y-ref">set_y_ref</a></strong>(const Vector &amp; y_ref) override<br>Set reference output.</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;&amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a> by moving the system object.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control">Control</a></strong>(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step)</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference">ControlOutputReference</a></strong>(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step)</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys">sys</a></strong>() const</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc">Kc</a></strong>() const<br>Get state feedback controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty">Kc_inty</a></strong>() const<br>Get integral controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u">Kc_u</a></strong>() const<br>Get input feedback controller gain.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design">g_design</a></strong>() const<br>Get input gain used in controller design.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref">u_ref</a></strong>() const<br>Get reference input.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref">x_ref</a></strong>() const<br>Get reference state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref">y_ref</a></strong>() const<br>Get reference output.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type">control_type</a></strong>() const<br>Get controller type.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu">tau_awu</a></strong>() const<br>Get time constant of anti-integral-windup.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb">u_lb</a></strong>() const<br>Get control lower bound.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub">u_ub</a></strong>() const<br>Get control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys">set_sys</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys)<br>Set system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design">set_g_design</a></strong>(const Vector &amp; g_design)<br>Set input gain used in controller design (g_design)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref">set_u_ref</a></strong>(const Vector &amp; u_ref)<br>Set reference input (u_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref">set_x_ref</a></strong>(const Vector &amp; x_ref)<br>Set reference state (x_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc">set_Kc</a></strong>(const Matrix &amp; Kc)<br>Set state controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const Matrix &amp; Kc_inty)<br>Set integral controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u">set_Kc_u</a></strong>(const Matrix &amp; Kc_u)<br>Set input controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu">set_tau_awu</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tau)<br>Set time constant of anti-integral-windup.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type">set_control_type</a></strong>(size_t control_type)<br>Sets the control type.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb">set_u_lb</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb)<br>sets control lower bound</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub">set_u_ub</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub)<br>Sets control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset">Reset</a></strong>()<br>reset system and control variables.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print">Print</a></strong>()<br>prints variables to stdout</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-">sys_</a></strong> <br>underlying LDS</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-">u_</a></strong> <br>control signal</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-">u_return_</a></strong> <br>control signal that is <em>returned</em> to user</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-">g_design_</a></strong> <br>input gain of the system used for controller design</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-">u_ref_</a></strong> <br>reference input</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-">u_ref_prev_</a></strong> <br>reference input at previous time step</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-">x_ref_</a></strong> <br>reference state</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-">y_ref_</a></strong> <br>reference output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-">cx_ref_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-">Kc_</a></strong> <br>state controller gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-">Kc_u_</a></strong> <br>input controller gain (optional when control updates \deltaU)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-">Kc_inty_</a></strong> <br>integral controller gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-">du_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-">dv_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-">v_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-">dv_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-">v_</a></strong> <br>Control after g inversion (e.g., control in physical units)</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-">int_e_</a></strong> <br>integrated error</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-">int_e_awu_adjust_</a></strong> <br>anti-windup adjustment to intE</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-">u_sat_</a></strong> <br>control signal after saturation (for antiWindup)</td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-">do_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-">do_lock_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-">u_saturated_</a></strong> <br>whether control signal has reached saturation limits</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-">u_lb_</a></strong> <br>lower bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-">u_ub_</a></strong> <br>upper bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-">tau_awu_</a></strong> <br>antiwindup time constant</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-">k_awu_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-">t_since_control_onset_</a></strong> <br>time since control epoch onset</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-">control_type_</a></strong> <br>controller type</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="set_"> + <strong>set_y_ref</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_y_ref( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> y_ref +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref">lds::Controller::set_y_ref</a></p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissoncontroller">lds::poisson::Controller</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1_fit/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1_fit/index.html new file mode 100644 index 00000000..c0d4f256 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1poisson_1_1_fit/index.html @@ -0,0 +1,553 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS Fit Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::poisson::Fit"> + <meta property="og:description" content="PLDS Fit Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::poisson::Fit | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::poisson::Fit</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonfit">lds::poisson::Fit</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#fit"><strong>Fit</strong></a></li> + <li><a href="#fit-1"><strong>Fit</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#set_"><strong>set_R</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldspoissonfit"> + lds::poisson::Fit + <a class="anchor" href="#ldspoissonfit">#</a> +</h1> +<p>PLDS <a href="">Fit</a> Type. +<br /> <code>#include &lt;lds_poisson_fit.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-fit">Fit</a></strong>() =default</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-fit">Fit</a></strong>(size_t n_u, size_t n_x, size_t n_y, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">Fit</a>.</td> + </tr> + <tr> + <td>virtual View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-h">h</a></strong>(Matrix &amp; y, const Matrix &amp; x, size_t t) override<br>output function</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-set-r">set_R</a></strong>(const Matrix &amp; R) override<br>sets output noise covariance (if any)</td> + </tr> + <tr> + <td>virtual const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/#function-r">R</a></strong>() const override</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-~fit">~Fit</a></strong>() =default</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-u">n_u</a></strong>() const<br>gets number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-x">n_x</a></strong>() const<br>gets number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-n-y">n_y</a></strong>() const<br>gets number of outputs</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-dt">dt</a></strong>() const<br>gets sample period</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-a">A</a></strong>() const<br>gets state matrix</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-b">B</a></strong>() const<br>gets input matrix</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-g">g</a></strong>() const<br>gets input gain</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-m">m</a></strong>() const<br>gets process disturbance</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-q">Q</a></strong>() const<br>gets process noise covariance</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-x0">x0</a></strong>() const<br>gets initial state estimate</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-p0">P0</a></strong>() const<br>gets covariance of initial state estimate</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-c">C</a></strong>() const<br>gets output matrix</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-d">d</a></strong>() const<br>gets output bias</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-a">set_A</a></strong>(const Matrix &amp; A)<br>sets state matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-b">set_B</a></strong>(const Matrix &amp; B)<br>sets input matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-g">set_g</a></strong>(const Vector &amp; g)<br>sets input gain/conversion factor</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-m">set_m</a></strong>(const Vector &amp; m)<br>sets process disturbance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-q">set_Q</a></strong>(const Matrix &amp; Q)<br>sets process noise covariance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-x0">set_x0</a></strong>(const Vector &amp; x0)<br>sets initial state estimate</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-p0">set_P0</a></strong>(const Matrix &amp; P0)<br>sets initial state estimate covariance</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-c">set_C</a></strong>(const Matrix &amp; C)<br>sets output matrix</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-d">set_d</a></strong>(const Vector &amp; d)<br>sets output bias</td> + </tr> + <tr> + <td>View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f">f</a></strong>(Matrix &amp; x, const Matrix &amp; u, size_t t)<br>system dynamics function</td> + </tr> + <tr> + <td>View</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-f">f</a></strong>(Matrix &amp; x_pre, const Matrix &amp; x_post, const Matrix &amp; u, size_t t)<br>system dynamics function</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-a-">A_</a></strong> <br>state matrix</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-b-">B_</a></strong> <br>input matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-g-">g_</a></strong> <br>input gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-m-">m_</a></strong> <br>process noise mean</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-q-">Q_</a></strong> <br>process noise cov</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-c-">C_</a></strong> <br>output matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-d-">d_</a></strong> <br>output bias</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-r-">R_</a></strong> <br>measurement noise</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-x0-">x0_</a></strong> <br>initial state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-p0-">P0_</a></strong> <br>initial covar</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="fit"> + <strong>Fit</strong> + <a class="anchor" href="#fit">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Fit() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="fit-1"> + <strong>Fit</strong> + <a class="anchor" href="#fit-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> Fit( +</span></span><span style="display:flex;"><span> size_t n_u, +</span></span><span style="display:flex;"><span> size_t n_x, +</span></span><span style="display:flex;"><span> size_t n_y, +</span></span><span style="display:flex;"><span> data_t dt +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_u</strong> number of inputs</li> +<li><strong>n_x</strong> number of states</li> +<li><strong>n_y</strong> number of outputs</li> +<li><strong>dt</strong> sample period</li> +</ul> +<hr> +<h3 id="h"> + <strong>h</strong> + <a class="anchor" href="#h">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> View h( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> y, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> x, +</span></span><span style="display:flex;"><span> size_t t +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>y</strong> output estimate (over time)</li> +<li><strong>x</strong> state estimate (over time)</li> +<li><strong>t</strong> time index</li> +</ul> +<p><strong>Return</strong>: output</p> +<p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-h">lds::Fit::h</a></p> +<hr> +<h3 id="set_"> + <strong>set_R</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_R( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-set-r">lds::Fit::set_R</a></p> +<hr> +<h3 id="r"> + <strong>R</strong> + <a class="anchor" href="#r">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/#function-r">lds::Fit::R</a></p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonfit">lds::poisson::Fit</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#fit"><strong>Fit</strong></a></li> + <li><a href="#fit-1"><strong>Fit</strong></a></li> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#set_"><strong>set_R</strong></a></li> + <li><a href="#r"><strong>R</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/index.html new file mode 100644 index 00000000..8de2b3b8 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/index.html @@ -0,0 +1,465 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS E-M Fit Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::poisson::FitEM"> + <meta property="og:description" content="PLDS E-M Fit Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::poisson::FitEM | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::poisson::FitEM</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonfitem">lds::poisson::FitEM</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldspoissonfitem"> + lds::poisson::FitEM + <a class="anchor" href="#ldspoissonfitem">#</a> +</h1> +<p>PLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">Fit</a> Type. <a href="#detailed-description">More&hellip;</a></p> +<p><br /> <code>#include &lt;lds_poisson_fit_em.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></p> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>(size_t n_x, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em">EM</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> &amp; fit0, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">EM</a><a href="">Fit</a> type.</td> + </tr> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em">~EM</a></strong>() =default</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run">Run</a></strong>(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tol =1e-2)<br>Runs fitting by Expectation(E)-Maximization(M)</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata">ReturnData</a></strong>()<br>Returns the input/output data to caller.</td> + </tr> + <tr> + <td>const std::vector&lt; Matrix &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x">x</a></strong>() const<br>gets estimated state (over time)</td> + </tr> + <tr> + <td>const std::vector&lt; Matrix &gt; &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y">y</a></strong>() const<br>gets estimated output (over time)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t">sum_E_x_t_x_t</a></strong>() const<br>gets state-input covariance</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1">sum_E_xu_tm1_xu_tm1</a></strong>() const<br>gets state-input covariance (t-minus-1)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1">sum_E_xu_t_xu_tm1</a></strong>() const<br>gets single lag state-input covariance</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot">n_t_tot</a></strong>()<br>total number of time samples</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta">theta</a></strong>() const<br>gets parameters updated in M step</td> + </tr> + </tbody> +</table> +<p><strong>Protected Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation">Expectation</a></strong>(bool force_common_initial =false)<br>Expectation step.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization">Maximization</a></strong>(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)<br>Maximization step.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics">MaximizeDynamics</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq">MaximizeQ</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial">MaximizeInitial</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth">Smooth</a></strong>(bool force_common_initial)<br>get smoothed estimates</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset">Reset</a></strong>()<br>reset to initial conditions</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars">InitVars</a></strong>()<br>Initializes the variables.</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta">UpdateTheta</a></strong>()<br>updates parameter list, theta</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-">u_</a></strong> <br>input training data</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-">z_</a></strong> <br>measurement training data</td> + </tr> + <tr> + <td>std::vector&lt; Matrix &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-">x_</a></strong> <br>state estimate</td> + </tr> + <tr> + <td>std::vector&lt; Cube &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-">P_</a></strong> <br>state estimate cov</td> + </tr> + <tr> + <td>std::vector&lt; Cube &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-">P_t_tm1_</a></strong> <br>single-lag state covariance</td> + </tr> + <tr> + <td>std::vector&lt; Matrix &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-">y_</a></strong> <br>output estimate</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-">diag_y_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-">sum_E_x_t_x_t_</a></strong> <br>state covariance (current time)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-">sum_E_xu_tm1_xu_tm1_</a></strong> <br>state-input covariance (t-minus-1)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-">sum_E_xu_t_xu_tm1_</a></strong> <br>single lag state-input covariance</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-">fit_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-">theta_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-">n_trials_</a></strong> <br>number of input/output data sequences</td> + </tr> + <tr> + <td>std::vector&lt; size_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-">n_t_</a></strong> <br>number of time steps</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-">n_t_tot_</a></strong> <br>total number of time steps across trials</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">lds</span><span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>FitEM; +</span></span></code></pre></div><pre tabindex="0"><code> This type is used in the process of fitting PLDS models by + expectation-maximization (EM). +</code></pre><hr> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonfitem">lds::poisson::FitEM</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/index.html new file mode 100644 index 00000000..c4a186e3 --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/index.html @@ -0,0 +1,392 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Subspace Identification (SSID) for PLDS."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::poisson::FitSSID"> + <meta property="og:description" content="Subspace Identification (SSID) for PLDS."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::poisson::FitSSID | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::poisson::FitSSID</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonfitssid">lds::poisson::FitSSID</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldspoissonfitssid"> + lds::poisson::FitSSID + <a class="anchor" href="#ldspoissonfitssid">#</a> +</h1> +<p>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for PLDS. +<br /> <code>#include &lt;lds_poisson_fit_ssid.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></p> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid">SSID</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> type.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid">SSID</a></strong>(size_t n_x, size_t n_h, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; u_train, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &amp;&amp; z_train, const Vector &amp; d =Vector(1).fill(-kInf))<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a> type.</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a>, Vector &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run">Run</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt">SSIDWt</a> ssid_wt)<br>Runs fitting by subspace identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>)</td> + </tr> + <tr> + <td>std::tuple&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata">ReturnData</a></strong>()<br>Returns the I/O data to caller.</td> + </tr> + </tbody> +</table> +<p><strong>Protected Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd">CalcD</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> t_silence =0.1, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> thresh_silence =0.001)<br>Using periods of silence in inputs (u), calculates the output \ bias (d)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat">CreateHankelDataMat</a></strong>()<br>Creates the block-hankel I/O data matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd">CalcSVD</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt">SSIDWt</a> wt)<br>performs the singular value decomposition (SVD)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve">Solve</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> wt_dc)<br>solves for LDS parameters</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs">RecomputeExtObs</a></strong>()<br>recompute extended observability matrix from estimates of A, C</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID&lt; Fit &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-">u_</a></strong> <br>input training data</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-">z_</a></strong> <br>measurement training data</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-">D_</a></strong> <br>block-Hankel I/O data matrix</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">Fit</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-">fit_</a></strong> <br>fit</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-">g_dc_</a></strong> <br>I/O gain @ DC.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-">n_h_</a></strong></td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-">n_trials_</a></strong> <br>number of input/output data sequences</td> + </tr> + <tr> + <td>std::vector&lt; size_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-">n_t_</a></strong> <br>number of time steps</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-">n_t_tot_</a></strong> <br>total number of time steps across trials</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-">L_</a></strong> <br>lower triangle decomp of covariance matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-">s_</a></strong> <br>singular values</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-">ext_obs_t_</a></strong> <br>extended observability matrix</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonfitssid">lds::poisson::FitSSID</a> + <ul> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/index.html new file mode 100644 index 00000000..95f3c0aa --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/index.html @@ -0,0 +1,658 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Poisson-observation SwitchedController Type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::poisson::SwitchedController"> + <meta property="og:description" content="Poisson-observation SwitchedController Type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::poisson::SwitchedController | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::poisson::SwitchedController</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonswitchedcontroller">lds::poisson::SwitchedController</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldspoissonswitchedcontroller"> + lds::poisson::SwitchedController + <a class="anchor" href="#ldspoissonswitchedcontroller">#</a> +</h1> +<p>Poisson-observation <a href="">SwitchedController</a> Type. +<br /> <code>#include &lt;lds_poisson_sctrl.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController&lt; System &gt;</a>, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/#function-set-y-ref">set_y_ref</a></strong>(const Vector &amp; y_ref) override<br>Set reference output.</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>(const std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp; systems, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switchedcontroller">SwitchedController</a></strong>(std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt; &amp;&amp; systems, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">SwitchedController</a> (moves systems).</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-switch">Switch</a></strong>(size_t idx, bool do_force_switch =false)<br>Switch to a different sub-system/controller.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc">set_Kc</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc)<br>sets state feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc">set_Kc</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc)<br>sets state feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc_inty)<br>sets integral feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc_inty)<br>sets integral feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u">set_Kc_u</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp; Kc_u)<br>sets input feedback gains</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-kc-u">set_Kc_u</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt;&gt; &amp;&amp; Kc_u)<br>sets input feedback gains (moving)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design">set_g_design</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp; g)<br>sets input gain used during controller design</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#function-set-g-design">set_g_design</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a> &amp;&amp; g)<br>sets input gain used during controller design (moving)</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-systems-">systems_</a></strong> <br>underlying sub-systems which are switched between</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-n-sys-">n_sys_</a></strong> <br>number of systems</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-idx-">idx_</a></strong> <br>current system/controller index.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-list-">Kc_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-inty-list-">Kc_inty_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-kc-u-list-">Kc_u_list_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">UniformVectorList</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/#variable-g-design-list-">g_design_list_</a></strong></td> + </tr> + </tbody> +</table> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller">Controller</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;&amp; sys, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub, size_t control_type =0)<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">Controller</a> by moving the system object.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control">Control</a></strong>(const Vector &amp; z, bool do_control =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal (single-step)</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference">ControlOutputReference</a></strong>(const Vector &amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_soft_start =0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> sigma_u_noise =0, bool do_reset_at_control_onset =true)<br>updates control signal, given previously-set (single-step)</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys">sys</a></strong>() const</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc">Kc</a></strong>() const<br>Get state feedback controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty">Kc_inty</a></strong>() const<br>Get integral controller gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u">Kc_u</a></strong>() const<br>Get input feedback controller gain.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design">g_design</a></strong>() const<br>Get input gain used in controller design.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref">u_ref</a></strong>() const<br>Get reference input.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref">x_ref</a></strong>() const<br>Get reference state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref">y_ref</a></strong>() const<br>Get reference output.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type">control_type</a></strong>() const<br>Get controller type.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu">tau_awu</a></strong>() const<br>Get time constant of anti-integral-windup.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb">u_lb</a></strong>() const<br>Get control lower bound.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub">u_ub</a></strong>() const<br>Get control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys">set_sys</a></strong>(const <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a> &amp; sys)<br>Set system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design">set_g_design</a></strong>(const Vector &amp; g_design)<br>Set input gain used in controller design (g_design)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref">set_u_ref</a></strong>(const Vector &amp; u_ref)<br>Set reference input (u_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref">set_x_ref</a></strong>(const Vector &amp; x_ref)<br>Set reference state (x_ref)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc">set_Kc</a></strong>(const Matrix &amp; Kc)<br>Set state controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty">set_Kc_inty</a></strong>(const Matrix &amp; Kc_inty)<br>Set integral controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u">set_Kc_u</a></strong>(const Matrix &amp; Kc_u)<br>Set input controller gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu">set_tau_awu</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> tau)<br>Set time constant of anti-integral-windup.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type">set_control_type</a></strong>(size_t control_type)<br>Sets the control type.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb">set_u_lb</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_lb)<br>sets control lower bound</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub">set_u_ub</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> u_ub)<br>Sets control upper bound.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset">Reset</a></strong>()<br>reset system and control variables.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print">Print</a></strong>()<br>prints variables to stdout</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller&lt; System &gt;</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">System</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-">sys_</a></strong> <br>underlying LDS</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-">u_</a></strong> <br>control signal</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-">u_return_</a></strong> <br>control signal that is <em>returned</em> to user</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-">g_design_</a></strong> <br>input gain of the system used for controller design</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-">u_ref_</a></strong> <br>reference input</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-">u_ref_prev_</a></strong> <br>reference input at previous time step</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-">x_ref_</a></strong> <br>reference state</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-">y_ref_</a></strong> <br>reference output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-">cx_ref_</a></strong></td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-">Kc_</a></strong> <br>state controller gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-">Kc_u_</a></strong> <br>input controller gain (optional when control updates \deltaU)</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-">Kc_inty_</a></strong> <br>integral controller gain</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-">du_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-">dv_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-">v_ref_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-">dv_</a></strong></td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-">v_</a></strong> <br>Control after g inversion (e.g., control in physical units)</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-">int_e_</a></strong> <br>integrated error</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-">int_e_awu_adjust_</a></strong> <br>anti-windup adjustment to intE</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-">u_sat_</a></strong> <br>control signal after saturation (for antiWindup)</td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-">do_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-">do_lock_control_prev_</a></strong></td> + </tr> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-">u_saturated_</a></strong> <br>whether control signal has reached saturation limits</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-">u_lb_</a></strong> <br>lower bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-">u_ub_</a></strong> <br>upper bound on control</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-">tau_awu_</a></strong> <br>antiwindup time constant</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-">k_awu_</a></strong></td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-">t_since_control_onset_</a></strong> <br>time since control epoch onset</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-">control_type_</a></strong> <br>controller type</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="set_"> + <strong>set_y_ref</strong> + <a class="anchor" href="#set_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> set_y_ref( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> y_ref +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref">lds::Controller::set_y_ref</a></p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonswitchedcontroller">lds::poisson::SwitchedController</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#set_"><strong>set_y_ref</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/classlds_1_1poisson_1_1_system/index.html b/docs/docs/api/classes/classlds_1_1poisson_1_1_system/index.html new file mode 100644 index 00000000..ed43dcea --- /dev/null +++ b/docs/docs/api/classes/classlds_1_1poisson_1_1_system/index.html @@ -0,0 +1,733 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Poisson System type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::poisson::System"> + <meta property="og:description" content="Poisson System type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::poisson::System | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::poisson::System</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonsystem">lds::poisson::System</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#system"><strong>System</strong></a></li> + <li><a href="#system-1"><strong>System</strong></a></li> + <li><a href="#simulate"><strong>Simulate</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#h_"><strong>h_</strong></a></li> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldspoissonsystem"> + lds::poisson::System + <a class="anchor" href="#ldspoissonsystem">#</a> +</h1> +<p>Poisson <a href="">System</a> type. +<br /> <code>#include &lt;lds_poisson_sys.h&gt;</code></p> +<p>Inherits from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></p> +<h2 id="public-functions"> + Public Functions + <a class="anchor" href="#public-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-system">System</a></strong>() =default<br>Constructs a new <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">System</a>.</td> + </tr> + <tr> + <td></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-system">System</a></strong>(std::size_t n_u, std::size_t n_x, std::size_t n_y, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> dt, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> p0 =kDefaultP0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> q0 =kDefaultQ0)<br>Constructs a new Poisson <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">System</a>.</td> + </tr> + <tr> + <td>virtual const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-simulate">Simulate</a></strong>(const Vector &amp; u_tm1) override<br>Simulate system measurement.</td> + </tr> + </tbody> +</table> +<h2 id="protected-functions"> + Protected Functions + <a class="anchor" href="#protected-functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h">h</a></strong>() override<br><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">System</a> output function.</td> + </tr> + <tr> + <td>virtual Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-h-">h_</a></strong>(Vector x) override<br><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">System</a> output function: stateless.</td> + </tr> + <tr> + <td>virtual void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/#function-recurseke">RecurseKe</a></strong>() override<br>Recursively recalculate estimator gain (Ke)</td> + </tr> + </tbody> +</table> +<h2 id="additional-inherited-members"> + Additional inherited members + <a class="anchor" href="#additional-inherited-members">#</a> +</h2> +<p><strong>Public Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>virtual</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-~system">~System</a></strong>()</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-filter">Filter</a></strong>(const Vector &amp; u_tm1, const Vector &amp; z)<br>Filter data to produce causal state estimates.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-f">f</a></strong>(const Vector &amp; u, bool do_add_noise =false)<br>system dynamics function</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-u">n_u</a></strong>() const<br>Get number of inputs.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-x">n_x</a></strong>() const<br>Get number of states.</td> + </tr> + <tr> + <td>size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-n-y">n_y</a></strong>() const<br>Get number of outputs.</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-dt">dt</a></strong>() const<br>Get sample period.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x">x</a></strong>() const<br>Get current state.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p">P</a></strong>() const<br>Get covariance of state estimate.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m">m</a></strong>() const<br>Get current process disturbance/bias.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p-m">P_m</a></strong>() const<br>Get covariance of process disturbance estimate.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-cx">cx</a></strong>() const<br>Get C*x.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-y">y</a></strong>() const<br>Get output.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-x0">x0</a></strong>() const<br>Get initial state.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-m0">m0</a></strong>() const<br>Get initial disturbance.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-a">A</a></strong>() const<br>Get state matrix.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-b">B</a></strong>() const<br>Get input matrix.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-g">g</a></strong>() const<br>Get input gain/conversion factor.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-c">C</a></strong>() const<br>Get output matrix.</td> + </tr> + <tr> + <td>const Vector &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-d">d</a></strong>() const<br>Get output bias.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke">Ke</a></strong>() const<br>Get estimator gain.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-ke-m">Ke_m</a></strong>() const<br>Get estimator gain for process disturbance (m)</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q">Q</a></strong>()<br>Get process noise covariance.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-q-m">Q_m</a></strong>()<br>Get process noise covariance of disturbance evoluation.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0">P0</a></strong>()<br>Get covariance of initial state.</td> + </tr> + <tr> + <td>const Matrix &amp;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-p0-m">P0_m</a></strong>()<br>Get covariance of initial process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-a">set_A</a></strong>(const Matrix &amp; A)<br>Set state matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-b">set_B</a></strong>(const Matrix &amp; B)<br>Set input matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-m">set_m</a></strong>(const Vector &amp; m, bool do_force_assign =false)<br>Set process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-g">set_g</a></strong>(const Vector &amp; g)<br>Set input gain.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q">set_Q</a></strong>(const Matrix &amp; Q)<br>Set process noise covariance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-q-m">set_Q_m</a></strong>(const Matrix &amp; Q_m)<br>Set process noise covariance of disturbance evoluation.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x0">set_x0</a></strong>(const Vector &amp; x0)<br>Set initial state.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0">set_P0</a></strong>(const Matrix &amp; P0)<br>Set covariance of initial state.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-p0-m">set_P0_m</a></strong>(const Matrix &amp; P0_m)<br>Set covariance of initial process disturbance.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-c">set_C</a></strong>(const Matrix &amp; C)<br>Set output matrix.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-d">set_d</a></strong>(const Vector &amp; d)<br>Set output bias.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-set-x">set_x</a></strong>(const Vector &amp; x)<br>Set state of system.</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-reset">Reset</a></strong>()<br>Reset system variables.</td> + </tr> + <tr> + <td>std::vector&lt; <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-nstep-pred-block">nstep_pred_block</a></strong>(<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; u, <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">UniformMatrixList</a>&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2">kMatFreeDim2</a> &gt; z, size_t n_pred =1)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-print">Print</a></strong>()<br>Print system variables to stdout.</td> + </tr> + </tbody> +</table> +<p><strong>Protected Functions inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-initvars">InitVars</a></strong>(<a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> p0 =kDefaultP0, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> q0 =kDefaultQ0)</td> + </tr> + </tbody> +</table> +<p><strong>Public Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>bool</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-do-adapt-m">do_adapt_m</a></strong> <br>whether to adaptively estimate disturbance m</td> + </tr> + </tbody> +</table> +<p><strong>Protected Attributes inherited from <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong></p> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-x-">n_x_</a></strong> <br>number of states</td> + </tr> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-u-">n_u_</a></strong> <br>number of inputs</td> + </tr> + <tr> + <td>std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-n-y-">n_y_</a></strong> <br>number of outputs</td> + </tr> + <tr> + <td><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-dt-">dt_</a></strong> <br>sample period</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x-">x_</a></strong> <br>state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-">P_</a></strong> <br>covariance of state estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m-">m_</a></strong> <br>process disturbance</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p-m-">P_m_</a></strong> <br>covariance of disturbance estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-cx-">cx_</a></strong> <br>C*x.</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-y-">y_</a></strong> <br>output</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-z-">z_</a></strong> <br>measurement</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-x0-">x0_</a></strong> <br>initial state</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-">P0_</a></strong> <br>covariance of initial state estimate</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-m0-">m0_</a></strong> <br>initial process disturbance</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-p0-m-">P0_m_</a></strong> <br>covariance of initial disturbance est.</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-a-">A_</a></strong> <br>state matrix</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-b-">B_</a></strong> <br>input matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-g-">g_</a></strong> <br>input gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-">Q_</a></strong> <br>covariance of process noise</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-q-m-">Q_m_</a></strong> <br>covariance of disturbance random walk</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-c-">C_</a></strong> <br>output matrix</td> + </tr> + <tr> + <td>Vector</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-d-">d_</a></strong> <br>output bias</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-">Ke_</a></strong> <br>estimator gain</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#variable-ke-m-">Ke_m_</a></strong> <br>estimator gain for process disturbance</td> + </tr> + </tbody> +</table> +<hr> +<hr> +<h2 id="public-function-details"> + Public Function Details + <a class="anchor" href="#public-function-details">#</a> +</h2> +<h3 id="system"> + <strong>System</strong> + <a class="anchor" href="#system">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>System() <span style="color:#f92672">=</span><span style="color:#66d9ef">default</span> +</span></span></code></pre></div><hr> +<h3 id="system-1"> + <strong>System</strong> + <a class="anchor" href="#system-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>System( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_u, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_x, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_y, +</span></span><span style="display:flex;"><span> data_t dt, +</span></span><span style="display:flex;"><span> data_t p0 <span style="color:#f92672">=</span>kDefaultP0, +</span></span><span style="display:flex;"><span> data_t q0 <span style="color:#f92672">=</span>kDefaultQ0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>n_u</strong> number of inputs</li> +<li><strong>n_x</strong> number of states</li> +<li><strong>n_y</strong> number of outputs</li> +<li><strong>dt</strong> sample period</li> +<li><strong>p0</strong> [optional] initial diagonal elements of state estimate covariance (P)</li> +<li><strong>q0</strong> [optional] initial diagonal elements of process noise covariance (Q)</li> +</ul> +<hr> +<h3 id="simulate"> + <strong>Simulate</strong> + <a class="anchor" href="#simulate">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> Simulate( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> u_tm1 +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>u_tm1</strong> input at t-1</li> +</ul> +<p><strong>Return</strong>: z measurement</p> +<p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-simulate">lds::System::Simulate</a></p> +<p>Simulate system and produce measurement</p> +<hr> +<h2 id="protected-function-details"> + Protected Function Details + <a class="anchor" href="#protected-function-details">#</a> +</h2> +<h3 id="h"> + <strong>h</strong> + <a class="anchor" href="#h">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> h() <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h">lds::System::h</a></p> +<hr> +<h3 id="h_"> + <strong>h_</strong> + <a class="anchor" href="#h_">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">virtual</span> Vector h_( +</span></span><span style="display:flex;"><span> Vector x +</span></span><span style="display:flex;"><span>) <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-h-">lds::System::h_</a></p> +<hr> +<h3 id="recurseke"> + <strong>RecurseKe</strong> + <a class="anchor" href="#recurseke">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> RecurseKe() <span style="color:#66d9ef">override</span> +</span></span></code></pre></div><p><strong>Reimplements</strong>: <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/#function-recurseke">lds::System::RecurseKe</a></p> +<p>Recursively recalculate estimator gain (Ke).</p> +<p>References:</p> +<p>Smith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.</p> +<p>Eden UT, &hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.</p> +<hr> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoissonsystem">lds::poisson::System</a> + <ul> + <li><a href="#public-functions">Public Functions</a></li> + <li><a href="#protected-functions">Protected Functions</a></li> + <li><a href="#additional-inherited-members">Additional inherited members</a></li> + <li><a href="#public-function-details">Public Function Details</a> + <ul> + <li><a href="#system"><strong>System</strong></a></li> + <li><a href="#system-1"><strong>System</strong></a></li> + <li><a href="#simulate"><strong>Simulate</strong></a></li> + </ul> + </li> + <li><a href="#protected-function-details">Protected Function Details</a> + <ul> + <li><a href="#h"><strong>h</strong></a></li> + <li><a href="#h_"><strong>h_</strong></a></li> + <li><a href="#recurseke"><strong>RecurseKe</strong></a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/index.html b/docs/docs/api/classes/index.html new file mode 100644 index 00000000..93ce3684 --- /dev/null +++ b/docs/docs/api/classes/index.html @@ -0,0 +1,398 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Classes + # + + + +lds::Controller + + +lds::EM + + +lds::Fit LDS Fit Type. + + +lds::SSID + + +lds::SwitchedController SwitchedController Type. + + +lds::System Linear Dynamical System Type. + + +lds::UniformMatrixList + + +lds::UniformSystemList + + +lds::UniformVectorList + + +lds::gaussian::Controller Gaussian-observation Controller Type. + + +lds::gaussian::Fit GLDS Fit Type. + + +lds::gaussian::FitEM GLDS E-M Fit Type. + + +lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. + + +lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. + + +lds::gaussian::System Gaussian LDS Type. + + +lds::poisson::Controller PLDS Controller Type. + + +lds::poisson::Fit PLDS Fit Type. + + +lds::poisson::FitEM PLDS E-M Fit Type. + + +lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. + + +lds::poisson::SwitchedController Poisson-observation SwitchedController Type. + + +lds::poisson::System Poisson System type. + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Classes"> + <meta property="og:description" content="Classes # lds::Controller +lds::EM +lds::Fit LDS Fit Type. +lds::SSID +lds::SwitchedController SwitchedController Type. +lds::System Linear Dynamical System Type. +lds::UniformMatrixList +lds::UniformSystemList +lds::UniformVectorList +lds::gaussian::Controller Gaussian-observation Controller Type. +lds::gaussian::Fit GLDS Fit Type. +lds::gaussian::FitEM GLDS E-M Fit Type. +lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. +lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. +lds::gaussian::System Gaussian LDS Type. +lds::poisson::Controller PLDS Controller Type. +lds::poisson::Fit PLDS Fit Type. +lds::poisson::FitEM PLDS E-M Fit Type. +lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. +lds::poisson::SwitchedController Poisson-observation SwitchedController Type. +lds::poisson::System Poisson System type. +Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Classes | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/"class=active>Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Classes</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#classes">Classes</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h1> +<ul> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></strong> <br>LDS <a href="">Fit</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController</a></strong> <br><a href="">SwitchedController</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong> <br>Linear Dynamical <a href="">System</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">lds::UniformMatrixList</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">lds::UniformSystemList</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">lds::UniformVectorList</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/">lds::gaussian::Controller</a></strong> <br>Gaussian-observation <a href="">Controller</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">lds::gaussian::Fit</a></strong> <br>GLDS <a href="">Fit</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/">lds::gaussian::FitEM</a></strong> <br>GLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">Fit</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/">lds::gaussian::FitSSID</a></strong> <br>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for GLDS.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/">lds::gaussian::SwitchedController</a></strong> <br>Gaussian-observation <a href="">SwitchedController</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">lds::gaussian::System</a></strong> <br>Gaussian LDS Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/">lds::poisson::Controller</a></strong> <br>PLDS <a href="">Controller</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">lds::poisson::Fit</a></strong> <br>PLDS <a href="">Fit</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/">lds::poisson::FitEM</a></strong> <br>PLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">Fit</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/">lds::poisson::FitSSID</a></strong> <br>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for PLDS.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/">lds::poisson::SwitchedController</a></strong> <br>Poisson-observation <a href="">SwitchedController</a> Type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">lds::poisson::System</a></strong> <br>Poisson <a href="">System</a> type.</p> +</li> +</ul> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#classes">Classes</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/classes/index.xml b/docs/docs/api/classes/index.xml new file mode 100644 index 00000000..79e538d2 --- /dev/null +++ b/docs/docs/api/classes/index.xml @@ -0,0 +1,158 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Classes on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/</link> + <description>Recent content in Classes on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>lds::Controller</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/</guid> + <description>&lt;h1 id=&#34;ldscontroller&#34;&gt;&#xA; lds::Controller&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldscontroller&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherited by &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/&#34;&gt;lds::SwitchedController&amp;lt; System &amp;gt;&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/&#34;&gt;lds::gaussian::Controller&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/&#34;&gt;lds::poisson::Controller&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller&#34;&gt;Controller&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/&#34;&gt;Controller&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller&#34;&gt;Controller&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp; sys, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_lb, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_ub, size_t control_type =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/&#34;&gt;Controller&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller&#34;&gt;Controller&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp;&amp;amp; sys, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_lb, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_ub, size_t control_type =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/&#34;&gt;Controller&lt;/a&gt; by moving the system object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control&#34;&gt;Control&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; z, bool do_control =true, bool do_lock_control =false, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_soft_start =0, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_u_noise =0, bool do_reset_at_control_onset =true)&lt;br&gt;updates control signal (single-step)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference&#34;&gt;ControlOutputReference&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_soft_start =0, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_u_noise =0, bool do_reset_at_control_onset =true)&lt;br&gt;updates control signal, given previously-set (single-step)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys&#34;&gt;sys&lt;/a&gt;&lt;/strong&gt;() const&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc&#34;&gt;Kc&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get state feedback controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty&#34;&gt;Kc_inty&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get integral controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u&#34;&gt;Kc_u&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get input feedback controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design&#34;&gt;g_design&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get input gain used in controller design.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref&#34;&gt;u_ref&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get reference input.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref&#34;&gt;x_ref&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get reference state.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref&#34;&gt;y_ref&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get reference output.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type&#34;&gt;control_type&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu&#34;&gt;tau_awu&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get time constant of anti-integral-windup.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb&#34;&gt;u_lb&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get control lower bound.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub&#34;&gt;u_ub&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get control upper bound.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys&#34;&gt;set_sys&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp; sys)&lt;br&gt;Set system.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design&#34;&gt;set_g_design&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; g_design)&lt;br&gt;Set input gain used in controller design (g_design)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref&#34;&gt;set_u_ref&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; u_ref)&lt;br&gt;Set reference input (u_ref)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref&#34;&gt;set_x_ref&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; x_ref)&lt;br&gt;Set reference state (x_ref)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref&#34;&gt;set_y_ref&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; y_ref)&lt;br&gt;Set reference output (y_ref)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc&#34;&gt;set_Kc&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; Kc)&lt;br&gt;Set state controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty&#34;&gt;set_Kc_inty&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; Kc_inty)&lt;br&gt;Set integral controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u&#34;&gt;set_Kc_u&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; Kc_u)&lt;br&gt;Set input controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu&#34;&gt;set_tau_awu&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; tau)&lt;br&gt;Set time constant of anti-integral-windup.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type&#34;&gt;set_control_type&lt;/a&gt;&lt;/strong&gt;(size_t control_type)&lt;br&gt;Sets the control type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb&#34;&gt;set_u_lb&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_lb)&lt;br&gt;sets control lower bound&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub&#34;&gt;set_u_ub&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_ub)&lt;br&gt;Sets control upper bound.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset&#34;&gt;Reset&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;reset system and control variables.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print&#34;&gt;Print&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;prints variables to stdout&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-attributes&#34;&gt;&#xA; Protected Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-&#34;&gt;sys_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;underlying LDS&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-&#34;&gt;u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;control signal&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-&#34;&gt;u_return_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;control signal that is &lt;em&gt;returned&lt;/em&gt; to user&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-&#34;&gt;g_design_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input gain of the system used for controller design&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-&#34;&gt;u_ref_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference input&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-&#34;&gt;u_ref_prev_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference input at previous time step&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-&#34;&gt;x_ref_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference state&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-&#34;&gt;y_ref_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference output&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-&#34;&gt;cx_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-&#34;&gt;Kc_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state controller gain&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-&#34;&gt;Kc_u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input controller gain (optional when control updates \deltaU)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-&#34;&gt;Kc_inty_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;integral controller gain&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-&#34;&gt;du_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-&#34;&gt;dv_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-&#34;&gt;v_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-&#34;&gt;dv_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-&#34;&gt;v_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;Control after g inversion (e.g., control in physical units)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-&#34;&gt;int_e_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;integrated error&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-&#34;&gt;int_e_awu_adjust_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;anti-windup adjustment to intE&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-&#34;&gt;u_sat_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;control signal after saturation (for antiWindup)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;bool&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-&#34;&gt;do_control_prev_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;bool&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-&#34;&gt;do_lock_control_prev_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;bool&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-&#34;&gt;u_saturated_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;whether control signal has reached saturation limits&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-&#34;&gt;u_lb_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;lower bound on control&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-&#34;&gt;u_ub_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;upper bound on control&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-&#34;&gt;tau_awu_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;antiwindup time constant&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-&#34;&gt;k_awu_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-&#34;&gt;t_since_control_onset_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;time since control epoch onset&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-&#34;&gt;control_type_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;controller type&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Controller;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;controller&#34;&gt;&#xA; &lt;strong&gt;Controller&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#controller&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Controller() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;controller-1&#34;&gt;&#xA; &lt;strong&gt;Controller&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#controller-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;inline&lt;/span&gt; Controller(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; sys,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_lb,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_ub,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t control_type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::EM</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/</guid> + <description>&lt;h1 id=&#34;ldsem&#34;&gt;&#xA; lds::EM&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsem&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherited by &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/&#34;&gt;lds::gaussian::FitEM&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/&#34;&gt;lds::poisson::FitEM&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em&#34;&gt;EM&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/&#34;&gt;EM&lt;/a&gt;&lt;a href=&#34;&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em&#34;&gt;EM&lt;/a&gt;&lt;/strong&gt;(size_t n_x, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; dt, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; u_train, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; z_train)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/&#34;&gt;EM&lt;/a&gt;&lt;a href=&#34;&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em&#34;&gt;EM&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; &amp;amp; fit0, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; u_train, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; z_train)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/&#34;&gt;EM&lt;/a&gt;&lt;a href=&#34;&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em&#34;&gt;~EM&lt;/a&gt;&lt;/strong&gt;() =default&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run&#34;&gt;Run&lt;/a&gt;&lt;/strong&gt;(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; tol =1e-2)&lt;br&gt;Runs fitting by Expectation(E)-Maximization(M)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::tuple&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata&#34;&gt;ReturnData&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Returns the input/output data to caller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::vector&amp;lt; Matrix &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x&#34;&gt;x&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets estimated state (over time)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::vector&amp;lt; Matrix &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y&#34;&gt;y&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets estimated output (over time)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t&#34;&gt;sum_E_x_t_x_t&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets state-input covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1&#34;&gt;sum_E_xu_tm1_xu_tm1&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets state-input covariance (t-minus-1)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1&#34;&gt;sum_E_xu_t_xu_tm1&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets single lag state-input covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot&#34;&gt;n_t_tot&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;total number of time samples&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta&#34;&gt;theta&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets parameters updated in M step&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-functions&#34;&gt;&#xA; Protected Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation&#34;&gt;Expectation&lt;/a&gt;&lt;/strong&gt;(bool force_common_initial =false)&lt;br&gt;Expectation step.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization&#34;&gt;Maximization&lt;/a&gt;&lt;/strong&gt;(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)&lt;br&gt;Maximization step.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics&#34;&gt;MaximizeDynamics&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq&#34;&gt;MaximizeQ&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial&#34;&gt;MaximizeInitial&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeoutput&#34;&gt;MaximizeOutput&lt;/a&gt;&lt;/strong&gt;() =0&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizemeasurement&#34;&gt;MaximizeMeasurement&lt;/a&gt;&lt;/strong&gt;() =0&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth&#34;&gt;Smooth&lt;/a&gt;&lt;/strong&gt;(bool force_common_initial)&lt;br&gt;get smoothed estimates&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-recurseke&#34;&gt;RecurseKe&lt;/a&gt;&lt;/strong&gt;(Matrix &amp;amp; Ke, Cube &amp;amp; P_pre, Cube &amp;amp; P_post, size_t t) =0&lt;br&gt;recursively update estimator gain Ke&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset&#34;&gt;Reset&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;reset to initial conditions&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars&#34;&gt;InitVars&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Initializes the variables.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta&#34;&gt;UpdateTheta&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;updates parameter list, theta&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-attributes&#34;&gt;&#xA; Protected Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-&#34;&gt;u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-&#34;&gt;z_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;measurement training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Matrix &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-&#34;&gt;x_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state estimate&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Cube &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-&#34;&gt;P_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state estimate cov&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Cube &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-&#34;&gt;P_t_tm1_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;single-lag state covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Matrix &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-&#34;&gt;y_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;output estimate&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-&#34;&gt;diag_y_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-&#34;&gt;sum_E_x_t_x_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state covariance (current time)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-&#34;&gt;sum_E_xu_tm1_xu_tm1_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state-input covariance (t-minus-1)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-&#34;&gt;sum_E_xu_t_xu_tm1_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;single lag state-input covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-&#34;&gt;fit_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-&#34;&gt;theta_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-&#34;&gt;dt_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;sample period&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-&#34;&gt;n_u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of inputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-&#34;&gt;n_x_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of states&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-&#34;&gt;n_y_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of outputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-&#34;&gt;n_trials_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of input/output data sequences&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; size_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-&#34;&gt;n_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of time steps&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-&#34;&gt;n_t_tot_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;total number of time steps across trials&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; Fit &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;EM;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;em&#34;&gt;&#xA; &lt;strong&gt;EM&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#em&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EM() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;em-1&#34;&gt;&#xA; &lt;strong&gt;EM&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#em-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EM(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_x,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t dt,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; u_train,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; z_train&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::Fit</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/</guid> + <description>LDS Fit Type.</description> + </item> + <item> + <title>lds::gaussian::Controller</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/</guid> + <description>Gaussian-observation Controller Type.</description> + </item> + <item> + <title>lds::gaussian::Fit</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/</guid> + <description>GLDS Fit Type.</description> + </item> + <item> + <title>lds::gaussian::FitEM</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/</guid> + <description>GLDS E-M Fit Type.</description> + </item> + <item> + <title>lds::gaussian::FitSSID</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/</guid> + <description>Subspace Identification (SSID) for GLDS.</description> + </item> + <item> + <title>lds::gaussian::SwitchedController</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/</guid> + <description>Gaussian-observation SwitchedController Type.</description> + </item> + <item> + <title>lds::gaussian::System</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/</guid> + <description>Gaussian LDS Type.</description> + </item> + <item> + <title>lds::poisson::Controller</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/</guid> + <description>PLDS Controller Type.</description> + </item> + <item> + <title>lds::poisson::Fit</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/</guid> + <description>PLDS Fit Type.</description> + </item> + <item> + <title>lds::poisson::FitEM</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/</guid> + <description>PLDS E-M Fit Type.</description> + </item> + <item> + <title>lds::poisson::FitSSID</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/</guid> + <description>Subspace Identification (SSID) for PLDS.</description> + </item> + <item> + <title>lds::poisson::SwitchedController</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/</guid> + <description>Poisson-observation SwitchedController Type.</description> + </item> + <item> + <title>lds::poisson::System</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/</guid> + <description>Poisson System type.</description> + </item> + <item> + <title>lds::SSID</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/</guid> + <description>&lt;h1 id=&#34;ldsssid&#34;&gt;&#xA; lds::SSID&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsssid&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherited by &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/&#34;&gt;lds::gaussian::FitSSID&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/&#34;&gt;lds::poisson::FitSSID&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid&#34;&gt;SSID&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/&#34;&gt;SSID&lt;/a&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid&#34;&gt;SSID&lt;/a&gt;&lt;/strong&gt;(size_t n_x, size_t n_h, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; dt, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; u_train, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; z_train, const Vector &amp;amp; d =Vector(1).fill(-kInf))&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/&#34;&gt;SSID&lt;/a&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::tuple&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt;, Vector &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run&#34;&gt;Run&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt&#34;&gt;SSIDWt&lt;/a&gt; ssid_wt)&lt;br&gt;Runs fitting by subspace identification (&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/&#34;&gt;SSID&lt;/a&gt;)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::tuple&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata&#34;&gt;ReturnData&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Returns the I/O data to caller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-functions&#34;&gt;&#xA; Protected Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd&#34;&gt;CalcD&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; t_silence =0.1, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; thresh_silence =0.001)&lt;br&gt;Using periods of silence in inputs (u), calculates the output \ bias (d)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat&#34;&gt;CreateHankelDataMat&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Creates the block-hankel I/O data matrix.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-decomposedata&#34;&gt;DecomposeData&lt;/a&gt;&lt;/strong&gt;() =0&lt;br&gt;Decompose data to lower-triangular matrix (used in Solve)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd&#34;&gt;CalcSVD&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt&#34;&gt;SSIDWt&lt;/a&gt; wt)&lt;br&gt;performs the singular value decomposition (SVD)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve&#34;&gt;Solve&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; wt_dc)&lt;br&gt;solves for LDS parameters&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs&#34;&gt;RecomputeExtObs&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;recompute extended observability matrix from estimates of A, C&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-attributes&#34;&gt;&#xA; Protected Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-&#34;&gt;u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-&#34;&gt;z_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;measurement training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-&#34;&gt;D_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;block-Hankel I/O data matrix&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-&#34;&gt;fit_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;fit&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-&#34;&gt;g_dc_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;I/O gain @ DC.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-&#34;&gt;dt_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;sample period&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-&#34;&gt;n_u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of inputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-&#34;&gt;n_x_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of states&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-&#34;&gt;n_y_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of outputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-&#34;&gt;n_h_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-&#34;&gt;n_trials_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of input/output data sequences&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; size_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-&#34;&gt;n_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of time steps&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-&#34;&gt;n_t_tot_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;total number of time steps across trials&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-&#34;&gt;L_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;lower triangle decomp of covariance matrix&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-&#34;&gt;s_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;singular values&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-&#34;&gt;ext_obs_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;extended observability matrix&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; Fit &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;SSID;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;ssid&#34;&gt;&#xA; &lt;strong&gt;SSID&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ssid&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SSID() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;ssid-1&#34;&gt;&#xA; &lt;strong&gt;SSID&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ssid-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SSID(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_x,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_h,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t dt,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; u_train,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; z_train,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; Vector &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; d &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Vector(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;).fill(&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;kInf)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::SwitchedController</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/</guid> + <description>SwitchedController Type.</description> + </item> + <item> + <title>lds::System</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/</guid> + <description>Linear Dynamical System Type.</description> + </item> + <item> + <title>lds::UniformMatrixList</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/</guid> + <description>&lt;h1 id=&#34;ldsuniformmatrixlist&#34;&gt;&#xA; lds::UniformMatrixList&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsuniformmatrixlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherits from std::vector&amp;lt; Matrix &amp;gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(const std::vector&amp;lt; Matrix &amp;gt; &amp;amp; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; by copying existing vector of Matrix if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(std::vector&amp;lt; Matrix &amp;gt; &amp;amp;&amp;amp; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; by moving existing vector of Matrix if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(std::initializer_list&amp;lt; Matrix &amp;gt; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; from initializer_list of Matrix if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; (copy).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; (move).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-~uniformmatrixlist&#34;&gt;~UniformMatrixList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Destroys the object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::array&amp;lt; size_t, 2 &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-dim&#34;&gt;dim&lt;/a&gt;&lt;/strong&gt;(size_t n =0) const&lt;br&gt;gets dimensions of uniformly sized matrices&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-size&#34;&gt;size&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;size of container&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-at&#34;&gt;at&lt;/a&gt;&lt;/strong&gt;(size_t n)&lt;br&gt;gets reference to n^th element&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-swap&#34;&gt;Swap&lt;/a&gt;&lt;/strong&gt;(Matrix &amp;amp; that, size_t n)&lt;br&gt;swaps input matrix with n^th matrix of list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp; that)&lt;br&gt;assigns the contents (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&amp;amp; that)&lt;br&gt;assigns the contents (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-append&#34;&gt;append&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; mat)&lt;br&gt;appends a matrix to the list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;MatrixListFreeDim D &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;kMatFreeDimNone&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformMatrixList;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;uniformmatrixlist&#34;&gt;&#xA; &lt;strong&gt;UniformMatrixList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformmatrixlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UniformMatrixList() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;uniformmatrixlist-1&#34;&gt;&#xA; &lt;strong&gt;UniformMatrixList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformmatrixlist-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;explicit&lt;/span&gt; UniformMatrixList(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; Matrix &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; mats,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;array&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; size_t, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; dim &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::UniformSystemList</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/</guid> + <description>&lt;h1 id=&#34;ldsuniformsystemlist&#34;&gt;&#xA; lds::UniformSystemList&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsuniformsystemlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherits from std::vector&amp;lt; System &amp;gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(const std::vector&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;gt; &amp;amp; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; by copying existing vector of &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(std::vector&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; by moving existing vector of &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(std::initializer_list&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;gt; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; from initializer_list of &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; (copy).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; (move).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-~uniformsystemlist&#34;&gt;~UniformSystemList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Destroys the object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::array&amp;lt; size_t, 3 &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-dim&#34;&gt;dim&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets dimensions of the uniformly sized systems&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-size&#34;&gt;size&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;size of container&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-at&#34;&gt;at&lt;/a&gt;&lt;/strong&gt;(size_t n)&lt;br&gt;gets reference to n^th element&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-swap&#34;&gt;Swap&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp; that, size_t n)&lt;br&gt;swaps input system with n^th system of list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp; that)&lt;br&gt;assigns the contents (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;assigns the contents (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformSystemList;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;uniformsystemlist&#34;&gt;&#xA; &lt;strong&gt;UniformSystemList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformsystemlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UniformSystemList() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;uniformsystemlist-1&#34;&gt;&#xA; &lt;strong&gt;UniformSystemList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformsystemlist-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;explicit&lt;/span&gt; UniformSystemList(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; systems,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;array&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; size_t, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; dim &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::UniformVectorList</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/</guid> + <description>&lt;h1 id=&#34;ldsuniformvectorlist&#34;&gt;&#xA; lds::UniformVectorList&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsuniformvectorlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Inherits from std::vector&amp;lt; Vector &amp;gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(const std::vector&amp;lt; Vector &amp;gt; &amp;amp; vecs, size_t dim =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; by copying existing vector of Vector if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(std::vector&amp;lt; Vector &amp;gt; &amp;amp;&amp;amp; vecs, size_t dim =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; by moving existing vector of Vector if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(std::initializer_list&amp;lt; Vector &amp;gt; vecs, size_t dim =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; from initializer_list of Vector if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-~uniformvectorlist&#34;&gt;~UniformVectorList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Destroys the object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-dim&#34;&gt;dim&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets dimensions of the uniformly sized matrices&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-size&#34;&gt;size&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;size of container&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-at&#34;&gt;at&lt;/a&gt;&lt;/strong&gt;(size_t n)&lt;br&gt;gets reference to n^th element&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-swap&#34;&gt;Swap&lt;/a&gt;&lt;/strong&gt;(Vector &amp;amp; that, size_t n)&lt;br&gt;swaps input matrix with n^th vector of list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp; that)&lt;br&gt;assigns the contents (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;assigns the contents (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;uniformvectorlist&#34;&gt;&#xA; &lt;strong&gt;UniformVectorList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformvectorlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UniformVectorList() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;uniformvectorlist-1&#34;&gt;&#xA; &lt;strong&gt;UniformVectorList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformvectorlist-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;explicit&lt;/span&gt; UniformVectorList(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; Vector &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; vecs,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t dim &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + </channel> +</rss> diff --git a/docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html new file mode 100644 index 00000000..3e0d3973 --- /dev/null +++ b/docs/docs/api/examples/eg_glds_ctrl_8cpp-example/index.html @@ -0,0 +1,468 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Example GLDS Control."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="eg_glds_ctrl.cpp"> + <meta property="og:description" content="Example GLDS Control."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>eg_glds_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>eg_glds_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_glds_ctrlcpp">eg_glds_ctrl.cpp</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="eg_glds_ctrlcpp"> + eg_glds_ctrl.cpp + <a class="anchor" href="#eg_glds_ctrlcpp">#</a> +</h1> +<p>Example GLDS Control ```cpp</p> +<p>//===&ndash; eg_glds_ctrl.cpp - Example GLDS Control &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the &ldquo;License&rdquo;); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a> +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an &ldquo;AS IS&rdquo; BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===//</p> +<p>#include <ldsCtrlEst></p> +<p>using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout;</p> +<p>auto main() -&gt; int { +cout &laquo; &quot; ********** Example Gaussian LDS Control ********** \n\n&quot;;</p> +<p>// Make 1st-order SISO system, sampled at 1kHz +data_t dt = 1e-3; +size_t n_u = 1; +size_t n_x = 1; +size_t n_y = 1;</p> +<p>// no time steps for simulation. +auto n_t = static_cast&lt;size_t&gt;(5.0 / dt);</p> +<p>// construct ground truth system to be controlled&hellip; +// initializes to random walk model with top-most n_y state observed +lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);</p> +<p>// Ground-truth parameters for the controlled system +// (stand-in for physical system to be controlled) +Matrix a_true(n_x, n_x, arma::fill::eye); +a_true[0] = exp(-dt / 0.01); +Matrix b_true = Matrix(n_x, n_u).fill(2e-4); +// control signal to model input unit conversion e.g., V -&gt; mW/mm2: +Vector g_true = Vector(n_y).fill(10.0);</p> +<p>// output noise covariance +Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;</p> +<p>size_t which_m = 0; // whether low or high disturbance (0, 1) +data_t m_low = 5 * dt * (1 - a_true[0]); +data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. +data_t m_high = 20 * dt * (1 - a_true[0]); +data_t pr_hi2lo = pr_lo2hi;</p> +<p>// initially let m be low +Vector m0_true = Vector(n_x).fill(m_low);</p> +<p>// Assign params. +controlled_system.set_A(a_true); +controlled_system.set_B(b_true); +controlled_system.set_m(m0_true); +controlled_system.set_g(g_true); +controlled_system.set_R(r_true);</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;controlled_system:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +controlled_system.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// make a controller +lds::gaussian::Controller controller; +{ +// Create <strong>incorrect</strong> model used for control. +// (e.g., imperfect model fitting) +Matrix b_controller = b_true / 2;</p> +<pre><code>// let's assume zero process disturbance initially +// (will be re-estimating) +Vector m_controller = Vector(n_x, arma::fill::zeros); + +// for this demo, just use arbitrary default R +Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; + +lds::gaussian::System controller_system(controlled_system); +controller_system.set_B(b_controller); +controller_system.set_m(m_controller); +controller_system.set_R(r_controller); +controller_system.Reset(); // reset to new m + +// going to adaptively re-estimate the disturbance +controller_system.do_adapt_m = true; + +// set adaptation rate by changing covariance of assumed process noise +// acting on random-walk evolution of m +Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; +controller_system.set_Q_m(q_m); + +// create controller +// lower and upper bounds on control signal (e.g., in Volts) +data_t u_lb = 0.0; // [=] V +data_t u_ub = 5.0; // [=] V +controller = std::move( + lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); +</code></pre> +<p>}</p> +<p>// Control variables: +// if following enabled, adapts set point with re-estimated process +// disturbance n.b., should not need integral action if this is enabled as the +// adaptive estimator minimizes DC error +bool do_adaptive_set_point = false;</p> +<p>// Reference/target output, controller gains +Vector y_ref0 = Vector(n_y).fill(20.0 * dt); +Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error +Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err</p> +<p>// setting initial state to target to avoid error at onset: +Vector x0 = Vector(n_x).fill(y_ref0[0]);</p> +<p>// set up controller type bit mask so controller knows how to proceed +size_t control_type = 0; +if (do_adaptive_set_point) { +// adapt set point with estimated disturbance +control_type = control_type | lds::kControlTypeAdaptM; +} else { +// use integral action to minimize DC error +control_type = control_type | lds::kControlTypeIntY; +}</p> +<p>// set controller type +controller.set_control_type(control_type);</p> +<p>// Let&rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): +Vector g_design = Vector(n_u).fill(9);</p> +<p>// Set params. +// <strong>n.b. using arbitrary defaults for Q, R in this example. Really, these +// should be set by users, as they tune characteristics of Kalman filter. +// Users can also choose not to recursively calculate the estimator gain and +// supply it (setKe) instead of covariances.</strong> +controller.set_y_ref(y_ref0); +controller.set_Kc(k_x); +controller.set_Kc_inty(k_inty); +controller.set_g_design(g_design);</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;control system:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +controller.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// set up variables for simulation +// create Matrix to save outputs in&hellip; +Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];</p> +<p>// Simulated measurements +Matrix z(n_y, n_t, arma::fill::zeros);</p> +<p>// simulated control signal ([=] V) +Matrix u(n_u, n_t, arma::fill::zeros);</p> +<p>// outputs, states and gain/disturbance params +// *_hat indicates online estimates +Matrix y_hat(n_y, n_t, arma::fill::zeros); +Matrix x_hat(n_x, n_t, arma::fill::zeros); +Matrix m_hat(n_x, n_t, arma::fill::zeros);</p> +<p>// *_true indicates ground truth (system being controlled) +Matrix y_true(n_y, n_t, arma::fill::zeros); +Matrix x_true(n_x, n_t, arma::fill::zeros); +Matrix m_true(n_x, n_t, arma::fill::zeros);</p> +<p>// set initial val +y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); +y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();</p> +<p>x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); +x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();</p> +<p>m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); +m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();</p> +<p>cout &laquo; &ldquo;Starting &quot; &laquo; n_t * dt &laquo; &quot; sec simulation &hellip; \n&rdquo;; +auto start = std::chrono::high_resolution_clock::now(); +for (size_t t = 1; t &lt; n_t; t++) { +// simulate a stochastically switched disturbance +Vector chance = arma::randu<Vector>(1); +if (which_m == 0) // low disturbance +{ +if (chance[0] &lt; pr_lo2hi) { // switches low -&gt; high disturbance +m0_true = std::vector&lt;data_t&gt;(n_x, m_high); +which_m = 1; +} +} else { // high disturbance +if (chance[0] &lt; pr_hi2lo) { // swithces high -&gt; low disturbance +m0_true = std::vector&lt;data_t&gt;(n_x, m_low); +which_m = 0; +} +} +controlled_system.set_m(m0_true);</p> +<pre><code>// input +Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); + +// Simulate the true system. +z.col(t) = controlled_system.Simulate(u_tm1); + +// This method uses a steady-state solution to control problem to calculate +// x_ref, u_ref from reference output y_ref. Therefore, it is only +// applicable to regulation problems or cases where reference trajectory +// changes slowly compared to system dynamics. +u.col(t) = controller.ControlOutputReference(z.col(t)); + +// save the signals +y_true.col(t) = controlled_system.y(); +x_true.col(t) = controlled_system.x(); +m_true.col(t) = controlled_system.m(); + +y_hat.col(t) = controller.sys().y(); +x_hat.col(t) = controller.sys().x(); +m_hat.col(t) = controller.sys().m(); +</code></pre> +<p>}</p> +<p>auto finish = std::chrono::high_resolution_clock::now(); +std::chrono::duration&lt;data_t, std::milli&gt; sim_time_ms = finish - start; +cout &laquo; &ldquo;Finished simulation in &quot; &laquo; sim_time_ms.count() &laquo; &quot; ms.\n&rdquo;; +cout &laquo; &ldquo;(app. &quot; &laquo; (sim_time_ms.count() / n_t) * 1e3 &laquo; &quot; us/time-step)\n&rdquo;;</p> +<p>cout &laquo; &ldquo;Saving simulation data to disk.\n&rdquo;;</p> +<p>// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, +// xTrue, mTrue saving with hdf5 via armadillo +arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;</p> +<p>auto dt_vec = Vector(1).fill(dt); +dt_vec.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;dt&rdquo;)); +y_ref.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;y_ref&rdquo;, replace)); +u.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;u&rdquo;, replace)); +z.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;z&rdquo;, replace)); +x_true.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;x_true&rdquo;, replace)); +m_true.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;m_true&rdquo;, replace)); +y_true.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;y_true&rdquo;, replace)); +x_hat.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;x_hat&rdquo;, replace)); +m_hat.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;m_hat&rdquo;, replace)); +y_hat.save(arma::hdf5_name(&ldquo;eg_glds_ctrl.h5&rdquo;, &ldquo;y_hat&rdquo;, replace));</p> +<p>cout &laquo; &ldquo;fin.\n&rdquo;; +return 0; +}</p> +<pre tabindex="0"><code> +_Filename: eg_glds_ctrl.cpp_ + +------------------------------- + +Updated on 31 March 2025 at 16:04:30 EDT +</code></pre></article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_glds_ctrlcpp">eg_glds_ctrl.cpp</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html new file mode 100644 index 00000000..9cf174c8 --- /dev/null +++ b/docs/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/index.html @@ -0,0 +1,471 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u)."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="eg_glds_du_plds_ctrl.cpp"> + <meta property="og:description" content="Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u)."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>eg_glds_du_plds_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>eg_glds_du_plds_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_glds_du_plds_ctrlcpp">eg_glds_du_plds_ctrl.cpp</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="eg_glds_du_plds_ctrlcpp"> + eg_glds_du_plds_ctrl.cpp + <a class="anchor" href="#eg_glds_du_plds_ctrlcpp">#</a> +</h1> +<p>Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp</p> +<p>//===&ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS &mdash;===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the &ldquo;License&rdquo;); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a> +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an &ldquo;AS IS&rdquo; BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===//</p> +<p>#include <ldsCtrlEst></p> +<p>using lds::data_t; +using lds::Matrix; +using lds::Vector; +using std::cout;</p> +<p>auto main() -&gt; int { +cout &laquo; &quot; ********** Example Gaussian LDS du Control of PLDS ********** \n\n&quot;;</p> +<p>// Make 1st-order SISO system, sampled at 1kHz +data_t dt = 1e-3; +size_t n_u = 1; +size_t n_x = 1; +size_t n_y = 1;</p> +<p>// no time steps for simulation. +auto n_t = static_cast&lt;size_t&gt;(5.0 / dt);</p> +<p>// construct ground truth system to be controlled&hellip; +// initializes to random walk model with top-most n_y state observed +lds::poisson::System controlled_system(n_u, n_x, n_y, dt);</p> +<p>// Ground-truth parameters for the controlled system +// (stand-in for physical system to be controlled) +Matrix a_true(n_x, n_x, arma::fill::eye); +a_true[0] = exp(-dt / 0.01); +Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); +// control signal to model input unit conversion e.g., V -&gt; mW/mm2: +Vector g_true = Vector(n_y).fill(10.0);</p> +<p>size_t which_m = 0; // whether low or high disturbance (0, 1) +data_t m_low = log(1 * dt) * (1 - a_true[0]); +data_t pr_lo2hi = +0; // 1e-3; // probability of going from low to high disturb. +data_t m_high = log(20 * dt) * (1 - a_true[0]); +data_t pr_hi2lo = pr_lo2hi;</p> +<p>// initially let m be low +Vector m0_true = Vector(n_x).fill(m_low); +Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);</p> +<p>// Assign params. +controlled_system.set_A(a_true); +controlled_system.set_B(b_true); +controlled_system.set_m(m0_true); +controlled_system.set_g(g_true); +controlled_system.set_x0(x0_true); +controlled_system.Reset();</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;controlled_system:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +controlled_system.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// make a controller +lds::gaussian::Controller controller; +{ +// Create <strong>incorrect</strong> model used for control. +// (e.g., imperfect model fitting) +Matrix b_controller = b_true / 50;</p> +<pre><code>// let's assume zero process disturbance initially +// (will be re-estimating) +Vector m_controller = Vector(n_x, arma::fill::zeros); + +// process noise covariance +Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; + +// output noise covariance +Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; + +lds::gaussian::System controller_system(n_u, n_x, n_y, dt); +controller_system.set_A(a_true); +controller_system.set_B(b_controller); +controller_system.set_g(g_true); +controller_system.set_m(m_controller); +controller_system.set_Q(q_controller); +controller_system.set_R(r_controller); +controller_system.Reset(); // reset to new m + +// going to adaptively re-estimate the disturbance +controller_system.do_adapt_m = true; + +// set adaptation rate by changing covariance of assumed process noise +// acting on random-walk evolution of m +Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; +controller_system.set_Q_m(q_m); + +// create controller +// lower and upper bounds on control signal (e.g., in Volts) +data_t u_lb = 0.0; // [=] V +data_t u_ub = 5.0; // [=] V +controller = std::move( + lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); +</code></pre> +<p>}</p> +<p>// Control variables: +// Reference/target output, controller gains +Vector y_ref0 = Vector(n_y).fill(20.0 * dt);</p> +<p>// to design for this example, augmented state with control and made the input +// du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = +// 1e-2. +Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error +Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err +Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp</p> +<p>// set up controller type bit mask so controller knows how to proceed +size_t control_type = 0; +// use integral action to minimize DC error +control_type = control_type | lds::kControlTypeIntY; +// update change in control (LP filters control) +control_type = control_type | lds::kControlTypeDeltaU;</p> +<p>// set controller type +controller.set_control_type(control_type);</p> +<p>// Let&rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): +Vector g_design = Vector(n_u).fill(10);</p> +<p>// Set params. +// <strong>n.b. using arbitrary defaults for Q, R in this example. Really, these +// should be set by users, as they tune characteristics of Kalman filter. +// Users can also choose not to recursively calculate the estimator gain and +// supply it (setKe) instead of covariances.</strong> +controller.set_y_ref(y_ref0); +controller.set_Kc(k_x); +controller.set_Kc_inty(k_inty); +controller.set_Kc_u(k_u); +controller.set_g_design(g_design);</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;control system:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +controller.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// set up variables for simulation +// create Matrix to save outputs in&hellip; +Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];</p> +<p>// Simulated measurements +Matrix z(n_y, n_t, arma::fill::zeros);</p> +<p>// simulated control signal ([=] V) +Matrix u(n_u, n_t, arma::fill::zeros);</p> +<p>// outputs, states and gain/disturbance params +// *_hat indicates online estimates +Matrix y_hat(n_y, n_t, arma::fill::zeros); +Matrix x_hat(n_x, n_t, arma::fill::zeros); +Matrix m_hat(n_x, n_t, arma::fill::zeros);</p> +<p>// *_true indicates ground truth (system being controlled) +Matrix y_true(n_y, n_t, arma::fill::zeros); +Matrix x_true(n_x, n_t, arma::fill::zeros); +Matrix m_true(n_x, n_t, arma::fill::zeros);</p> +<p>// get initial val +y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); +y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();</p> +<p>x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); +x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();</p> +<p>m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); +m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();</p> +<p>cout &laquo; &ldquo;Starting &quot; &laquo; n_t * dt &laquo; &quot; sec simulation &hellip; \n&rdquo;; +auto start = std::chrono::high_resolution_clock::now(); +for (size_t t = 1; t &lt; n_t; t++) { +// simulate a stochastically switched disturbance +Vector chance = arma::randu<Vector>(1); +if (which_m == 0) // low disturbance +{ +if (chance[0] &lt; pr_lo2hi) { // switches low -&gt; high disturbance +m0_true = std::vector&lt;data_t&gt;(n_x, m_high); +which_m = 1; +} +} else { // high disturbance +if (chance[0] &lt; pr_hi2lo) { // swithces high -&gt; low disturbance +m0_true = std::vector&lt;data_t&gt;(n_x, m_low); +which_m = 0; +} +} +controlled_system.set_m(m0_true);</p> +<pre><code>// input +Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); + +// Simulate the true system. +z.col(t) = controlled_system.Simulate(u_tm1); + +// This method uses a steady-state solution to control problem to calculate +// x_ref, u_ref from reference output y_ref. Therefore, it is only +// applicable to regulation problems or cases where reference trajectory +// changes slowly compared to system dynamics. +u.col(t) = controller.ControlOutputReference(z.col(t)); + +// save the signals +y_true.col(t) = controlled_system.y(); +x_true.col(t) = controlled_system.x(); +m_true.col(t) = controlled_system.m(); + +y_hat.col(t) = controller.sys().y(); +x_hat.col(t) = controller.sys().x(); +m_hat.col(t) = controller.sys().m(); +</code></pre> +<p>}</p> +<p>auto finish = std::chrono::high_resolution_clock::now(); +std::chrono::duration&lt;data_t, std::milli&gt; sim_time_ms = finish - start; +cout &laquo; &ldquo;Finished simulation in &quot; &laquo; sim_time_ms.count() &laquo; &quot; ms.\n&rdquo;; +cout &laquo; &ldquo;(app. &quot; &laquo; (sim_time_ms.count() / n_t) * 1e3 &laquo; &quot; us/time-step)\n&rdquo;;</p> +<p>cout &laquo; &ldquo;Saving simulation data to disk.\n&rdquo;;</p> +<p>// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, +// xTrue, mTrue saving with hdf5 via armadillo +arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;</p> +<p>auto dt_vec = Vector(1).fill(dt); +dt_vec.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;dt&rdquo;)); +y_ref.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;y_ref&rdquo;, replace)); +u.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;u&rdquo;, replace)); +z.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;z&rdquo;, replace)); +x_true.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;x_true&rdquo;, replace)); +m_true.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;m_true&rdquo;, replace)); +y_true.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;y_true&rdquo;, replace)); +x_hat.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;x_hat&rdquo;, replace)); +m_hat.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;m_hat&rdquo;, replace)); +y_hat.save(arma::hdf5_name(&ldquo;eg_glds_du_plds_ctrl.h5&rdquo;, &ldquo;y_hat&rdquo;, replace));</p> +<p>cout &laquo; &ldquo;fin.\n&rdquo;; +return 0; +}</p> +<pre tabindex="0"><code> +_Filename: eg_glds_du_plds_ctrl.cpp_ + +------------------------------- + +Updated on 31 March 2025 at 16:04:30 EDT +</code></pre></article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_glds_du_plds_ctrlcpp">eg_glds_du_plds_ctrl.cpp</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html new file mode 100644 index 00000000..e831a253 --- /dev/null +++ b/docs/docs/api/examples/eg_plds_ctrl_8cpp-example/index.html @@ -0,0 +1,444 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Example PLDS Control."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="eg_plds_ctrl.cpp"> + <meta property="og:description" content="Example PLDS Control."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>eg_plds_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>eg_plds_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_plds_ctrlcpp">eg_plds_ctrl.cpp</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="eg_plds_ctrlcpp"> + eg_plds_ctrl.cpp + <a class="anchor" href="#eg_plds_ctrlcpp">#</a> +</h1> +<p>Example PLDS Control ```cpp</p> +<p>//===&ndash; eg_plds_ctrl.cpp - Example PLDS Control &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the &ldquo;License&rdquo;); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a> +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an &ldquo;AS IS&rdquo; BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===//</p> +<p>#include <ldsCtrlEst></p> +<p>using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout;</p> +<p>auto main() -&gt; int { +cout &laquo; &quot; ********** Example Poisson LDS Control ********** \n\n&quot;;</p> +<p>// Make SISO system sampled at 1kHz +data_t dt = 1e-3; +size_t n_u = 1; +size_t n_x = 1; +size_t n_y = 1;</p> +<p>// no time steps for simulation. +auto n_t = static_cast&lt;size_t&gt;(10.0 / dt);</p> +<p>// Control variables: _reference/target output, controller gains +// n.b., Can either use Vector (arma::Col) or std::vector +Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; +Matrix k_x = +Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error +Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + +10; // gains on integrated output err</p> +<p>// Set control type bit mask, so controller knows what to do +size_t control_type = lds::kControlTypeIntY; // integral action</p> +<p>// Ground-truth parameters for the controlled system +// (stand-in for physical system to be controlled) +Matrix a_true(n_x, n_x, arma::fill::eye); +a_true[0] = 0.986; +Matrix b_true(n_x, n_u, arma::fill::zeros); +b_true[0] = 0.054; +Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);</p> +<p>size_t which_m = 0; +data_t m_low = log(1 * dt) * (1 - a_true[0]); +data_t pr_lo2hi = 1e-3; +data_t m_high = log(20 * dt) * (1 - a_true[0]); +data_t pr_hi2lo = pr_lo2hi;</p> +<p>Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; +// construct ground truth system to be controlled&hellip; +lds::poisson::System controlled_system(n_u, n_x, n_y, dt);</p> +<p>// Assign params. +controlled_system.set_A(a_true); +controlled_system.set_B(b_true); +controlled_system.set_m(m0_true); +controlled_system.set_x0(x0_true); +// reset to initial conditions +controlled_system.Reset();</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;controlled_system:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +controlled_system.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// Create the controller +lds::poisson::Controller controller; +{ +// Create model used for control. +lds::poisson::System controller_system(controlled_system);</p> +<pre><code>// for this example, assume model correct, except disturbance +Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; +Vector x0_controller = arma::log(y_ref0); +controller_system.set_m(m0_controller); +controller_system.set_x0(x0_controller); +controller_system.Reset(); //reset to new init condition + +// adaptively re-estimate process disturbance (m) +controller_system.do_adapt_m = true; + +// set adaptation rate by changing covariance of assumed process noise +// acting on random-walk evolution of m +Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; +controller_system.set_Q_m(q_m); + +data_t u_lb = 0.0; +data_t u_ub = 5.0; +controller = std::move( + lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); +</code></pre> +<p>} +// set controller type +controller.set_control_type(control_type);</p> +<p>// set controller gains +controller.set_Kc(k_x); +controller.set_Kc_inty(k_inty);</p> +<p>// to protect against integral windup when output is consistently above +// target: +data_t tau_awu(0.1); +controller.set_tau_awu(tau_awu);</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;controller:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +controller.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// create Matrix to save outputs in&hellip; +Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); +y_ref.each_col() += y_ref0;</p> +<p>// Simulated measurements +Matrix z(n_y, n_t, arma::fill::zeros);</p> +<p>// simulated control signal ([=] V) +Matrix u(n_u, n_t, arma::fill::zeros);</p> +<p>// outputs, states and gain/disturbance params +// *_hat indicates online estimates +Matrix y_hat(n_y, n_t, arma::fill::zeros); +Matrix x_hat(n_x, n_t, arma::fill::zeros); +Matrix m_hat(n_y, n_t, arma::fill::zeros);</p> +<p>// *_true indicates ground truth (system being controlled) +Matrix y_true(n_y, n_t, arma::fill::zeros); +Matrix x_true(n_x, n_t, arma::fill::zeros); +Matrix m_true(n_y, n_t, arma::fill::zeros);</p> +<p>// set initial val +y_hat.col(0) = controller.sys().y(); +y_true.col(0) = controlled_system.y();</p> +<p>x_hat.col(0) = controller.sys().x(); +x_true.col(0) = controlled_system.x();</p> +<p>m_hat.col(0) = controller.sys().m(); +m_true.col(0) = controlled_system.m();</p> +<p>cout &laquo; &ldquo;Starting &quot; &laquo; n_t * dt &laquo; &quot; sec simulation &hellip; \n&rdquo;; +auto start = std::chrono::high_resolution_clock::now(); +for (size_t t = 1; t &lt; n_t; t++) { +// simulate a stochastically switched disturbance +Vector chance = arma::randu<Vector>(1); +if (which_m == 0) // low disturbance +{ +if (chance[0] &lt; pr_lo2hi) { // switches low -&gt; high disturbance +m0_true = std::vector&lt;data_t&gt;(n_x, m_high); +which_m = 1; +} +} else { // high disturbance +if (chance[0] &lt; pr_hi2lo) { // swithces high -&gt; low disturbance +m0_true = std::vector&lt;data_t&gt;(n_x, m_low); +which_m = 0; +} +} +controlled_system.set_m(m0_true);</p> +<pre><code>// e.g., use sinusoidal reference +data_t f = 0.5; // freq [=] Hz +Vector t_vec = Vector(n_y, arma::fill::ones) * t; +y_ref.col(t) += + y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); + +// Simulate the true system. +z.col(t)=controlled_system.Simulate(u.col(t-1)); + +// This method uses a steady-state solution to control problem to calculate +// x_ref, u_ref from reference output y_ref. Notably, it does this in the +// log-linear space (i.e., log(y)). +// +// Therefore, it is only applicable to regulation problems or cases where +// reference trajectory changes slowly compared to system dynamics. +controller.set_y_ref(y_ref.col(t)); +u.col(t)=controller.ControlOutputReference(z.col(t)); + +y_true.col(t) = controlled_system.y(); +x_true.col(t) = controlled_system.x(); +m_true.col(t) = controlled_system.m(); + +y_hat.col(t) = controller.sys().y(); +x_hat.col(t) = controller.sys().x(); +m_hat.col(t) = controller.sys().m(); +</code></pre> +<p>}</p> +<p>auto finish = std::chrono::high_resolution_clock::now(); +std::chrono::duration&lt;data_t, std::milli&gt; sim_time_ms = finish - start; +cout &laquo; &ldquo;Finished simulation in &quot; &laquo; sim_time_ms.count() &laquo; &quot; ms.\n&rdquo;; +cout &laquo; &ldquo;(app. &quot; &laquo; (sim_time_ms.count() / n_t) * 1e3 &laquo; &quot; us/time-step)\n&rdquo;;</p> +<p>// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, +// x_true, m_true saving with hdf5 via armadillo +arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;</p> +<p>auto dt_vec = Vector(1).fill(dt); +dt_vec.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;dt&rdquo;)); +y_ref.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;y_ref&rdquo;, replace)); +u.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;u&rdquo;, replace)); +z.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;z&rdquo;, replace)); +x_true.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;x_true&rdquo;, replace)); +m_true.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;m_true&rdquo;, replace)); +y_true.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;y_true&rdquo;, replace)); +x_hat.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;x_hat&rdquo;, replace)); +m_hat.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;m_hat&rdquo;, replace)); +y_hat.save(arma::hdf5_name(&ldquo;eg_plds_ctrl.h5&rdquo;, &ldquo;y_hat&rdquo;, replace));</p> +<p>return 0; +}</p> +<pre tabindex="0"><code> +_Filename: eg_plds_ctrl.cpp_ + +------------------------------- + +Updated on 31 March 2025 at 16:04:30 EDT +</code></pre></article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_plds_ctrlcpp">eg_plds_ctrl.cpp</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/examples/eg_plds_est_8cpp-example/index.html b/docs/docs/api/examples/eg_plds_est_8cpp-example/index.html new file mode 100644 index 00000000..6f8670f8 --- /dev/null +++ b/docs/docs/api/examples/eg_plds_est_8cpp-example/index.html @@ -0,0 +1,393 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Example PLDS Estimation."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="eg_plds_est.cpp"> + <meta property="og:description" content="Example PLDS Estimation."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>eg_plds_est.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>eg_plds_est.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_plds_estcpp">eg_plds_est.cpp</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="eg_plds_estcpp"> + eg_plds_est.cpp + <a class="anchor" href="#eg_plds_estcpp">#</a> +</h1> +<p>Example PLDS Estimation ```cpp</p> +<p>//===&ndash; eg_plds_est.cpp - Example PLDS Estimation &mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the &ldquo;License&rdquo;); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a> +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an &ldquo;AS IS&rdquo; BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===//</p> +<p>#include <ldsCtrlEst></p> +<p>using lds::Matrix; +using lds::Vector; +using lds::data_t; +using std::cout;</p> +<p>// for generating random input +Matrix random_walk(size_t n_t, const Matrix&amp; Q, const Vector&amp; x0);</p> +<p>int main() { +cout &laquo; &quot; ********** Example Poisson LDS Estimation ********** \n\n&quot;;</p> +<p>// Make SISO system sampled at 1kHz +data_t dt = 1e-3; +size_t n_u = 1; // no. inputs +size_t n_x = 1; // no. states +size_t n_y = 1; // no. outputs +auto n_t = static_cast&lt;size_t&gt;(30 / dt); // no time steps for simulation.</p> +<p>// construct ground truth system&hellip; +lds::poisson::System system_true(n_u, n_x, n_y, dt);</p> +<p>// Model parameters +Matrix a_true(n_x, n_x, arma::fill::eye); +a_true[0] = exp(-dt / 0.075); +Matrix b_true = Matrix(n_x, n_u).fill(1e-2); +Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance +Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - +a_true); // initial state</p> +<p>// Assign params. +system_true.set_A(a_true); +system_true.set_B(b_true); +system_true.set_x0(x0_true); +system_true.set_m(m0_true); +system_true.Reset();</p> +<p>// Construct system for estimation +// e.g., will create a model with incorrect disturbance +lds::poisson::System system_estimator(n_u, n_x, n_y, dt);</p> +<p>// Can copy parameters from another system object +system_estimator = system_true;</p> +<p>// wrong disturbance +Vector m0_est = m0_true * 2; +system_estimator.set_m(m0_est);</p> +<p>// set new initial conditions +Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - +a_true); // initial state +system_estimator.set_x0(x0_est); +system_estimator.Reset(); // reset to initial condition.</p> +<p>// turn on adaptive disturbance estimation +system_estimator.do_adapt_m = true;</p> +<p>// set adaptation rate by changing covariance of assumed process noise acting +// on random-walk evolution of m +Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; +system_estimator.set_Q_m(q_m);</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;estimator:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +system_estimator.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// Set up simulation : +// Simulated measurements +Matrix z(n_y, n_t, arma::fill::zeros);</p> +<p>// Stimulus (generate random stimulus) +Matrix q_u = +Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk +Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));</p> +<p>// create matrix to save outputs in&hellip; +Matrix y_hat(n_y, n_t, arma::fill::zeros); +Matrix y_true(n_y, n_t, arma::fill::zeros);</p> +<p>// states and disturbance params +Matrix x_hat(n_x, n_t, arma::fill::zeros); +Matrix m_hat(n_x, n_t, arma::fill::zeros);</p> +<p>Matrix x_true(n_x, n_t, arma::fill::zeros); +Matrix m_true(n_x, n_t, arma::fill::zeros);</p> +<p>// initial conditions +y_hat.col(0) = system_estimator.y(); +y_true.col(0) = system_true.y(); +x_hat.col(0) = system_estimator.x(); +x_true.col(0) = system_true.x(); +m_hat.col(0) = system_estimator.m(); +m_true.col(0) = system_true.m();</p> +<p>cout &laquo; &ldquo;Starting &quot; &laquo; n_t * dt &laquo; &quot; sec simlation &hellip; \n&rdquo;; +auto start = std::chrono::high_resolution_clock::now(); +for (size_t t = 1; t &lt; n_t; t++) { +// Simlate the true system. +z.col(t) = system_true.Simulate(u.col(t - 1));</p> +<pre><code>// Filter (predict -&gt; update) +system_estimator.Filter(u.col(t - 1), z.col(t)); + +// save signals +y_hat.col(t) = system_estimator.y(); +y_true.col(t) = system_true.y(); + +x_true.col(t) = system_true.x(); +m_true.col(t) = system_true.m(); + +x_hat.col(t) = system_estimator.x(); +m_hat.col(t) = system_estimator.m(); +</code></pre> +<p>}</p> +<p>auto finish = std::chrono::high_resolution_clock::now(); +std::chrono::duration&lt;data_t, std::milli&gt; sim_time_ms = finish - start; +cout &laquo; &ldquo;Finished simlation in &quot; &laquo; sim_time_ms.count() &laquo; &quot; ms.\n&rdquo;; +cout &laquo; &ldquo;(app. &quot; &laquo; (sim_time_ms.count() / n_t) * 1e3 &laquo; &quot; us/time-step)\n&rdquo;;</p> +<p>// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, +// x_true, m_true saving with hdf5 via armadillo +arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;</p> +<p>auto dt_vec = Vector(1).fill(dt); +dt_vec.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;dt&rdquo;)); +u.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;u&rdquo;, replace)); +z.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;z&rdquo;, replace)); +x_true.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;x_true&rdquo;, replace)); +m_true.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;m_true&rdquo;, replace)); +y_true.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;y_true&rdquo;, replace)); +x_hat.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;x_hat&rdquo;, replace)); +m_hat.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;m_hat&rdquo;, replace)); +y_hat.save(arma::hdf5_name(&ldquo;eg_plds_est.h5&rdquo;, &ldquo;y_hat&rdquo;, replace));</p> +<p>return 0; +}</p> +<p>// for generating random input +Matrix random_walk(size_t n_t, const Matrix&amp; Q, const Vector&amp; x0) { +size_t n = Q.n_rows;</p> +<p>if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { +throw std::logic_error(&ldquo;Q must be <code>n</code> x <code>n</code>.&rdquo;); +}</p> +<p>Matrix x(n, n_t, arma::fill::zeros); +x.col(0) = x0; +for (size_t t = 1; t &lt; n_t; t++) { +x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); +}</p> +<p>return x; +}</p> +<pre tabindex="0"><code> +_Filename: eg_plds_est.cpp_ + +------------------------------- + +Updated on 31 March 2025 at 16:04:30 EDT +</code></pre></article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_plds_estcpp">eg_plds_est.cpp</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html b/docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html new file mode 100644 index 00000000..a88c1638 --- /dev/null +++ b/docs/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/index.html @@ -0,0 +1,441 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Example Switched PLDS Control."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="eg_plds_switched_ctrl.cpp"> + <meta property="og:description" content="Example Switched PLDS Control."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>eg_plds_switched_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>eg_plds_switched_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_plds_switched_ctrlcpp">eg_plds_switched_ctrl.cpp</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="eg_plds_switched_ctrlcpp"> + eg_plds_switched_ctrl.cpp + <a class="anchor" href="#eg_plds_switched_ctrlcpp">#</a> +</h1> +<p>Example Switched PLDS Control ```cpp</p> +<p>//===&ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control &mdash;===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the &ldquo;License&rdquo;); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a> +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an &ldquo;AS IS&rdquo; BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===// +//===&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;-===//</p> +<p>#include <ldsCtrlEst></p> +<p>using lds::data_t; +using lds::Matrix; +using lds::Vector; +using std::cout;</p> +<p>auto main() -&gt; int { +cout &laquo; &quot; ********** Example Switched Poisson LDS Control ********** \n\n&quot;;</p> +<p>// whether to do switched control +bool do_switch_ctrl = true;</p> +<p>// Make SISO system sampled at 1kHz +data_t dt = 1e-3; +size_t n_u = 1; +size_t n_x = 1; +size_t n_y = 1;</p> +<p>// no time steps for simulation. +auto n_t = static_cast&lt;size_t&gt;(30.0 / dt);</p> +<p>// for simulating switching +size_t which_mode = 1; +data_t pr_21 = 1e-3; // prob mode 1 -&gt; 2 +data_t pr_12 = pr_21; // prob mode 2 -&gt; 1</p> +<p>// simulated system being controlled +lds::poisson::System controlled_system(n_u, n_x, n_y, dt);</p> +<p>// <strong>Assume the system is not well characterized by one LDS, but is well +// characterized by two LDS models with different input matrices.</strong> +data_t scale_sys_b = 2;</p> +<p>Matrix a(n_x, n_x, arma::fill::eye); +a[0] = 0.985; +Matrix b1 = Matrix(n_x, n_u).fill(0.05); +Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));</p> +<p>controlled_system.set_A(a); +controlled_system.set_B(b1); +controlled_system.set_d(d); +controlled_system.Reset(); // reset to initial conditions</p> +<p>// reference +Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);</p> +<p>// Let underlying system 1 be more sensitive than system 2 +Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);</p> +<p>// create switched controller +lds::poisson::SwitchedController switched_controller; +lds::UniformMatrixList&lt;&gt; k_x; // feedback controller gains +{ +// create switched controller sub-systems +// system 1 +lds::poisson::System sys1(controlled_system);</p> +<pre><code>// set process noise covariance +Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; +sys1.set_Q(q_controller); + +// adaptively estimate process disturbance (m) +// n.b. using arbitrary default value for process noise if enabled. +sys1.do_adapt_m = true; + +// setting initial mode to target to avoid large error at onset: +Vector x0_controller = arma::log(y_ref0) - d; +sys1.set_x0(x0_controller); +sys1.Reset(); // reset to initial conditions + +cout &lt;&lt; &quot;.....................................\n&quot;; +cout &lt;&lt; &quot;sys1:\n&quot;; +cout &lt;&lt; &quot;.....................................\n&quot;; +sys1.Print(); +cout &lt;&lt; &quot;.....................................\n&quot;; + +// system 2 +lds::poisson::System sys2 = sys1; + +// set parameters +sys2.set_B(b2); + +cout &lt;&lt; &quot;.....................................\n&quot;; +cout &lt;&lt; &quot;sys2:\n&quot;; +cout &lt;&lt; &quot;.....................................\n&quot;; +sys2.Print(); +cout &lt;&lt; &quot;.....................................\n&quot;; + +lds::UniformSystemList&lt;lds::poisson::System&gt; systems({sys1, sys2}); + +// controller gains for underlying system s: +Matrix k_x1(n_u, n_x, arma::fill::ones); +Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. +k_x = lds::UniformMatrixList&lt;&gt;({k_x1, k_x2}); + +data_t u_lb = 0.0; +data_t u_ub = 5.0; +switched_controller = std::move( + lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); +</code></pre> +<p>} +// Control variables +size_t control_type = 0; // no integral action, etc +switched_controller.set_control_type(control_type); +switched_controller.set_Kc(std::move(k_x));</p> +<p>switched_controller.set_y_ref(y_ref0);</p> +<p>std::vector<a href="lds::poisson::System">lds::poisson::System</a> systems_vec(3, lds::poisson::System()); +lds::UniformSystemList<a href="lds::poisson::System">lds::poisson::System</a> systems(std::move(systems_vec));</p> +<p>cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +cout &laquo; &ldquo;switched_controller:\n&rdquo;; +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;; +switched_controller.Print(); +cout &laquo; &ldquo;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;&hellip;.\n&rdquo;;</p> +<p>// Fake measurements +Matrix z(n_y, n_t, arma::fill::zeros);</p> +<p>// Will later contain control. +Matrix u(n_u, n_t, arma::fill::zeros);</p> +<p>// create Matrix to save outputs in&hellip; +Matrix y_hat(n_y, n_t, arma::fill::zeros); +Matrix y_true(n_y, n_t, arma::fill::zeros); +Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);</p> +<p>// modes and gain/disturbance params +Matrix x_hat(n_x, n_t, arma::fill::zeros); +Matrix x_true(n_x, n_t, arma::fill::zeros); +Matrix mode(1, n_t, arma::fill::ones);</p> +<p>// set initial val +y_hat.col(0) = switched_controller.sys().y(); +y_true.col(0) = controlled_system.y(); +x_hat.col(0) = switched_controller.sys().x(); +x_true.col(0) = controlled_system.x();</p> +<p>cout &laquo; &ldquo;Starting &quot; &laquo; n_t * dt &laquo; &quot; sec simulation &hellip; \n&rdquo;; +auto start = std::chrono::high_resolution_clock::now(); +for (size_t t = 1; t &lt; n_t; t++) { +// Let the controlled system stochastically change gain +// Assume another algorithm decodes this mode change and signals the +// switched_controller +Vector chance(1, arma::fill::randu); +if (which_mode == 1) // mode1 +{ +if (chance[0] &lt; pr_21) { +which_mode = 2; +controlled_system.set_B(b2); +if (do_switch_ctrl) { +switched_controller.Switch(1); +} +} +} else { // mode2 +if (chance[0] &lt; pr_12) { +which_mode = 1; +controlled_system.set_B(b1); +if (do_switch_ctrl) { +switched_controller.Switch(0); +} +} +}</p> +<pre><code>// Simulate the true system. +z.col(t) = controlled_system.Simulate(u.col(t - 1)); + +// perform control +u.col(t) = switched_controller.ControlOutputReference(z.col(t)); + +mode.col(t) = which_mode; +y_ref.col(t) = y_ref0; +y_true.col(t) = controlled_system.y(); +x_true.col(t) = controlled_system.x(); +y_hat.col(t) = switched_controller.sys().y(); +x_hat.col(t) = switched_controller.sys().x(); +</code></pre> +<p>}</p> +<p>auto finish = std::chrono::high_resolution_clock::now(); +std::chrono::duration&lt;data_t, std::milli&gt; sim_time_ms = finish - start; +cout &laquo; &ldquo;Finished simulation in &quot; &laquo; sim_time_ms.count() &laquo; &quot; ms.\n&rdquo;; +cout &laquo; &ldquo;(app. &quot; &laquo; (sim_time_ms.count() / n_t) * 1e3 &laquo; &quot; us/time-step)\n&rdquo;;</p> +<p>// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, +// x_true, m_true saving with hdf5 via armadillo +arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;</p> +<p>auto dt_vec = Vector(1).fill(dt); +dt_vec.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;dt&rdquo;)); +y_ref.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;y_ref&rdquo;, replace)); +u.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;u&rdquo;, replace)); +z.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;z&rdquo;, replace)); +x_true.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;x_true&rdquo;, replace)); +y_true.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;y_true&rdquo;, replace)); +x_hat.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;x_hat&rdquo;, replace)); +y_hat.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;y_hat&rdquo;, replace)); +mode.save(arma::hdf5_name(&ldquo;eg_plds_switched_ctrl.h5&rdquo;, &ldquo;mode&rdquo;, replace));</p> +<p>return 0; +}</p> +<pre tabindex="0"><code> +_Filename: eg_plds_switched_ctrl.cpp_ + +------------------------------- + +Updated on 31 March 2025 at 16:04:30 EDT +</code></pre></article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#eg_plds_switched_ctrlcpp">eg_plds_switched_ctrl.cpp</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/examples/index.html b/docs/docs/api/examples/index.html new file mode 100644 index 00000000..a07ea90a --- /dev/null +++ b/docs/docs/api/examples/index.html @@ -0,0 +1,286 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Examples + # + + + +eg_glds_ctrl.cpp Example GLDS Control. + + +eg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). + + +eg_plds_ctrl.cpp Example PLDS Control. + + +eg_plds_est.cpp Example PLDS Estimation. + + +eg_plds_switched_ctrl.cpp Example Switched PLDS Control. + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Examples"> + <meta property="og:description" content="Examples # eg_glds_ctrl.cpp Example GLDS Control. +eg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). +eg_plds_ctrl.cpp Example PLDS Control. +eg_plds_est.cpp Example PLDS Estimation. +eg_plds_switched_ctrl.cpp Example Switched PLDS Control. +Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Examples | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/"class=active>Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Examples</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#examples">Examples</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="examples"> + Examples + <a class="anchor" href="#examples">#</a> +</h1> +<ul> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/#example-eg-glds-ctrl.cpp">eg_glds_ctrl.cpp</a></strong> <br>Example GLDS Control.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/#example-eg-glds-du-plds-ctrl.cpp">eg_glds_du_plds_ctrl.cpp</a></strong> <br>Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/#example-eg-plds-ctrl.cpp">eg_plds_ctrl.cpp</a></strong> <br>Example PLDS Control.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/#example-eg-plds-est.cpp">eg_plds_est.cpp</a></strong> <br>Example PLDS Estimation.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/#example-eg-plds-switched-ctrl.cpp">eg_plds_switched_ctrl.cpp</a></strong> <br>Example Switched PLDS Control.</p> +</li> +</ul> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#examples">Examples</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/examples/index.xml b/docs/docs/api/examples/index.xml new file mode 100644 index 00000000..c128721d --- /dev/null +++ b/docs/docs/api/examples/index.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Examples on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/</link> + <description>Recent content in Examples on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>eg_glds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</guid> + <description>Example GLDS Control.</description> + </item> + <item> + <title>eg_glds_du_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</guid> + <description>Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).</description> + </item> + <item> + <title>eg_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</guid> + <description>Example PLDS Control.</description> + </item> + <item> + <title>eg_plds_est.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</guid> + <description>Example PLDS Estimation.</description> + </item> + <item> + <title>eg_plds_switched_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</guid> + <description>Example Switched PLDS Control.</description> + </item> + </channel> +</rss> diff --git a/docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html b/docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html new file mode 100644 index 00000000..72d0728d --- /dev/null +++ b/docs/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/index.html @@ -0,0 +1,437 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + ldsCtrlEst_h + # + + + Files + # + + + + + Name + + + + + ldsCtrlEst_h/lds.h lds namespace + + + ldsCtrlEst_h/lds_ctrl.h Controller. + + + ldsCtrlEst_h/lds_fit.h LDS base fit type. + + + ldsCtrlEst_h/lds_fit_em.h subspace identification + + + ldsCtrlEst_h/lds_fit_ssid.h subspace identification + + + ldsCtrlEst_h/lds_gaussian.h glds namespace + + + ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. + + + ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. + + + ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. + + + ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. + + + ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. + + + ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. + + + ldsCtrlEst_h/lds_poisson.h plds namespace + + + ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. + + + ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. + + + ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. + + + ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. + + + ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. + + + ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. + + + ldsCtrlEst_h/lds_sctrl.h SwitchedController type. + + + ldsCtrlEst_h/lds_sys.h LDS base type. + + + ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. + + + ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. + + + ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. + + + ldsCtrlEst_h/mex_c_util.h arma &lt;-&gt; mex interoperability utilities (Matlab C API) + + + ldsCtrlEst_h/mex_cpp_util.h arma &lt;-&gt; mex interoperability utilities (Matlab C&#43;&#43; API) + + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h"> + <meta property="og:description" content="ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma &lt;-&gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma &lt;-&gt; mex interoperability utilities (Matlab C&#43;&#43; API) Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_h">ldsCtrlEst_h</a> + <ul> + <li><a href="#files">Files</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_h"> + ldsCtrlEst_h + <a class="anchor" href="#ldsctrlest_h">#</a> +</h1> +<h2 id="files"> + Files + <a class="anchor" href="#files">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h">ldsCtrlEst_h/lds.h</a></strong> <br><code>lds</code> namespace</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h">ldsCtrlEst_h/lds_ctrl.h</a></strong> <br>Controller.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h">ldsCtrlEst_h/lds_fit.h</a></strong> <br>LDS base fit type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h">ldsCtrlEst_h/lds_fit_em.h</a></strong> <br>subspace identification</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h">ldsCtrlEst_h/lds_fit_ssid.h</a></strong> <br>subspace identification</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h">ldsCtrlEst_h/lds_gaussian.h</a></strong> <br><code>glds</code> namespace</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h">ldsCtrlEst_h/lds_gaussian_ctrl.h</a></strong> <br>GLDS Controller.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h">ldsCtrlEst_h/lds_gaussian_fit.h</a></strong> <br>GLDS fit type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h">ldsCtrlEst_h/lds_gaussian_fit_em.h</a></strong> <br>GLDS E-M fit type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h">ldsCtrlEst_h/lds_gaussian_fit_ssid.h</a></strong> <br>GLDS SSID fit type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h">ldsCtrlEst_h/lds_gaussian_sctrl.h</a></strong> <br>GLDS switched controller type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h">ldsCtrlEst_h/lds_gaussian_sys.h</a></strong> <br>GLDS base type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h">ldsCtrlEst_h/lds_poisson.h</a></strong> <br><code>plds</code> namespace</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h">ldsCtrlEst_h/lds_poisson_ctrl.h</a></strong> <br>PLDS controller type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h">ldsCtrlEst_h/lds_poisson_fit.h</a></strong> <br>PLDS base fit type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h">ldsCtrlEst_h/lds_poisson_fit_em.h</a></strong> <br>PLDS E-M fit type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h">ldsCtrlEst_h/lds_poisson_fit_ssid.h</a></strong> <br>PLDS SSID fit type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h">ldsCtrlEst_h/lds_poisson_sctrl.h</a></strong> <br>PLDS switched controller type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h">ldsCtrlEst_h/lds_poisson_sys.h</a></strong> <br>PLDS base type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h">ldsCtrlEst_h/lds_sctrl.h</a></strong> <br>SwitchedController type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h">ldsCtrlEst_h/lds_sys.h</a></strong> <br>LDS base type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h">ldsCtrlEst_h/lds_uniform_mats.h</a></strong> <br>List of uniformly sized matrices.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h">ldsCtrlEst_h/lds_uniform_systems.h</a></strong> <br>List of uniformly sized Systems.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h">ldsCtrlEst_h/lds_uniform_vecs.h</a></strong> <br>List of uniformly sized vectors.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h">ldsCtrlEst_h/mex_c_util.h</a></strong> <br>arma &lt;-&gt; mex interoperability utilities (Matlab C API)</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h">ldsCtrlEst_h/mex_cpp_util.h</a></strong> <br>arma &lt;-&gt; mex interoperability utilities (Matlab C++ API)</td> + </tr> + </tbody> +</table> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_h">ldsCtrlEst_h</a> + <ul> + <li><a href="#files">Files</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html b/docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html new file mode 100644 index 00000000..4e5f4394 --- /dev/null +++ b/docs/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/index.html @@ -0,0 +1,311 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + src + # + + + Files + # + + + + + Name + + + + + src/lds.cpp misc lds namespace functions + + + src/lds_gaussian_sys.cpp GLDS base type. + + + src/lds_poisson_sys.cpp PLDS base type. + + + src/lds_sys.cpp LDS base type. + + + src/lds_uniform_vecs.cpp Uniformly sized vectors. + + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="src"> + <meta property="og:description" content="src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>src | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>src</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#src">src</a> + <ul> + <li><a href="#files">Files</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="src"> + src + <a class="anchor" href="#src">#</a> +</h1> +<h2 id="files"> + Files + <a class="anchor" href="#files">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp">src/lds.cpp</a></strong> <br>misc lds namespace functions</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp">src/lds_gaussian_sys.cpp</a></strong> <br>GLDS base type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp">src/lds_poisson_sys.cpp</a></strong> <br>PLDS base type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp">src/lds_sys.cpp</a></strong> <br>LDS base type.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp">src/lds_uniform_vecs.cpp</a></strong> <br>Uniformly sized vectors.</td> + </tr> + </tbody> +</table> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#src">src</a> + <ul> + <li><a href="#files">Files</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html b/docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html new file mode 100644 index 00000000..d6be595b --- /dev/null +++ b/docs/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/index.html @@ -0,0 +1,311 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + examples + # + + + Files + # + + + + + Name + + + + + examples/eg_glds_ctrl.cpp + + + examples/eg_glds_du_plds_ctrl.cpp + + + examples/eg_plds_ctrl.cpp + + + examples/eg_plds_est.cpp + + + examples/eg_plds_switched_ctrl.cpp + + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="examples"> + <meta property="og:description" content="examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>examples | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>examples</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#examples">examples</a> + <ul> + <li><a href="#files">Files</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="examples"> + examples + <a class="anchor" href="#examples">#</a> +</h1> +<h2 id="files"> + Files + <a class="anchor" href="#files">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp">examples/eg_glds_ctrl.cpp</a></strong></td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp">examples/eg_glds_du_plds_ctrl.cpp</a></strong></td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp">examples/eg_plds_ctrl.cpp</a></strong></td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp">examples/eg_plds_est.cpp</a></strong></td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp">examples/eg_plds_switched_ctrl.cpp</a></strong></td> + </tr> + </tbody> +</table> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#examples">examples</a> + <ul> + <li><a href="#files">Files</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html b/docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html new file mode 100644 index 00000000..bc851d8b --- /dev/null +++ b/docs/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/index.html @@ -0,0 +1,287 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + include + # + + + Directories + # + + + + + Name + + + + + ldsCtrlEst_h + + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="include"> + <meta property="og:description" content="include # Directories # Name ldsCtrlEst_h Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>include | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>include</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#include">include</a> + <ul> + <li><a href="#directories">Directories</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="include"> + include + <a class="anchor" href="#include">#</a> +</h1> +<h2 id="directories"> + Directories + <a class="anchor" href="#directories">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/#dir-ldsctrlest-h">ldsCtrlEst_h</a></strong></td> + </tr> + </tbody> +</table> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#include">include</a> + <ul> + <li><a href="#directories">Directories</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/eg__glds__ctrl_8cpp/index.html b/docs/docs/api/files/eg__glds__ctrl_8cpp/index.html new file mode 100644 index 00000000..0e3ef361 --- /dev/null +++ b/docs/docs/api/files/eg__glds__ctrl_8cpp/index.html @@ -0,0 +1,679 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + examples/eg_glds_ctrl.cpp + # + + + Types + # + + + + + + Name + + + + + using arma::Mat&lt; data_t &gt; + Matrix + + + using arma::Col&lt; data_t &gt; + Vector + + + using double + data_t + + + + + Functions + # + + + + + + Name + + + + + int + main() + + + + + Type Details + # + + + Matrix + # + +using lds::Matrix = arma::Mat&lt;data_t&gt;; + + Vector + # + +using lds::Vector = arma::Col&lt;data_t&gt;; + + data_t + # + +using lds::data_t = double; +Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="examples/eg_glds_ctrl.cpp"> + <meta property="og:description" content="examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat&lt; data_t &gt; Matrix using arma::Col&lt; data_t &gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat&lt;data_t&gt;; Vector # using lds::Vector = arma::Col&lt;data_t&gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>examples/eg_glds_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>examples/eg_glds_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_glds_ctrlcpp">examples/eg_glds_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#data_t">data_t</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="exampleseg_glds_ctrlcpp"> + examples/eg_glds_ctrl.cpp + <a class="anchor" href="#exampleseg_glds_ctrlcpp">#</a> +</h1> +<h2 id="types"> + Types + <a class="anchor" href="#types">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>using arma::Mat&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-matrix">Matrix</a></strong></td> + </tr> + <tr> + <td>using arma::Col&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-vector">Vector</a></strong></td> + </tr> + <tr> + <td>using double</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-data-t">data_t</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>int</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#function-main">main</a></strong>()</td> + </tr> + </tbody> +</table> +<h2 id="type-details"> + Type Details + <a class="anchor" href="#type-details">#</a> +</h2> +<h3 id="matrix"> + Matrix + <a class="anchor" href="#matrix">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="vector"> + Vector + <a class="anchor" href="#vector">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="data_t"> + data_t + <a class="anchor" href="#data_t">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">double</span>; +</span></span></code></pre></div><p>Type of all data in library. If need 32b, change <code>double</code> to <code>float</code>. This could be potentially useful for large scale problems where there are memory constraints.</p> +<h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="main"> + main + <a class="anchor" href="#main">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">int</span> main() +</span></span></code></pre></div><p>Going to simulate a switching disturbance (m) acting on system</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">auto</span> <span style="color:#a6e22e">main</span>() <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">int</span> { +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ********** Example Gaussian LDS Control ********** </span><span style="color:#ae81ff">\n\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Make 1st-order SISO system, sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// no time steps for simulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">5.0</span> <span style="color:#f92672">/</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// construct ground truth system to be controlled... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// initializes to random walk model with top-most n_y state observed +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System controlled_system(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Ground-truth parameters for the controlled system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (stand-in for physical system to be controlled) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix a_true(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a_true[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> exp(<span style="color:#f92672">-</span>dt <span style="color:#f92672">/</span> <span style="color:#ae81ff">0.01</span>); +</span></span><span style="display:flex;"><span> Matrix b_true <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(<span style="color:#ae81ff">2e-4</span>); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// control signal to model input unit conversion e.g., V -&gt; mW/mm2: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector g_true <span style="color:#f92672">=</span> Vector(n_y).fill(<span style="color:#ae81ff">10.0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// output noise covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix r_true <span style="color:#f92672">=</span> Matrix(n_y, n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-4</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#75715e">// whether low or high disturbance (0, 1) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t m_low <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> dt <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_lo2hi <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; <span style="color:#75715e">// probability of going from low to high disturb. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t m_high <span style="color:#f92672">=</span> <span style="color:#ae81ff">20</span> <span style="color:#f92672">*</span> dt <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_hi2lo <span style="color:#f92672">=</span> pr_lo2hi; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// initially let m be low +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m0_true <span style="color:#f92672">=</span> Vector(n_x).fill(m_low); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Assign params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controlled_system.set_A(a_true); +</span></span><span style="display:flex;"><span> controlled_system.set_B(b_true); +</span></span><span style="display:flex;"><span> controlled_system.set_m(m0_true); +</span></span><span style="display:flex;"><span> controlled_system.set_g(g_true); +</span></span><span style="display:flex;"><span> controlled_system.set_R(r_true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;controlled_system:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> controlled_system.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make a controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>Controller controller; +</span></span><span style="display:flex;"><span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Create **incorrect** model used for control. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (e.g., imperfect model fitting) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix b_controller <span style="color:#f92672">=</span> b_true <span style="color:#f92672">/</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// let&#39;s assume zero process disturbance initially +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (will be re-estimating) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m_controller <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// for this demo, just use arbitrary default R +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix r_controller <span style="color:#f92672">=</span> Matrix(n_y, n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> lds<span style="color:#f92672">::</span>kDefaultR0; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System controller_system(controlled_system); +</span></span><span style="display:flex;"><span> controller_system.set_B(b_controller); +</span></span><span style="display:flex;"><span> controller_system.set_m(m_controller); +</span></span><span style="display:flex;"><span> controller_system.set_R(r_controller); +</span></span><span style="display:flex;"><span> controller_system.Reset(); <span style="color:#75715e">// reset to new m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// going to adaptively re-estimate the disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller_system.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set adaptation rate by changing covariance of assumed process noise +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// acting on random-walk evolution of m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_m <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-6</span>; +</span></span><span style="display:flex;"><span> controller_system.set_Q_m(q_m); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// lower and upper bounds on control signal (e.g., in Volts) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t u_lb <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; <span style="color:#75715e">// [=] V +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t u_ub <span style="color:#f92672">=</span> <span style="color:#ae81ff">5.0</span>; <span style="color:#75715e">// [=] V +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move( +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>Controller(std<span style="color:#f92672">::</span>move(controller_system), u_lb, u_ub)); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Control variables: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if following enabled, adapts set point with re-estimated process +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// disturbance n.b., should not need integral action if this is enabled as the +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// adaptive estimator minimizes DC error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> do_adaptive_set_point <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Reference/target output, controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector y_ref0 <span style="color:#f92672">=</span> Vector(n_y).fill(<span style="color:#ae81ff">20.0</span> <span style="color:#f92672">*</span> dt); +</span></span><span style="display:flex;"><span> Matrix k_x <span style="color:#f92672">=</span> Matrix(n_u, n_x).fill(<span style="color:#ae81ff">100</span>); <span style="color:#75715e">// gains on state error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix k_inty <span style="color:#f92672">=</span> Matrix(n_u, n_y).fill(<span style="color:#ae81ff">1e3</span>); <span style="color:#75715e">// gains on integrated err +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// setting initial state to target to avoid error at onset: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0 <span style="color:#f92672">=</span> Vector(n_x).fill(y_ref0[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set up controller type bit mask so controller knows how to proceed +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_adaptive_set_point) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// adapt set point with estimated disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> control_type <span style="color:#f92672">=</span> control_type <span style="color:#f92672">|</span> lds<span style="color:#f92672">::</span>kControlTypeAdaptM; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// use integral action to minimize DC error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> control_type <span style="color:#f92672">=</span> control_type <span style="color:#f92672">|</span> lds<span style="color:#f92672">::</span>kControlTypeIntY; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set controller type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_control_type(control_type); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Let&#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector g_design <span style="color:#f92672">=</span> Vector(n_u).fill(<span style="color:#ae81ff">9</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Set params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// **n.b. using arbitrary defaults for Q, R in this example. Really, these +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// should be set by users, as they tune characteristics of Kalman filter. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Users can also choose not to recursively calculate the estimator gain and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// supply it (setKe) instead of covariances.** +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_y_ref(y_ref0); +</span></span><span style="display:flex;"><span> controller.set_Kc(k_x); +</span></span><span style="display:flex;"><span> controller.set_Kc_inty(k_inty); +</span></span><span style="display:flex;"><span> controller.set_g_design(g_design); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;control system:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> controller.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set up variables for simulation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// create Matrix to save outputs in... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_ref <span style="color:#f92672">=</span> Matrix(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> y_ref0[<span style="color:#ae81ff">0</span>]; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulated measurements +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix z(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// simulated control signal ([=] V) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix u(n_u, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// outputs, states and gain/disturbance params +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// *_hat indicates online estimates +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_hat(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix x_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// *_true indicates ground truth (system being controlled) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_true(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix x_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set initial val +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_hat.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().y(); +</span></span><span style="display:flex;"><span> y_true.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_hat.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().x(); +</span></span><span style="display:flex;"><span> x_true.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> m_hat.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().m(); +</span></span><span style="display:flex;"><span> m_true.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Starting &#34;</span> <span style="color:#f92672">&lt;&lt;</span> n_t <span style="color:#f92672">*</span> dt <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; sec simulation ... </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> start <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// simulate a stochastically switched disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector chance <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>randu<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (which_m <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#75715e">// low disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_lo2hi) { <span style="color:#75715e">// switches low -&gt; high disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> m0_true <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>(n_x, m_high); +</span></span><span style="display:flex;"><span> which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { <span style="color:#75715e">// high disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_hi2lo) { <span style="color:#75715e">// swithces high -&gt; low disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> m0_true <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>(n_x, m_low); +</span></span><span style="display:flex;"><span> which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> controlled_system.set_m(m0_true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// input +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector u_tm1(u.colptr(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), u.n_rows, false, true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t) <span style="color:#f92672">=</span> controlled_system.Simulate(u_tm1); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// This method uses a steady-state solution to control problem to calculate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_ref, u_ref from reference output y_ref. Therefore, it is only +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// applicable to regulation problems or cases where reference trajectory +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// changes slowly compared to system dynamics. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u.col(t) <span style="color:#f92672">=</span> controller.ControlOutputReference(z.col(t)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// save the signals +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_true.col(t) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> x_true.col(t) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> m_true.col(t) <span style="color:#f92672">=</span> controlled_system.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> y_hat.col(t) <span style="color:#f92672">=</span> controller.sys().y(); +</span></span><span style="display:flex;"><span> x_hat.col(t) <span style="color:#f92672">=</span> controller.sys().x(); +</span></span><span style="display:flex;"><span> m_hat.col(t) <span style="color:#f92672">=</span> controller.sys().m(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> finish <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>duration<span style="color:#f92672">&lt;</span>data_t, std<span style="color:#f92672">::</span>milli<span style="color:#f92672">&gt;</span> sim_time_ms <span style="color:#f92672">=</span> finish <span style="color:#f92672">-</span> start; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Finished simulation in &#34;</span> <span style="color:#f92672">&lt;&lt;</span> sim_time_ms.count() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ms.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;(app. &#34;</span> <span style="color:#f92672">&lt;&lt;</span> (sim_time_ms.count() <span style="color:#f92672">/</span> n_t) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e3</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; us/time-step)</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Saving simulation data to disk.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// xTrue, mTrue saving with hdf5 via armadillo +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>opts replace <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>replace; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> dt_vec <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">1</span>).fill(dt); +</span></span><span style="display:flex;"><span> dt_vec.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;dt&#34;</span>)); +</span></span><span style="display:flex;"><span> y_ref.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_ref&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> u.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;u&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> z.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;z&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;m_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;m_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;fin.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span>} +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_glds_ctrlcpp">examples/eg_glds_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#data_t">data_t</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html b/docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html new file mode 100644 index 00000000..30e274c6 --- /dev/null +++ b/docs/docs/api/files/eg__glds__du__plds__ctrl_8cpp/index.html @@ -0,0 +1,691 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + examples/eg_glds_du_plds_ctrl.cpp + # + + + Types + # + + + + + + Name + + + + + using double + data_t + + + using arma::Mat&lt; data_t &gt; + Matrix + + + using arma::Col&lt; data_t &gt; + Vector + + + + + Functions + # + + + + + + Name + + + + + int + main() + + + + + Type Details + # + + + data_t + # + +using lds::data_t = double; +Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints. + + Matrix + # + +using lds::Matrix = arma::Mat&lt;data_t&gt;; + + Vector + # + +using lds::Vector = arma::Col&lt;data_t&gt;; + + Function Details + # + + + main + # + +int main() +Going to simulate a switching disturbance (m) acting on system"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="examples/eg_glds_du_plds_ctrl.cpp"> + <meta property="og:description" content="examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat&lt; data_t &gt; Matrix using arma::Col&lt; data_t &gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints. +Matrix # using lds::Matrix = arma::Mat&lt;data_t&gt;; Vector # using lds::Vector = arma::Col&lt;data_t&gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>examples/eg_glds_du_plds_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>examples/eg_glds_du_plds_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_glds_du_plds_ctrlcpp">examples/eg_glds_du_plds_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#data_t">data_t</a></li> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="exampleseg_glds_du_plds_ctrlcpp"> + examples/eg_glds_du_plds_ctrl.cpp + <a class="anchor" href="#exampleseg_glds_du_plds_ctrlcpp">#</a> +</h1> +<h2 id="types"> + Types + <a class="anchor" href="#types">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>using double</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-data-t">data_t</a></strong></td> + </tr> + <tr> + <td>using arma::Mat&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-matrix">Matrix</a></strong></td> + </tr> + <tr> + <td>using arma::Col&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-vector">Vector</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>int</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#function-main">main</a></strong>()</td> + </tr> + </tbody> +</table> +<h2 id="type-details"> + Type Details + <a class="anchor" href="#type-details">#</a> +</h2> +<h3 id="data_t"> + data_t + <a class="anchor" href="#data_t">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">double</span>; +</span></span></code></pre></div><p>Type of all data in library. If need 32b, change <code>double</code> to <code>float</code>. This could be potentially useful for large scale problems where there are memory constraints.</p> +<h3 id="matrix"> + Matrix + <a class="anchor" href="#matrix">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="vector"> + Vector + <a class="anchor" href="#vector">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="main"> + main + <a class="anchor" href="#main">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">int</span> main() +</span></span></code></pre></div><p>Going to simulate a switching disturbance (m) acting on system</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">auto</span> <span style="color:#a6e22e">main</span>() <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">int</span> { +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ********** Example Gaussian LDS du Control of PLDS ********** </span><span style="color:#ae81ff">\n\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Make 1st-order SISO system, sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// no time steps for simulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">5.0</span> <span style="color:#f92672">/</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// construct ground truth system to be controlled... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// initializes to random walk model with top-most n_y state observed +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System controlled_system(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Ground-truth parameters for the controlled system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (stand-in for physical system to be controlled) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix a_true(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a_true[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> exp(<span style="color:#f92672">-</span>dt <span style="color:#f92672">/</span> <span style="color:#ae81ff">0.01</span>); +</span></span><span style="display:flex;"><span> Matrix b_true <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(<span style="color:#ae81ff">2.5e-2</span>); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// control signal to model input unit conversion e.g., V -&gt; mW/mm2: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector g_true <span style="color:#f92672">=</span> Vector(n_y).fill(<span style="color:#ae81ff">10.0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#75715e">// whether low or high disturbance (0, 1) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t m_low <span style="color:#f92672">=</span> log(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> dt) <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_lo2hi <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">0</span>; <span style="color:#75715e">// 1e-3; // probability of going from low to high disturb. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t m_high <span style="color:#f92672">=</span> log(<span style="color:#ae81ff">20</span> <span style="color:#f92672">*</span> dt) <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_hi2lo <span style="color:#f92672">=</span> pr_lo2hi; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// initially let m be low +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m0_true <span style="color:#f92672">=</span> Vector(n_x).fill(m_low); +</span></span><span style="display:flex;"><span> Vector x0_true <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> log(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Assign params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controlled_system.set_A(a_true); +</span></span><span style="display:flex;"><span> controlled_system.set_B(b_true); +</span></span><span style="display:flex;"><span> controlled_system.set_m(m0_true); +</span></span><span style="display:flex;"><span> controlled_system.set_g(g_true); +</span></span><span style="display:flex;"><span> controlled_system.set_x0(x0_true); +</span></span><span style="display:flex;"><span> controlled_system.Reset(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;controlled_system:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> controlled_system.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make a controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>Controller controller; +</span></span><span style="display:flex;"><span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Create **incorrect** model used for control. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (e.g., imperfect model fitting) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix b_controller <span style="color:#f92672">=</span> b_true <span style="color:#f92672">/</span> <span style="color:#ae81ff">50</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// let&#39;s assume zero process disturbance initially +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (will be re-estimating) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m_controller <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// process noise covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_controller <span style="color:#f92672">=</span> Matrix(n_y, n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-8</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// output noise covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix r_controller <span style="color:#f92672">=</span> Matrix(n_y, n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System controller_system(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> controller_system.set_A(a_true); +</span></span><span style="display:flex;"><span> controller_system.set_B(b_controller); +</span></span><span style="display:flex;"><span> controller_system.set_g(g_true); +</span></span><span style="display:flex;"><span> controller_system.set_m(m_controller); +</span></span><span style="display:flex;"><span> controller_system.set_Q(q_controller); +</span></span><span style="display:flex;"><span> controller_system.set_R(r_controller); +</span></span><span style="display:flex;"><span> controller_system.Reset(); <span style="color:#75715e">// reset to new m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// going to adaptively re-estimate the disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller_system.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set adaptation rate by changing covariance of assumed process noise +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// acting on random-walk evolution of m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_m <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-8</span>; +</span></span><span style="display:flex;"><span> controller_system.set_Q_m(q_m); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// lower and upper bounds on control signal (e.g., in Volts) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t u_lb <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; <span style="color:#75715e">// [=] V +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t u_ub <span style="color:#f92672">=</span> <span style="color:#ae81ff">5.0</span>; <span style="color:#75715e">// [=] V +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move( +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>Controller(std<span style="color:#f92672">::</span>move(controller_system), u_lb, u_ub)); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Control variables: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Reference/target output, controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector y_ref0 <span style="color:#f92672">=</span> Vector(n_y).fill(<span style="color:#ae81ff">20.0</span> <span style="color:#f92672">*</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// to design for this example, augmented state with control and made the input +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 1e-2. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix k_x <span style="color:#f92672">=</span> Matrix(n_u, n_x).fill(<span style="color:#ae81ff">2.44</span>); <span style="color:#75715e">// gains on state error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix k_inty <span style="color:#f92672">=</span> Matrix(n_u, n_y).fill(<span style="color:#ae81ff">97.4</span>); <span style="color:#75715e">// gains on integrated err +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix k_u <span style="color:#f92672">=</span> Matrix(n_u, n_u).fill(<span style="color:#ae81ff">5.23e-2</span>); <span style="color:#75715e">// gains on input amp +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set up controller type bit mask so controller knows how to proceed +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// use integral action to minimize DC error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> control_type <span style="color:#f92672">=</span> control_type <span style="color:#f92672">|</span> lds<span style="color:#f92672">::</span>kControlTypeIntY; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update change in control (LP filters control) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> control_type <span style="color:#f92672">=</span> control_type <span style="color:#f92672">|</span> lds<span style="color:#f92672">::</span>kControlTypeDeltaU; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set controller type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_control_type(control_type); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Let&#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector g_design <span style="color:#f92672">=</span> Vector(n_u).fill(<span style="color:#ae81ff">10</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Set params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// **n.b. using arbitrary defaults for Q, R in this example. Really, these +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// should be set by users, as they tune characteristics of Kalman filter. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Users can also choose not to recursively calculate the estimator gain and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// supply it (setKe) instead of covariances.** +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_y_ref(y_ref0); +</span></span><span style="display:flex;"><span> controller.set_Kc(k_x); +</span></span><span style="display:flex;"><span> controller.set_Kc_inty(k_inty); +</span></span><span style="display:flex;"><span> controller.set_Kc_u(k_u); +</span></span><span style="display:flex;"><span> controller.set_g_design(g_design); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;control system:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> controller.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set up variables for simulation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// create Matrix to save outputs in... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_ref <span style="color:#f92672">=</span> Matrix(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> y_ref0[<span style="color:#ae81ff">0</span>]; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulated measurements +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix z(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// simulated control signal ([=] V) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix u(n_u, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// outputs, states and gain/disturbance params +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// *_hat indicates online estimates +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_hat(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix x_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// *_true indicates ground truth (system being controlled) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_true(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix x_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// get initial val +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_hat.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().y(); +</span></span><span style="display:flex;"><span> y_true.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_hat.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().x(); +</span></span><span style="display:flex;"><span> x_true.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> m_hat.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().m(); +</span></span><span style="display:flex;"><span> m_true.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Starting &#34;</span> <span style="color:#f92672">&lt;&lt;</span> n_t <span style="color:#f92672">*</span> dt <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; sec simulation ... </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> start <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// simulate a stochastically switched disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector chance <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>randu<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (which_m <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#75715e">// low disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_lo2hi) { <span style="color:#75715e">// switches low -&gt; high disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> m0_true <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>(n_x, m_high); +</span></span><span style="display:flex;"><span> which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { <span style="color:#75715e">// high disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_hi2lo) { <span style="color:#75715e">// swithces high -&gt; low disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> m0_true <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>(n_x, m_low); +</span></span><span style="display:flex;"><span> which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> controlled_system.set_m(m0_true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// input +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector u_tm1(u.colptr(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), u.n_rows, false, true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t) <span style="color:#f92672">=</span> controlled_system.Simulate(u_tm1); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// This method uses a steady-state solution to control problem to calculate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_ref, u_ref from reference output y_ref. Therefore, it is only +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// applicable to regulation problems or cases where reference trajectory +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// changes slowly compared to system dynamics. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u.col(t) <span style="color:#f92672">=</span> controller.ControlOutputReference(z.col(t)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// save the signals +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_true.col(t) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> x_true.col(t) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> m_true.col(t) <span style="color:#f92672">=</span> controlled_system.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> y_hat.col(t) <span style="color:#f92672">=</span> controller.sys().y(); +</span></span><span style="display:flex;"><span> x_hat.col(t) <span style="color:#f92672">=</span> controller.sys().x(); +</span></span><span style="display:flex;"><span> m_hat.col(t) <span style="color:#f92672">=</span> controller.sys().m(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> finish <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>duration<span style="color:#f92672">&lt;</span>data_t, std<span style="color:#f92672">::</span>milli<span style="color:#f92672">&gt;</span> sim_time_ms <span style="color:#f92672">=</span> finish <span style="color:#f92672">-</span> start; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Finished simulation in &#34;</span> <span style="color:#f92672">&lt;&lt;</span> sim_time_ms.count() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ms.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;(app. &#34;</span> <span style="color:#f92672">&lt;&lt;</span> (sim_time_ms.count() <span style="color:#f92672">/</span> n_t) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e3</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; us/time-step)</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Saving simulation data to disk.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// xTrue, mTrue saving with hdf5 via armadillo +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>opts replace <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>replace; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> dt_vec <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">1</span>).fill(dt); +</span></span><span style="display:flex;"><span> dt_vec.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;dt&#34;</span>)); +</span></span><span style="display:flex;"><span> y_ref.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_ref&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> u.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;u&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> z.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;z&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;m_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;m_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_glds_du_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;fin.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span>} +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_glds_du_plds_ctrlcpp">examples/eg_glds_du_plds_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#data_t">data_t</a></li> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/eg__plds__ctrl_8cpp/index.html b/docs/docs/api/files/eg__plds__ctrl_8cpp/index.html new file mode 100644 index 00000000..a0bf2a5f --- /dev/null +++ b/docs/docs/api/files/eg__plds__ctrl_8cpp/index.html @@ -0,0 +1,649 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + examples/eg_plds_ctrl.cpp + # + + + Types + # + + + + + + Name + + + + + using arma::Mat&lt; data_t &gt; + Matrix + + + using arma::Col&lt; data_t &gt; + Vector + + + using double + data_t + + + + + Functions + # + + + + + + Name + + + + + int + main() + + + + + Type Details + # + + + Matrix + # + +using lds::Matrix = arma::Mat&lt;data_t&gt;; + + Vector + # + +using lds::Vector = arma::Col&lt;data_t&gt;; + + data_t + # + +using lds::data_t = double; +Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="examples/eg_plds_ctrl.cpp"> + <meta property="og:description" content="examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat&lt; data_t &gt; Matrix using arma::Col&lt; data_t &gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat&lt;data_t&gt;; Vector # using lds::Vector = arma::Col&lt;data_t&gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>examples/eg_plds_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>examples/eg_plds_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_plds_ctrlcpp">examples/eg_plds_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#data_t">data_t</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="exampleseg_plds_ctrlcpp"> + examples/eg_plds_ctrl.cpp + <a class="anchor" href="#exampleseg_plds_ctrlcpp">#</a> +</h1> +<h2 id="types"> + Types + <a class="anchor" href="#types">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>using arma::Mat&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-matrix">Matrix</a></strong></td> + </tr> + <tr> + <td>using arma::Col&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-vector">Vector</a></strong></td> + </tr> + <tr> + <td>using double</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-data-t">data_t</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>int</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#function-main">main</a></strong>()</td> + </tr> + </tbody> +</table> +<h2 id="type-details"> + Type Details + <a class="anchor" href="#type-details">#</a> +</h2> +<h3 id="matrix"> + Matrix + <a class="anchor" href="#matrix">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="vector"> + Vector + <a class="anchor" href="#vector">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="data_t"> + data_t + <a class="anchor" href="#data_t">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">double</span>; +</span></span></code></pre></div><p>Type of all data in library. If need 32b, change <code>double</code> to <code>float</code>. This could be potentially useful for large scale problems where there are memory constraints.</p> +<h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="main"> + main + <a class="anchor" href="#main">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">int</span> main() +</span></span></code></pre></div><p>Going to simulate a switching disturbance (m) acting on system</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">auto</span> <span style="color:#a6e22e">main</span>() <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">int</span> { +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ********** Example Poisson LDS Control ********** </span><span style="color:#ae81ff">\n\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Make SISO system sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// no time steps for simulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">10.0</span> <span style="color:#f92672">/</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Control variables: _reference/target output, controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// n.b., Can either use Vector (arma::Col) or std::vector +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector y_ref0 <span style="color:#f92672">=</span> Vector(n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> <span style="color:#ae81ff">30.0</span> <span style="color:#f92672">*</span> dt; +</span></span><span style="display:flex;"><span> Matrix k_x <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> Matrix(n_u, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros) <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// gains on state error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix k_inty <span style="color:#f92672">=</span> Matrix(n_u, n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros) <span style="color:#f92672">+</span> +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">10</span>; <span style="color:#75715e">// gains on integrated output err +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Set control type bit mask, so controller knows what to do +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t control_type <span style="color:#f92672">=</span> lds<span style="color:#f92672">::</span>kControlTypeIntY; <span style="color:#75715e">// integral action +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Ground-truth parameters for the controlled system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (stand-in for physical system to be controlled) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix a_true(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a_true[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.986</span>; +</span></span><span style="display:flex;"><span> Matrix b_true(n_x, n_u, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> b_true[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.054</span>; +</span></span><span style="display:flex;"><span> Vector x0_true <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> log(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> data_t m_low <span style="color:#f92672">=</span> log(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> dt) <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_lo2hi <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> data_t m_high <span style="color:#f92672">=</span> log(<span style="color:#ae81ff">20</span> <span style="color:#f92672">*</span> dt) <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_hi2lo <span style="color:#f92672">=</span> pr_lo2hi; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector m0_true <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> m_low; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// construct ground truth system to be controlled... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System controlled_system(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Assign params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controlled_system.set_A(a_true); +</span></span><span style="display:flex;"><span> controlled_system.set_B(b_true); +</span></span><span style="display:flex;"><span> controlled_system.set_m(m0_true); +</span></span><span style="display:flex;"><span> controlled_system.set_x0(x0_true); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// reset to initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controlled_system.Reset(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;controlled_system:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> controlled_system.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Create the controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>Controller controller; +</span></span><span style="display:flex;"><span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Create model used for control. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System controller_system(controlled_system); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// for this example, assume model correct, except disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m0_controller <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> m_low; +</span></span><span style="display:flex;"><span> Vector x0_controller <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>log(y_ref0); +</span></span><span style="display:flex;"><span> controller_system.set_m(m0_controller); +</span></span><span style="display:flex;"><span> controller_system.set_x0(x0_controller); +</span></span><span style="display:flex;"><span> controller_system.Reset(); <span style="color:#75715e">//reset to new init condition +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// adaptively re-estimate process disturbance (m) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller_system.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set adaptation rate by changing covariance of assumed process noise +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// acting on random-walk evolution of m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_m <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-5</span>; +</span></span><span style="display:flex;"><span> controller_system.set_Q_m(q_m); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t u_lb <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; +</span></span><span style="display:flex;"><span> data_t u_ub <span style="color:#f92672">=</span> <span style="color:#ae81ff">5.0</span>; +</span></span><span style="display:flex;"><span> controller <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move( +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>Controller(std<span style="color:#f92672">::</span>move(controller_system), u_lb, u_ub)); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set controller type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_control_type(control_type); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_Kc(k_x); +</span></span><span style="display:flex;"><span> controller.set_Kc_inty(k_inty); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// to protect against integral windup when output is consistently above +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// target: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t tau_awu(<span style="color:#ae81ff">0.1</span>); +</span></span><span style="display:flex;"><span> controller.set_tau_awu(tau_awu); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;controller:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> controller.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create Matrix to save outputs in... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_ref <span style="color:#f92672">=</span> Matrix(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> y_ref.each_col() <span style="color:#f92672">+=</span> y_ref0; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulated measurements +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix z(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// simulated control signal ([=] V) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix u(n_u, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// outputs, states and gain/disturbance params +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// *_hat indicates online estimates +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_hat(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix x_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_hat(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// *_true indicates ground truth (system being controlled) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_true(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix x_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_true(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set initial val +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().y(); +</span></span><span style="display:flex;"><span> y_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().x(); +</span></span><span style="display:flex;"><span> x_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> m_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controller.sys().m(); +</span></span><span style="display:flex;"><span> m_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Starting &#34;</span> <span style="color:#f92672">&lt;&lt;</span> n_t <span style="color:#f92672">*</span> dt <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; sec simulation ... </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> start <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// simulate a stochastically switched disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector chance <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>randu<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (which_m <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#75715e">// low disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_lo2hi) { <span style="color:#75715e">// switches low -&gt; high disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> m0_true <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>(n_x, m_high); +</span></span><span style="display:flex;"><span> which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { <span style="color:#75715e">// high disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_hi2lo) { <span style="color:#75715e">// swithces high -&gt; low disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> m0_true <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>(n_x, m_low); +</span></span><span style="display:flex;"><span> which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> controlled_system.set_m(m0_true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// e.g., use sinusoidal reference +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t f <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.5</span>; <span style="color:#75715e">// freq [=] Hz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector t_vec <span style="color:#f92672">=</span> Vector(n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones) <span style="color:#f92672">*</span> t; +</span></span><span style="display:flex;"><span> y_ref.col(t) <span style="color:#f92672">+=</span> +</span></span><span style="display:flex;"><span> y_ref0 <span style="color:#f92672">%</span> arma<span style="color:#f92672">::</span>sin(f <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> lds<span style="color:#f92672">::</span>kPi <span style="color:#f92672">*</span> dt <span style="color:#f92672">*</span> t_vec <span style="color:#f92672">-</span> lds<span style="color:#f92672">::</span>kPi <span style="color:#f92672">/</span> <span style="color:#ae81ff">4</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t)<span style="color:#f92672">=</span>controlled_system.Simulate(u.col(t<span style="color:#f92672">-</span><span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// This method uses a steady-state solution to control problem to calculate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_ref, u_ref from reference output y_ref. Notably, it does this in the +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// log-linear space (i.e., log(y)). +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Therefore, it is only applicable to regulation problems or cases where +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// reference trajectory changes slowly compared to system dynamics. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_y_ref(y_ref.col(t)); +</span></span><span style="display:flex;"><span> u.col(t)<span style="color:#f92672">=</span>controller.ControlOutputReference(z.col(t)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> y_true.col(t) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> x_true.col(t) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> m_true.col(t) <span style="color:#f92672">=</span> controlled_system.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> y_hat.col(t) <span style="color:#f92672">=</span> controller.sys().y(); +</span></span><span style="display:flex;"><span> x_hat.col(t) <span style="color:#f92672">=</span> controller.sys().x(); +</span></span><span style="display:flex;"><span> m_hat.col(t) <span style="color:#f92672">=</span> controller.sys().m(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> finish <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>duration<span style="color:#f92672">&lt;</span>data_t, std<span style="color:#f92672">::</span>milli<span style="color:#f92672">&gt;</span> sim_time_ms <span style="color:#f92672">=</span> finish <span style="color:#f92672">-</span> start; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Finished simulation in &#34;</span> <span style="color:#f92672">&lt;&lt;</span> sim_time_ms.count() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ms.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;(app. &#34;</span> <span style="color:#f92672">&lt;&lt;</span> (sim_time_ms.count() <span style="color:#f92672">/</span> n_t) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e3</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; us/time-step)</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_true, m_true saving with hdf5 via armadillo +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>opts replace <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>replace; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> dt_vec <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">1</span>).fill(dt); +</span></span><span style="display:flex;"><span> dt_vec.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;dt&#34;</span>)); +</span></span><span style="display:flex;"><span> y_ref.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_ref&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> u.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;u&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> z.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;z&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;m_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;m_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span>} +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_plds_ctrlcpp">examples/eg_plds_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#data_t">data_t</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/eg__plds__est_8cpp/index.html b/docs/docs/api/files/eg__plds__est_8cpp/index.html new file mode 100644 index 00000000..4a5b8ca0 --- /dev/null +++ b/docs/docs/api/files/eg__plds__est_8cpp/index.html @@ -0,0 +1,617 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + examples/eg_plds_est.cpp + # + + + Types + # + + + + + + Name + + + + + using arma::Mat&lt; data_t &gt; + Matrix + + + using arma::Col&lt; data_t &gt; + Vector + + + using double + data_t + + + + + Functions + # + + + + + + Name + + + + + Matrix + random_walk(size_t n_t, const Matrix &amp; Q, const Vector &amp; x0) + + + int + main() + + + + + Type Details + # + + + Matrix + # + +using lds::Matrix = arma::Mat&lt;data_t&gt;; + + Vector + # + +using lds::Vector = arma::Col&lt;data_t&gt;; + + data_t + # + +using lds::data_t = double; +Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="examples/eg_plds_est.cpp"> + <meta property="og:description" content="examples/eg_plds_est.cpp # Types # Name using arma::Mat&lt; data_t &gt; Matrix using arma::Col&lt; data_t &gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix &amp; Q, const Vector &amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat&lt;data_t&gt;; Vector # using lds::Vector = arma::Col&lt;data_t&gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>examples/eg_plds_est.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>examples/eg_plds_est.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_plds_estcpp">examples/eg_plds_est.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#data_t">data_t</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#random_walk">random_walk</a></li> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="exampleseg_plds_estcpp"> + examples/eg_plds_est.cpp + <a class="anchor" href="#exampleseg_plds_estcpp">#</a> +</h1> +<h2 id="types"> + Types + <a class="anchor" href="#types">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>using arma::Mat&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-matrix">Matrix</a></strong></td> + </tr> + <tr> + <td>using arma::Col&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-vector">Vector</a></strong></td> + </tr> + <tr> + <td>using double</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-data-t">data_t</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-random-walk">random_walk</a></strong>(size_t n_t, const Matrix &amp; Q, const Vector &amp; x0)</td> + </tr> + <tr> + <td>int</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-main">main</a></strong>()</td> + </tr> + </tbody> +</table> +<h2 id="type-details"> + Type Details + <a class="anchor" href="#type-details">#</a> +</h2> +<h3 id="matrix"> + Matrix + <a class="anchor" href="#matrix">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="vector"> + Vector + <a class="anchor" href="#vector">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="data_t"> + data_t + <a class="anchor" href="#data_t">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">double</span>; +</span></span></code></pre></div><p>Type of all data in library. If need 32b, change <code>double</code> to <code>float</code>. This could be potentially useful for large scale problems where there are memory constraints.</p> +<h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="random_walk"> + random_walk + <a class="anchor" href="#random_walk">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix random_walk( +</span></span><span style="display:flex;"><span> size_t n_t, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> Q, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> x0 +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><h3 id="main"> + main + <a class="anchor" href="#main">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">int</span> main() +</span></span></code></pre></div><h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// for generating random input +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Matrix <span style="color:#a6e22e">random_walk</span>(size_t n_t, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q, <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">int</span> <span style="color:#a6e22e">main</span>() { +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ********** Example Poisson LDS Estimation ********** </span><span style="color:#ae81ff">\n\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Make SISO system sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// no. inputs +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// no. states +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// no. outputs +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">30</span> <span style="color:#f92672">/</span> dt); <span style="color:#75715e">// no time steps for simulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// construct ground truth system... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System system_true(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Model parameters +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix a_true(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a_true[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> exp(<span style="color:#f92672">-</span>dt <span style="color:#f92672">/</span> <span style="color:#ae81ff">0.075</span>); +</span></span><span style="display:flex;"><span> Matrix b_true <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(<span style="color:#ae81ff">1e-2</span>); +</span></span><span style="display:flex;"><span> Vector m0_true <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros).fill(<span style="color:#f92672">-</span><span style="color:#ae81ff">7e-2</span>); <span style="color:#75715e">// disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_true <span style="color:#f92672">=</span> m0_true <span style="color:#f92672">*</span> arma<span style="color:#f92672">::</span>inv(Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">-</span> +</span></span><span style="display:flex;"><span> a_true); <span style="color:#75715e">// initial state +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Assign params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_true.set_A(a_true); +</span></span><span style="display:flex;"><span> system_true.set_B(b_true); +</span></span><span style="display:flex;"><span> system_true.set_x0(x0_true); +</span></span><span style="display:flex;"><span> system_true.set_m(m0_true); +</span></span><span style="display:flex;"><span> system_true.Reset(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Construct system for estimation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// e.g., will create a model with incorrect disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System system_estimator(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Can copy parameters from another system object +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator <span style="color:#f92672">=</span> system_true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// wrong disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m0_est <span style="color:#f92672">=</span> m0_true <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> system_estimator.set_m(m0_est); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set new initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_est <span style="color:#f92672">=</span> m0_est <span style="color:#f92672">*</span> arma<span style="color:#f92672">::</span>inv(Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">-</span> +</span></span><span style="display:flex;"><span> a_true); <span style="color:#75715e">// initial state +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator.set_x0(x0_est); +</span></span><span style="display:flex;"><span> system_estimator.Reset(); <span style="color:#75715e">// reset to initial condition. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// turn on adaptive disturbance estimation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set adaptation rate by changing covariance of assumed process noise acting +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// on random-walk evolution of m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_m <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-6</span>; +</span></span><span style="display:flex;"><span> system_estimator.set_Q_m(q_m); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;estimator:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> system_estimator.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Set up simulation : +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Simulated measurements +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix z(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Stimulus (generate random stimulus) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_u <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> Matrix(n_u, n_u, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-3</span>; <span style="color:#75715e">// cov of random walk +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix u <span style="color:#f92672">=</span> random_walk(n_t, q_u, Vector(n_u, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create matrix to save outputs in... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_hat(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix y_true(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// states and disturbance params +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix x_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix x_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix m_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> system_estimator.y(); +</span></span><span style="display:flex;"><span> y_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> system_true.y(); +</span></span><span style="display:flex;"><span> x_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> system_estimator.x(); +</span></span><span style="display:flex;"><span> x_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> system_true.x(); +</span></span><span style="display:flex;"><span> m_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> system_estimator.m(); +</span></span><span style="display:flex;"><span> m_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> system_true.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Starting &#34;</span> <span style="color:#f92672">&lt;&lt;</span> n_t <span style="color:#f92672">*</span> dt <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; sec simlation ... </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> start <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simlate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t) <span style="color:#f92672">=</span> system_true.Simulate(u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Filter (predict -&gt; update) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator.Filter(u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), z.col(t)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// save signals +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_hat.col(t) <span style="color:#f92672">=</span> system_estimator.y(); +</span></span><span style="display:flex;"><span> y_true.col(t) <span style="color:#f92672">=</span> system_true.y(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_true.col(t) <span style="color:#f92672">=</span> system_true.x(); +</span></span><span style="display:flex;"><span> m_true.col(t) <span style="color:#f92672">=</span> system_true.m(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_hat.col(t) <span style="color:#f92672">=</span> system_estimator.x(); +</span></span><span style="display:flex;"><span> m_hat.col(t) <span style="color:#f92672">=</span> system_estimator.m(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> finish <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>duration<span style="color:#f92672">&lt;</span>data_t, std<span style="color:#f92672">::</span>milli<span style="color:#f92672">&gt;</span> sim_time_ms <span style="color:#f92672">=</span> finish <span style="color:#f92672">-</span> start; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Finished simlation in &#34;</span> <span style="color:#f92672">&lt;&lt;</span> sim_time_ms.count() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ms.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;(app. &#34;</span> <span style="color:#f92672">&lt;&lt;</span> (sim_time_ms.count() <span style="color:#f92672">/</span> n_t) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e3</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; us/time-step)</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_true, m_true saving with hdf5 via armadillo +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>opts replace <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>replace; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> dt_vec <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">1</span>).fill(dt); +</span></span><span style="display:flex;"><span> dt_vec.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;dt&#34;</span>)); +</span></span><span style="display:flex;"><span> u.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;u&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> z.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;z&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;x_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;m_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;y_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;x_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> m_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;m_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_est.h5&#34;</span>, <span style="color:#e6db74">&#34;y_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// for generating random input +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>Matrix <span style="color:#a6e22e">random_walk</span>(size_t n_t, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q, <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x0) { +</span></span><span style="display:flex;"><span> size_t n <span style="color:#f92672">=</span> Q.n_rows; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ((n <span style="color:#f92672">!=</span> Q.n_cols) <span style="color:#f92672">||</span> (Q.n_cols <span style="color:#f92672">!=</span> Q.n_rows)) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>logic_error(<span style="color:#e6db74">&#34;Q must be `n` x `n`.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix x(n, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> x.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> x0; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> x.col(t) <span style="color:#f92672">=</span> x.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+</span> arma<span style="color:#f92672">::</span>mvnrnd(Vector(n, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros), Q); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> x; +</span></span><span style="display:flex;"><span>} +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_plds_estcpp">examples/eg_plds_est.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#data_t">data_t</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#random_walk">random_walk</a></li> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html b/docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html new file mode 100644 index 00000000..21e2396a --- /dev/null +++ b/docs/docs/api/files/eg__plds__switched__ctrl_8cpp/index.html @@ -0,0 +1,880 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + examples/eg_plds_switched_ctrl.cpp + # + + + Types + # + + + + + + Name + + + + + using double + data_t + + + using arma::Mat&lt; data_t &gt; + Matrix + + + using arma::Col&lt; data_t &gt; + Vector + + + + + Functions + # + + + + + + Name + + + + + int + main() + + + + + Type Details + # + + + data_t + # + +using lds::data_t = double; +Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints. + + Matrix + # + +using lds::Matrix = arma::Mat&lt;data_t&gt;; + + Vector + # + +using lds::Vector = arma::Col&lt;data_t&gt;; + + Function Details + # + + + main + # + +int main() + + Source code + # + +//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// +// +// Copyright 2021 Michael Bolus +// Copyright 2021 Georgia Institute of Technology +// +// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +#include &lt;ldsCtrlEst&gt; + +using lds::data_t; +using lds::Matrix; +using lds::Vector; +using std::cout; + +auto main() -&gt; int { + cout &lt;&lt; &#34; ********** Example Switched Poisson LDS Control ********** \n\n&#34;; + + // whether to do switched control + bool do_switch_ctrl = true; + + // Make SISO system sampled at 1kHz + data_t dt = 1e-3; + size_t n_u = 1; + size_t n_x = 1; + size_t n_y = 1; + + // no time steps for simulation. + auto n_t = static_cast&lt;size_t&gt;(30.0 / dt); + + // for simulating switching + size_t which_mode = 1; + data_t pr_21 = 1e-3; // prob mode 1 -&gt; 2 + data_t pr_12 = pr_21; // prob mode 2 -&gt; 1 + + // simulated system being controlled + lds::poisson::System controlled_system(n_u, n_x, n_y, dt); + + // **Assume the system is not well characterized by one LDS, but is well + // characterized by two LDS models with different input matrices.** + data_t scale_sys_b = 2; + + Matrix a(n_x, n_x, arma::fill::eye); + a[0] = 0.985; + Matrix b1 = Matrix(n_x, n_u).fill(0.05); + Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); + + controlled_system.set_A(a); + controlled_system.set_B(b1); + controlled_system.set_d(d); + controlled_system.Reset(); // reset to initial conditions + + // reference + Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); + + // Let underlying system 1 be more sensitive than system 2 + Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); + + // create switched controller + lds::poisson::SwitchedController switched_controller; + lds::UniformMatrixList&lt;&gt; k_x; // feedback controller gains + { + // create switched controller sub-systems + // system 1 + lds::poisson::System sys1(controlled_system); + + // set process noise covariance + Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; + sys1.set_Q(q_controller); + + // adaptively estimate process disturbance (m) + // n.b. using arbitrary default value for process noise if enabled. + sys1.do_adapt_m = true; + + // setting initial mode to target to avoid large error at onset: + Vector x0_controller = arma::log(y_ref0) - d; + sys1.set_x0(x0_controller); + sys1.Reset(); // reset to initial conditions + + cout &lt;&lt; &#34;.....................................\n&#34;; + cout &lt;&lt; &#34;sys1:\n&#34;; + cout &lt;&lt; &#34;.....................................\n&#34;; + sys1.Print(); + cout &lt;&lt; &#34;.....................................\n&#34;; + + // system 2 + lds::poisson::System sys2 = sys1; + + // set parameters + sys2.set_B(b2); + + cout &lt;&lt; &#34;.....................................\n&#34;; + cout &lt;&lt; &#34;sys2:\n&#34;; + cout &lt;&lt; &#34;.....................................\n&#34;; + sys2.Print(); + cout &lt;&lt; &#34;.....................................\n&#34;; + + lds::UniformSystemList&lt;lds::poisson::System&gt; systems({sys1, sys2}); + + // controller gains for underlying system s: + Matrix k_x1(n_u, n_x, arma::fill::ones); + Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. + k_x = lds::UniformMatrixList&lt;&gt;({k_x1, k_x2}); + + data_t u_lb = 0.0; + data_t u_ub = 5.0; + switched_controller = std::move( + lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); + } + // Control variables + size_t control_type = 0; // no integral action, etc + switched_controller.set_control_type(control_type); + switched_controller.set_Kc(std::move(k_x)); + + switched_controller.set_y_ref(y_ref0); + + std::vector&lt;lds::poisson::System&gt; systems_vec(3, lds::poisson::System()); + lds::UniformSystemList&lt;lds::poisson::System&gt; systems(std::move(systems_vec)); + + cout &lt;&lt; &#34;.....................................\n&#34;; + cout &lt;&lt; &#34;switched_controller:\n&#34;; + cout &lt;&lt; &#34;.....................................\n&#34;; + switched_controller.Print(); + cout &lt;&lt; &#34;.....................................\n&#34;; + + // Fake measurements + Matrix z(n_y, n_t, arma::fill::zeros); + + // Will later contain control. + Matrix u(n_u, n_t, arma::fill::zeros); + + // create Matrix to save outputs in... + Matrix y_hat(n_y, n_t, arma::fill::zeros); + Matrix y_true(n_y, n_t, arma::fill::zeros); + Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); + + // modes and gain/disturbance params + Matrix x_hat(n_x, n_t, arma::fill::zeros); + Matrix x_true(n_x, n_t, arma::fill::zeros); + Matrix mode(1, n_t, arma::fill::ones); + + // set initial val + y_hat.col(0) = switched_controller.sys().y(); + y_true.col(0) = controlled_system.y(); + x_hat.col(0) = switched_controller.sys().x(); + x_true.col(0) = controlled_system.x(); + + cout &lt;&lt; &#34;Starting &#34; &lt;&lt; n_t * dt &lt;&lt; &#34; sec simulation ... \n&#34;; + auto start = std::chrono::high_resolution_clock::now(); + for (size_t t = 1; t &lt; n_t; t&#43;&#43;) { + // Let the controlled system stochastically change gain + // Assume another algorithm decodes this mode change and signals the + // switched_controller + Vector chance(1, arma::fill::randu); + if (which_mode == 1) // mode1 + { + if (chance[0] &lt; pr_21) { + which_mode = 2; + controlled_system.set_B(b2); + if (do_switch_ctrl) { + switched_controller.Switch(1); + } + } + } else { // mode2 + if (chance[0] &lt; pr_12) { + which_mode = 1; + controlled_system.set_B(b1); + if (do_switch_ctrl) { + switched_controller.Switch(0); + } + } + } + + // Simulate the true system. + z.col(t) = controlled_system.Simulate(u.col(t - 1)); + + // perform control + u.col(t) = switched_controller.ControlOutputReference(z.col(t)); + + mode.col(t) = which_mode; + y_ref.col(t) = y_ref0; + y_true.col(t) = controlled_system.y(); + x_true.col(t) = controlled_system.x(); + y_hat.col(t) = switched_controller.sys().y(); + x_hat.col(t) = switched_controller.sys().x(); + } + + auto finish = std::chrono::high_resolution_clock::now(); + std::chrono::duration&lt;data_t, std::milli&gt; sim_time_ms = finish - start; + cout &lt;&lt; &#34;Finished simulation in &#34; &lt;&lt; sim_time_ms.count() &lt;&lt; &#34; ms.\n&#34;; + cout &lt;&lt; &#34;(app. &#34; &lt;&lt; (sim_time_ms.count() / n_t) * 1e3 &lt;&lt; &#34; us/time-step)\n&#34;; + + // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, + // x_true, m_true saving with hdf5 via armadillo + arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; + + auto dt_vec = Vector(1).fill(dt); + dt_vec.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;dt&#34;)); + y_ref.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;y_ref&#34;, replace)); + u.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;u&#34;, replace)); + z.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;z&#34;, replace)); + x_true.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;x_true&#34;, replace)); + y_true.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;y_true&#34;, replace)); + x_hat.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;x_hat&#34;, replace)); + y_hat.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;y_hat&#34;, replace)); + mode.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;mode&#34;, replace)); + + return 0; +} + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="examples/eg_plds_switched_ctrl.cpp"> + <meta property="og:description" content="examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat&lt; data_t &gt; Matrix using arma::Col&lt; data_t &gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints. +Matrix # using lds::Matrix = arma::Mat&lt;data_t&gt;; Vector # using lds::Vector = arma::Col&lt;data_t&gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an &#34;AS IS&#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include &lt;ldsCtrlEst&gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -&gt; int { cout &lt;&lt; &#34; ********** Example Switched Poisson LDS Control ********** \n\n&#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast&lt;size_t&gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -&gt; 2 data_t pr_12 = pr_21; // prob mode 2 -&gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList&lt;&gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout &lt;&lt; &#34;.....................................\n&#34;; cout &lt;&lt; &#34;sys1:\n&#34;; cout &lt;&lt; &#34;.....................................\n&#34;; sys1.Print(); cout &lt;&lt; &#34;.....................................\n&#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout &lt;&lt; &#34;.....................................\n&#34;; cout &lt;&lt; &#34;sys2:\n&#34;; cout &lt;&lt; &#34;.....................................\n&#34;; sys2.Print(); cout &lt;&lt; &#34;.....................................\n&#34;; lds::UniformSystemList&lt;lds::poisson::System&gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList&lt;&gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector&lt;lds::poisson::System&gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList&lt;lds::poisson::System&gt; systems(std::move(systems_vec)); cout &lt;&lt; &#34;.....................................\n&#34;; cout &lt;&lt; &#34;switched_controller:\n&#34;; cout &lt;&lt; &#34;.....................................\n&#34;; switched_controller.Print(); cout &lt;&lt; &#34;.....................................\n&#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout &lt;&lt; &#34;Starting &#34; &lt;&lt; n_t * dt &lt;&lt; &#34; sec simulation ... \n&#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t &lt; n_t; t&#43;&#43;) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] &lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] &lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration&lt;data_t, std::milli&gt; sim_time_ms = finish - start; cout &lt;&lt; &#34;Finished simulation in &#34; &lt;&lt; sim_time_ms.count() &lt;&lt; &#34; ms.\n&#34;; cout &lt;&lt; &#34;(app. &#34; &lt;&lt; (sim_time_ms.count() / n_t) * 1e3 &lt;&lt; &#34; us/time-step)\n&#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;dt&#34;)); y_ref.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;y_ref&#34;, replace)); u.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;u&#34;, replace)); z.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;z&#34;, replace)); x_true.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;x_true&#34;, replace)); y_true.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;y_true&#34;, replace)); x_hat.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;x_hat&#34;, replace)); y_hat.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;y_hat&#34;, replace)); mode.save(arma::hdf5_name(&#34;eg_plds_switched_ctrl.h5&#34;, &#34;mode&#34;, replace)); return 0; } Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>examples/eg_plds_switched_ctrl.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>examples/eg_plds_switched_ctrl.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_plds_switched_ctrlcpp">examples/eg_plds_switched_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#data_t">data_t</a></li> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="exampleseg_plds_switched_ctrlcpp"> + examples/eg_plds_switched_ctrl.cpp + <a class="anchor" href="#exampleseg_plds_switched_ctrlcpp">#</a> +</h1> +<h2 id="types"> + Types + <a class="anchor" href="#types">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>using double</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-data-t">data_t</a></strong></td> + </tr> + <tr> + <td>using arma::Mat&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-matrix">Matrix</a></strong></td> + </tr> + <tr> + <td>using arma::Col&lt; data_t &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-vector">Vector</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>int</td> + <td><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#function-main">main</a></strong>()</td> + </tr> + </tbody> +</table> +<h2 id="type-details"> + Type Details + <a class="anchor" href="#type-details">#</a> +</h2> +<h3 id="data_t"> + data_t + <a class="anchor" href="#data_t">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">double</span>; +</span></span></code></pre></div><p>Type of all data in library. If need 32b, change <code>double</code> to <code>float</code>. This could be potentially useful for large scale problems where there are memory constraints.</p> +<h3 id="matrix"> + Matrix + <a class="anchor" href="#matrix">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="vector"> + Vector + <a class="anchor" href="#vector">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="main"> + main + <a class="anchor" href="#main">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">int</span> main() +</span></span></code></pre></div><h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">auto</span> <span style="color:#a6e22e">main</span>() <span style="color:#f92672">-&gt;</span> <span style="color:#66d9ef">int</span> { +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ********** Example Switched Poisson LDS Control ********** </span><span style="color:#ae81ff">\n\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// whether to do switched control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> do_switch_ctrl <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Make SISO system sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// no time steps for simulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">30.0</span> <span style="color:#f92672">/</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// for simulating switching +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t which_mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> data_t pr_21 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; <span style="color:#75715e">// prob mode 1 -&gt; 2 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t pr_12 <span style="color:#f92672">=</span> pr_21; <span style="color:#75715e">// prob mode 2 -&gt; 1 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// simulated system being controlled +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System controlled_system(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// **Assume the system is not well characterized by one LDS, but is well +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// characterized by two LDS models with different input matrices.** +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t scale_sys_b <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix a(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.985</span>; +</span></span><span style="display:flex;"><span> Matrix b1 <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(<span style="color:#ae81ff">0.05</span>); +</span></span><span style="display:flex;"><span> Vector d <span style="color:#f92672">=</span> Vector(n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros).fill(log(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> dt)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> controlled_system.set_A(a); +</span></span><span style="display:flex;"><span> controlled_system.set_B(b1); +</span></span><span style="display:flex;"><span> controlled_system.set_d(d); +</span></span><span style="display:flex;"><span> controlled_system.Reset(); <span style="color:#75715e">// reset to initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// reference +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector y_ref0 <span style="color:#f92672">=</span> Vector(n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros).fill(<span style="color:#ae81ff">25.0</span> <span style="color:#f92672">*</span> dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Let underlying system 1 be more sensitive than system 2 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix b2 <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(b1[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">/</span> scale_sys_b); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create switched controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>SwitchedController switched_controller; +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> k_x; <span style="color:#75715e">// feedback controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create switched controller sub-systems +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// system 1 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System sys1(controlled_system); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set process noise covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_controller <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">5e-3</span>; +</span></span><span style="display:flex;"><span> sys1.set_Q(q_controller); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// adaptively estimate process disturbance (m) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// n.b. using arbitrary default value for process noise if enabled. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sys1.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// setting initial mode to target to avoid large error at onset: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_controller <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>log(y_ref0) <span style="color:#f92672">-</span> d; +</span></span><span style="display:flex;"><span> sys1.set_x0(x0_controller); +</span></span><span style="display:flex;"><span> sys1.Reset(); <span style="color:#75715e">// reset to initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;sys1:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> sys1.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// system 2 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System sys2 <span style="color:#f92672">=</span> sys1; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set parameters +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sys2.set_B(b2); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;sys2:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> sys2.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformSystemList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System<span style="color:#f92672">&gt;</span> systems({sys1, sys2}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// controller gains for underlying system s: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix k_x1(n_u, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones); +</span></span><span style="display:flex;"><span> Matrix k_x2 <span style="color:#f92672">=</span> scale_sys_b <span style="color:#f92672">*</span> k_x1; <span style="color:#75715e">// system2 is x-times less sensitive. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> k_x <span style="color:#f92672">=</span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;&gt;</span>({k_x1, k_x2}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t u_lb <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; +</span></span><span style="display:flex;"><span> data_t u_ub <span style="color:#f92672">=</span> <span style="color:#ae81ff">5.0</span>; +</span></span><span style="display:flex;"><span> switched_controller <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move( +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>SwitchedController(std<span style="color:#f92672">::</span>move(systems), u_lb, u_ub)); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Control variables +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#75715e">// no integral action, etc +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> switched_controller.set_control_type(control_type); +</span></span><span style="display:flex;"><span> switched_controller.set_Kc(std<span style="color:#f92672">::</span>move(k_x)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> switched_controller.set_y_ref(y_ref0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System<span style="color:#f92672">&gt;</span> systems_vec(<span style="color:#ae81ff">3</span>, lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System()); +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformSystemList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System<span style="color:#f92672">&gt;</span> systems(std<span style="color:#f92672">::</span>move(systems_vec)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;switched_controller:</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> switched_controller.Print(); +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.....................................</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Fake measurements +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix z(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Will later contain control. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix u(n_u, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create Matrix to save outputs in... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix y_hat(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix y_true(n_y, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix y_ref <span style="color:#f92672">=</span> Matrix(n_y, n_t).fill(y_ref0[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// modes and gain/disturbance params +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix x_hat(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix x_true(n_x, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix mode(<span style="color:#ae81ff">1</span>, n_t, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set initial val +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> y_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> switched_controller.sys().y(); +</span></span><span style="display:flex;"><span> y_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> x_hat.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> switched_controller.sys().x(); +</span></span><span style="display:flex;"><span> x_true.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Starting &#34;</span> <span style="color:#f92672">&lt;&lt;</span> n_t <span style="color:#f92672">*</span> dt <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; sec simulation ... </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> start <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Let the controlled system stochastically change gain +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Assume another algorithm decodes this mode change and signals the +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// switched_controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector chance(<span style="color:#ae81ff">1</span>, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>randu); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (which_mode <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>) <span style="color:#75715e">// mode1 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_21) { +</span></span><span style="display:flex;"><span> which_mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> controlled_system.set_B(b2); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_switch_ctrl) { +</span></span><span style="display:flex;"><span> switched_controller.Switch(<span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { <span style="color:#75715e">// mode2 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_12) { +</span></span><span style="display:flex;"><span> which_mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> controlled_system.set_B(b1); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_switch_ctrl) { +</span></span><span style="display:flex;"><span> switched_controller.Switch(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t) <span style="color:#f92672">=</span> controlled_system.Simulate(u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// perform control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u.col(t) <span style="color:#f92672">=</span> switched_controller.ControlOutputReference(z.col(t)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> mode.col(t) <span style="color:#f92672">=</span> which_mode; +</span></span><span style="display:flex;"><span> y_ref.col(t) <span style="color:#f92672">=</span> y_ref0; +</span></span><span style="display:flex;"><span> y_true.col(t) <span style="color:#f92672">=</span> controlled_system.y(); +</span></span><span style="display:flex;"><span> x_true.col(t) <span style="color:#f92672">=</span> controlled_system.x(); +</span></span><span style="display:flex;"><span> y_hat.col(t) <span style="color:#f92672">=</span> switched_controller.sys().y(); +</span></span><span style="display:flex;"><span> x_hat.col(t) <span style="color:#f92672">=</span> switched_controller.sys().x(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> finish <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>high_resolution_clock<span style="color:#f92672">::</span>now(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>chrono<span style="color:#f92672">::</span>duration<span style="color:#f92672">&lt;</span>data_t, std<span style="color:#f92672">::</span>milli<span style="color:#f92672">&gt;</span> sim_time_ms <span style="color:#f92672">=</span> finish <span style="color:#f92672">-</span> start; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Finished simulation in &#34;</span> <span style="color:#f92672">&lt;&lt;</span> sim_time_ms.count() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ms.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;(app. &#34;</span> <span style="color:#f92672">&lt;&lt;</span> (sim_time_ms.count() <span style="color:#f92672">/</span> n_t) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e3</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; us/time-step)</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_true, m_true saving with hdf5 via armadillo +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>opts replace <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>hdf5_opts<span style="color:#f92672">::</span>replace; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> dt_vec <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">1</span>).fill(dt); +</span></span><span style="display:flex;"><span> dt_vec.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;dt&#34;</span>)); +</span></span><span style="display:flex;"><span> y_ref.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_ref&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> u.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;u&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> z.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;z&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_true.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_true&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> x_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;x_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> y_hat.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;y_hat&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> mode.save(arma<span style="color:#f92672">::</span>hdf5_name(<span style="color:#e6db74">&#34;eg_plds_switched_ctrl.h5&#34;</span>, <span style="color:#e6db74">&#34;mode&#34;</span>, replace)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span>} +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#exampleseg_plds_switched_ctrlcpp">examples/eg_plds_switched_ctrl.cpp</a> + <ul> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#data_t">data_t</a></li> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#vector">Vector</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#main">main</a></li> + </ul> + </li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/index.html b/docs/docs/api/files/index.html new file mode 100644 index 00000000..836b996f --- /dev/null +++ b/docs/docs/api/files/index.html @@ -0,0 +1,438 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Files + # + + + +examples/eg_glds_ctrl.cpp + + +examples/eg_glds_du_plds_ctrl.cpp + + +examples/eg_plds_ctrl.cpp + + +examples/eg_plds_est.cpp + + +examples/eg_plds_switched_ctrl.cpp + + +ldsCtrlEst_h/lds.h lds namespace + + +ldsCtrlEst_h/lds_ctrl.h Controller. + + +ldsCtrlEst_h/lds_fit.h LDS base fit type. + + +ldsCtrlEst_h/lds_fit_em.h subspace identification + + +ldsCtrlEst_h/lds_fit_ssid.h subspace identification + + +ldsCtrlEst_h/lds_gaussian.h glds namespace + + +ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. + + +ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. + + +ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. + + +ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. + + +ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. + + +ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. + + +ldsCtrlEst_h/lds_poisson.h plds namespace + + +ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. + + +ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. + + +ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Files"> + <meta property="og:description" content="Files # examples/eg_glds_ctrl.cpp +examples/eg_glds_du_plds_ctrl.cpp +examples/eg_plds_ctrl.cpp +examples/eg_plds_est.cpp +examples/eg_plds_switched_ctrl.cpp +ldsCtrlEst_h/lds.h lds namespace +ldsCtrlEst_h/lds_ctrl.h Controller. +ldsCtrlEst_h/lds_fit.h LDS base fit type. +ldsCtrlEst_h/lds_fit_em.h subspace identification +ldsCtrlEst_h/lds_fit_ssid.h subspace identification +ldsCtrlEst_h/lds_gaussian.h glds namespace +ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. +ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. +ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. +ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. +ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. +ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. +ldsCtrlEst_h/lds_poisson.h plds namespace +ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. +ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. +ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Files | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/"class=active>Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Files</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#files">Files</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="files"> + Files + <a class="anchor" href="#files">#</a> +</h1> +<ul> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp">examples/eg_glds_ctrl.cpp</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp">examples/eg_glds_du_plds_ctrl.cpp</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp">examples/eg_plds_ctrl.cpp</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp">examples/eg_plds_est.cpp</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp">examples/eg_plds_switched_ctrl.cpp</a></strong></p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h">ldsCtrlEst_h/lds.h</a></strong> <br><code>lds</code> namespace</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h">ldsCtrlEst_h/lds_ctrl.h</a></strong> <br>Controller.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h">ldsCtrlEst_h/lds_fit.h</a></strong> <br>LDS base fit type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h">ldsCtrlEst_h/lds_fit_em.h</a></strong> <br>subspace identification</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h">ldsCtrlEst_h/lds_fit_ssid.h</a></strong> <br>subspace identification</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h">ldsCtrlEst_h/lds_gaussian.h</a></strong> <br><code>glds</code> namespace</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h">ldsCtrlEst_h/lds_gaussian_ctrl.h</a></strong> <br>GLDS Controller.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h">ldsCtrlEst_h/lds_gaussian_fit.h</a></strong> <br>GLDS fit type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h">ldsCtrlEst_h/lds_gaussian_fit_em.h</a></strong> <br>GLDS E-M fit type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h">ldsCtrlEst_h/lds_gaussian_fit_ssid.h</a></strong> <br>GLDS SSID fit type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h">ldsCtrlEst_h/lds_gaussian_sctrl.h</a></strong> <br>GLDS switched controller type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h">ldsCtrlEst_h/lds_gaussian_sys.h</a></strong> <br>GLDS base type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h">ldsCtrlEst_h/lds_poisson.h</a></strong> <br><code>plds</code> namespace</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h">ldsCtrlEst_h/lds_poisson_ctrl.h</a></strong> <br>PLDS controller type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h">ldsCtrlEst_h/lds_poisson_fit.h</a></strong> <br>PLDS base fit type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h">ldsCtrlEst_h/lds_poisson_fit_em.h</a></strong> <br>PLDS E-M fit type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h">ldsCtrlEst_h/lds_poisson_fit_ssid.h</a></strong> <br>PLDS SSID fit type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h">ldsCtrlEst_h/lds_poisson_sctrl.h</a></strong> <br>PLDS switched controller type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h">ldsCtrlEst_h/lds_poisson_sys.h</a></strong> <br>PLDS base type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h">ldsCtrlEst_h/lds_sctrl.h</a></strong> <br>SwitchedController type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h">ldsCtrlEst_h/lds_sys.h</a></strong> <br>LDS base type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h">ldsCtrlEst_h/lds_uniform_mats.h</a></strong> <br>List of uniformly sized matrices.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h">ldsCtrlEst_h/lds_uniform_systems.h</a></strong> <br>List of uniformly sized Systems.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h">ldsCtrlEst_h/lds_uniform_vecs.h</a></strong> <br>List of uniformly sized vectors.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h">ldsCtrlEst_h/mex_c_util.h</a></strong> <br>arma &lt;-&gt; mex interoperability utilities (Matlab C API)</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h">ldsCtrlEst_h/mex_cpp_util.h</a></strong> <br>arma &lt;-&gt; mex interoperability utilities (Matlab C++ API)</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp">src/lds.cpp</a></strong> <br>misc lds namespace functions</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp">src/lds_gaussian_sys.cpp</a></strong> <br>GLDS base type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp">src/lds_poisson_sys.cpp</a></strong> <br>PLDS base type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp">src/lds_sys.cpp</a></strong> <br>LDS base type.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp">src/lds_uniform_vecs.cpp</a></strong> <br>Uniformly sized vectors.</p> +</li> +</ul> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#files">Files</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/index.xml b/docs/docs/api/files/index.xml new file mode 100644 index 00000000..cfd1b2a1 --- /dev/null +++ b/docs/docs/api/files/index.xml @@ -0,0 +1,291 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Files on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/</link> + <description>Recent content in Files on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>examples</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</guid> + <description>&lt;h1 id=&#34;examples&#34;&gt;&#xA; examples&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#examples&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;files&#34;&gt;&#xA; Files&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#files&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp&#34;&gt;examples/eg_glds_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp&#34;&gt;examples/eg_glds_du_plds_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp&#34;&gt;examples/eg_plds_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp&#34;&gt;examples/eg_plds_est.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp&#34;&gt;examples/eg_plds_switched_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_glds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_glds_ctrlcpp&#34;&gt;&#xA; examples/eg_glds_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_glds_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_glds_du_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_glds_du_plds_ctrlcpp&#34;&gt;&#xA; examples/eg_glds_du_plds_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_glds_du_plds_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;function-details&#34;&gt;&#xA; Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;main&#34;&gt;&#xA; main&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#main&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; main()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Going to simulate a switching disturbance (m) acting on system&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_plds_ctrlcpp&#34;&gt;&#xA; examples/eg_plds_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_plds_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_plds_est.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_plds_estcpp&#34;&gt;&#xA; examples/eg_plds_est.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_plds_estcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-random-walk&#34;&gt;random_walk&lt;/a&gt;&lt;/strong&gt;(size_t n_t, const Matrix &amp;amp; Q, const Vector &amp;amp; x0)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_plds_switched_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_plds_switched_ctrlcpp&#34;&gt;&#xA; examples/eg_plds_switched_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_plds_switched_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;function-details&#34;&gt;&#xA; Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;main&#34;&gt;&#xA; main&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#main&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; main()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;source-code&#34;&gt;&#xA; Source code&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#source-code&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Copyright 2021 Michael Bolus&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Copyright 2021 Georgia Institute of Technology&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// you may not use this file except in compliance with the License.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// You may obtain a copy of the License at&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// http://www.apache.org/licenses/LICENSE-2.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Unless required by applicable law or agreed to in writing, software&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// See the License for the specific language governing permissions and&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// limitations under the License.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//===----------------------------------------------------------------------===//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//===----------------------------------------------------------------------===//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;ldsCtrlEst&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; ********** Example Switched Poisson LDS Control ********** &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// whether to do switched control&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; do_switch_ctrl &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Make SISO system sampled at 1kHz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; data_t dt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-3&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_u &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_y &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// no time steps for simulation.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; n_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static_cast&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;size_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;30.0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// for simulating switching&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; size_t which_mode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t pr_21 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-3&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// prob mode 1 -&amp;gt; 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; data_t pr_12 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pr_21; &lt;span style=&#34;color:#75715e&#34;&gt;// prob mode 2 -&amp;gt; 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// simulated system being controlled&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System controlled_system(n_u, n_x, n_y, dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// **Assume the system is not well characterized by one LDS, but is well&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// characterized by two LDS models with different input matrices.**&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; data_t scale_sys_b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix a(n_x, n_x, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;eye);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; a[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.985&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix b1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_x, n_u).fill(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.05&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Vector d &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Vector(n_y, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros).fill(log(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; dt));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_A(a);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_B(b1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_d(d);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.Reset(); &lt;span style=&#34;color:#75715e&#34;&gt;// reset to initial conditions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// reference&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Vector y_ref0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Vector(n_y, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros).fill(&lt;span style=&#34;color:#ae81ff&#34;&gt;25.0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Let underlying system 1 be more sensitive than system 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix b2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_x, n_u).fill(b1[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; scale_sys_b);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// create switched controller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;SwitchedController switched_controller;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; k_x; &lt;span style=&#34;color:#75715e&#34;&gt;// feedback controller gains&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// create switched controller sub-systems&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// system 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System sys1(controlled_system);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// set process noise covariance&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix q_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_x, n_x, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;eye) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5e-3&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.set_Q(q_controller);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// adaptively estimate process disturbance (m)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// n.b. using arbitrary default value for process noise if enabled.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; sys1.do_adapt_m &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// setting initial mode to target to avoid large error at onset:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Vector x0_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;log(y_ref0) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; d;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.set_x0(x0_controller);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.Reset(); &lt;span style=&#34;color:#75715e&#34;&gt;// reset to initial conditions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sys1:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.Print();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// system 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System sys2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys1;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// set parameters&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; sys2.set_B(b2);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sys2:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys2.Print();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformSystemList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; systems({sys1, sys2});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// controller gains for underlying system s:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix k_x1(n_u, n_x, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;ones);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix k_x2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scale_sys_b &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; k_x1; &lt;span style=&#34;color:#75715e&#34;&gt;// system2 is x-times less sensitive.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; k_x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;({k_x1, k_x2});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_lb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_ub &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5.0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;SwitchedController(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(systems), u_lb, u_ub));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Control variables&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; size_t control_type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// no integral action, etc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; switched_controller.set_control_type(control_type);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.set_Kc(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(k_x));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.set_y_ref(y_ref0);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; systems_vec(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System());&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformSystemList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; systems(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(systems_vec));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;switched_controller:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.Print();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Fake measurements&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix z(n_y, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Will later contain control.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix u(n_u, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// create Matrix to save outputs in...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix y_hat(n_y, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix y_true(n_y, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix y_ref &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_y, n_t).fill(y_ref0[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// modes and gain/disturbance params&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix x_hat(n_x, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix x_true(n_x, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix mode(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;ones);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// set initial val&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; y_hat.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_true.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_hat.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_true.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Starting &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; n_t &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; dt &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; sec simulation ... &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;chrono&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;high_resolution_clock&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;now();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (size_t t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;; t &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; n_t; t&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Let the controlled system stochastically change gain&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Assume another algorithm decodes this mode change and signals the&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// switched_controller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Vector chance(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;randu);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (which_mode &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;// mode1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (chance[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; pr_21) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; which_mode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_B(b2);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (do_switch_ctrl) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.Switch(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; { &lt;span style=&#34;color:#75715e&#34;&gt;// mode2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (chance[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; pr_12) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; which_mode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_B(b1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (do_switch_ctrl) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.Switch(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Simulate the true system.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; z.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.Simulate(u.col(t &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// perform control&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; u.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.ControlOutputReference(z.col(t));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; mode.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; which_mode;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_ref.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; y_ref0;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_true.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_true.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_hat.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_hat.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; finish &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;chrono&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;high_resolution_clock&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;now();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;chrono&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;duration&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t, std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;milli&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; sim_time_ms &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; finish &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; start;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Finished simulation in &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; sim_time_ms.count() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; ms.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(app. &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; (sim_time_ms.count() &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; n_t) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; us/time-step)&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// x_true, m_true saving with hdf5 via armadillo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_opts&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;opts replace &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_opts&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;replace;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; dt_vec &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Vector(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;).fill(dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; dt_vec.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dt&amp;#34;&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_ref.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;y_ref&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; u.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;u&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; z.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;z&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_true.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x_true&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_true.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;y_true&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_hat.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x_hat&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_hat.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;y_hat&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; mode.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>include</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</guid> + <description>&lt;h1 id=&#34;include&#34;&gt;&#xA; include&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#include&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;directories&#34;&gt;&#xA; Directories&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#directories&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/#dir-ldsctrlest-h&#34;&gt;ldsCtrlEst_h&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>ldsCtrlEst_h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</guid> + <description>&lt;h1 id=&#34;ldsctrlest_h&#34;&gt;&#xA; ldsCtrlEst_h&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsctrlest_h&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;files&#34;&gt;&#xA; Files&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#files&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h&#34;&gt;ldsCtrlEst_h/lds.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;&lt;code&gt;lds&lt;/code&gt; namespace&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h&#34;&gt;ldsCtrlEst_h/lds_ctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;Controller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h&#34;&gt;ldsCtrlEst_h/lds_fit.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;LDS base fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h&#34;&gt;ldsCtrlEst_h/lds_fit_em.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;subspace identification&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h&#34;&gt;ldsCtrlEst_h/lds_fit_ssid.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;subspace identification&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h&#34;&gt;ldsCtrlEst_h/lds_gaussian.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;&lt;code&gt;glds&lt;/code&gt; namespace&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_ctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS Controller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_fit.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_fit_em.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS E-M fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_fit_ssid.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS SSID fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_sctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS switched controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_sys.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h&#34;&gt;ldsCtrlEst_h/lds_poisson.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;&lt;code&gt;plds&lt;/code&gt; namespace&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h&#34;&gt;ldsCtrlEst_h/lds_poisson_ctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h&#34;&gt;ldsCtrlEst_h/lds_poisson_fit.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS base fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h&#34;&gt;ldsCtrlEst_h/lds_poisson_fit_em.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS E-M fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h&#34;&gt;ldsCtrlEst_h/lds_poisson_fit_ssid.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS SSID fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h&#34;&gt;ldsCtrlEst_h/lds_poisson_sctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS switched controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h&#34;&gt;ldsCtrlEst_h/lds_poisson_sys.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h&#34;&gt;ldsCtrlEst_h/lds_sctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;SwitchedController type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h&#34;&gt;ldsCtrlEst_h/lds_sys.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;LDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h&#34;&gt;ldsCtrlEst_h/lds_uniform_mats.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;List of uniformly sized matrices.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h&#34;&gt;ldsCtrlEst_h/lds_uniform_systems.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;List of uniformly sized Systems.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h&#34;&gt;ldsCtrlEst_h/lds_uniform_vecs.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;List of uniformly sized vectors.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h&#34;&gt;ldsCtrlEst_h/mex_c_util.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C API)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h&#34;&gt;ldsCtrlEst_h/mex_cpp_util.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C++ API)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_ctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</guid> + <description>Controller.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_fit_em.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</guid> + <description>subspace identification</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_fit_ssid.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</guid> + <description>subspace identification</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_fit.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</guid> + <description>LDS base fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_ctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</guid> + <description>GLDS Controller.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_fit_em.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</guid> + <description>GLDS E-M fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_fit_ssid.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</guid> + <description>GLDS SSID fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_fit.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</guid> + <description>GLDS fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_sctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</guid> + <description>GLDS switched controller type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_sys.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</guid> + <description>GLDS base type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</guid> + <description>glds namespace</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_ctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</guid> + <description>PLDS controller type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_fit_em.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</guid> + <description>PLDS E-M fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_fit_ssid.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</guid> + <description>PLDS SSID fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_fit.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</guid> + <description>PLDS base fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_sctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</guid> + <description>PLDS switched controller type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_sys.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</guid> + <description>PLDS base type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</guid> + <description>plds namespace</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_sctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</guid> + <description>SwitchedController type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_sys.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</guid> + <description>LDS base type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_uniform_mats.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</guid> + <description>List of uniformly sized matrices.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_uniform_systems.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</guid> + <description>List of uniformly sized Systems.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_uniform_vecs.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</guid> + <description>List of uniformly sized vectors.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</guid> + <description>lds namespace</description> + </item> + <item> + <title>ldsCtrlEst_h/mex_c_util.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</guid> + <description>arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C API)</description> + </item> + <item> + <title>ldsCtrlEst_h/mex_cpp_util.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</guid> + <description>arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C++ API)</description> + </item> + <item> + <title>src</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</guid> + <description>&lt;h1 id=&#34;src&#34;&gt;&#xA; src&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#src&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;files&#34;&gt;&#xA; Files&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#files&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp&#34;&gt;src/lds.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;misc lds namespace functions&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp&#34;&gt;src/lds_gaussian_sys.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp&#34;&gt;src/lds_poisson_sys.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp&#34;&gt;src/lds_sys.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;LDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp&#34;&gt;src/lds_uniform_vecs.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;Uniformly sized vectors.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>src/lds_gaussian_sys.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</guid> + <description>GLDS base type.</description> + </item> + <item> + <title>src/lds_poisson_sys.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</guid> + <description>PLDS base type.</description> + </item> + <item> + <title>src/lds_sys.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</guid> + <description>LDS base type.</description> + </item> + <item> + <title>src/lds_uniform_vecs.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</guid> + <description>Uniformly sized vectors.</description> + </item> + <item> + <title>src/lds.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</guid> + <description>misc lds namespace functions</description> + </item> + </channel> +</rss> diff --git a/docs/docs/api/files/lds_8cpp/index.html b/docs/docs/api/files/lds_8cpp/index.html new file mode 100644 index 00000000..03b9e008 --- /dev/null +++ b/docs/docs/api/files/lds_8cpp/index.html @@ -0,0 +1,405 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="misc lds namespace functions"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="src/lds.cpp"> + <meta property="og:description" content="misc lds namespace functions"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>src/lds.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>src/lds.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srcldscpp">src/lds.cpp</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="srcldscpp"> + src/lds.cpp + <a class="anchor" href="#srcldscpp">#</a> +</h1> +<p>misc lds namespace functions <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file implements miscellaneous lds namespace functions not bound to a class.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- lds.cpp - LDS -----------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst_h/lds.h&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// insert any necessary function definitions here. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">ForceSymPD</span>(Matrix<span style="color:#f92672">&amp;</span> X) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (X.is_sympd() <span style="color:#f92672">||</span> <span style="color:#f92672">!</span>X.is_square()) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make symmetric +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> X <span style="color:#f92672">=</span> (X <span style="color:#f92672">+</span> X.t()) <span style="color:#f92672">/</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// for eigenval decomp +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> did_succeed(true); +</span></span><span style="display:flex;"><span> Vector d; +</span></span><span style="display:flex;"><span> Matrix u; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// see first method (which may not be ideal): +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t k(<span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> is_sympd <span style="color:#f92672">=</span> X.is_sympd(); +</span></span><span style="display:flex;"><span> Matrix id <span style="color:#f92672">=</span> Matrix(X.n_rows, X.n_cols, fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">while</span> (<span style="color:#f92672">!</span>is_sympd) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (k <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">100</span>) { +</span></span><span style="display:flex;"><span> did_succeed <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>eig_sym(d, u, X, <span style="color:#e6db74">&#34;std&#34;</span>); +</span></span><span style="display:flex;"><span> data_t min_eig <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>min(d); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;After multiple iterations, min eigen val = &#34;</span> <span style="color:#f92672">&lt;&lt;</span> min_eig +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Failed to make matrix symmetric positive definite.&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Limit(d, arma::eps(0), kInf); // force to be positive... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Matrix d_diag = arma::diagmat(d); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// X = u * d_diag * u.t(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> did_succeed <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>eig_sym(d, u, X, <span style="color:#e6db74">&#34;std&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>did_succeed) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(<span style="color:#e6db74">&#34;ForceSymPD failed.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t min_eig <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>min(d); +</span></span><span style="display:flex;"><span> X <span style="color:#f92672">+=</span> id <span style="color:#f92672">*</span> abs(min_eig) <span style="color:#f92672">+</span> arma<span style="color:#f92672">::</span>datum<span style="color:#f92672">::</span>eps; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure symm: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> X <span style="color:#f92672">=</span> (X <span style="color:#f92672">+</span> X.t()) <span style="color:#f92672">/</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// double check eigenvals positive after symmetrizing: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>eig_sym(d, u, X, <span style="color:#e6db74">&#34;std&#34;</span>); +</span></span><span style="display:flex;"><span> min_eig <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>min(d); +</span></span><span style="display:flex;"><span> is_sympd <span style="color:#f92672">=</span> min_eig <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> k<span style="color:#f92672">++</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">ForceSymMinEig</span>(Matrix<span style="color:#f92672">&amp;</span> X, data_t eig_min) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>X.is_square()) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make symmetric +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> X <span style="color:#f92672">=</span> (X <span style="color:#f92672">+</span> X.t()) <span style="color:#f92672">/</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> did_succeed(true); +</span></span><span style="display:flex;"><span> Vector d; +</span></span><span style="display:flex;"><span> Matrix u; +</span></span><span style="display:flex;"><span> did_succeed <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>eig_sym(d, u, X, <span style="color:#e6db74">&#34;std&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>did_succeed) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(<span style="color:#e6db74">&#34;ForceSymMinEig failed.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> Limit(d, eig_min <span style="color:#f92672">+</span> arma<span style="color:#f92672">::</span>eps(eig_min), kInf); <span style="color:#75715e">// enforce lower bound +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix d_diag <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>diagmat(d); +</span></span><span style="display:flex;"><span> X <span style="color:#f92672">=</span> u <span style="color:#f92672">*</span> d_diag <span style="color:#f92672">*</span> u.t(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// double check symmetric +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> X <span style="color:#f92672">=</span> (X <span style="color:#f92672">+</span> X.t()) <span style="color:#f92672">/</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">lq</span>(Matrix<span style="color:#f92672">&amp;</span> L, Matrix<span style="color:#f92672">&amp;</span> Qt, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> X) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> did_succeed(true); +</span></span><span style="display:flex;"><span> did_succeed <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>qr_econ(Qt, L, X.t()); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>did_succeed) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(<span style="color:#e6db74">&#34;LQ decomposition failed.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> arma<span style="color:#f92672">::</span>inplace_trans(L); +</span></span><span style="display:flex;"><span> arma<span style="color:#f92672">::</span>inplace_trans(Qt); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>Matrix <span style="color:#a6e22e">calcCov</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> A, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> B) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// subtract out mean +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> m_a <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>mean(A, <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> Matrix a0 <span style="color:#f92672">=</span> A; +</span></span><span style="display:flex;"><span> a0.each_col() <span style="color:#f92672">-=</span> m_a; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> m_b <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>mean(B, <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> Matrix b0 <span style="color:#f92672">=</span> B; +</span></span><span style="display:flex;"><span> b0.each_col() <span style="color:#f92672">-=</span> m_b; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix cov <span style="color:#f92672">=</span> a0 <span style="color:#f92672">*</span> b0.t() <span style="color:#f92672">/</span> a0.n_cols; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> cov; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srcldscpp">src/lds.cpp</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds_8h/index.html b/docs/docs/api/files/lds_8h/index.html new file mode 100644 index 00000000..b4d28195 --- /dev/null +++ b/docs/docs/api/files/lds_8h/index.html @@ -0,0 +1,427 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="lds namespace"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds.h"> + <meta property="og:description" content="lds namespace"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hldsh">ldsCtrlEst_h/lds.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hldsh"> + ldsCtrlEst_h/lds.h + <a class="anchor" href="#ldsctrlest_hldsh">#</a> +</h1> +<p><code>lds</code> namespace <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file defines the <code>lds</code> namespace, which will be an umbrella for linear dynamical systems with Gaussian (<code>[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)</code>) or Poisson (<code>[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)</code>) observations.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// #ifndef LDSCTRLEST +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// #include &lt;ldsCtrlEst&gt; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// #endif +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;armadillo&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> data_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">double</span>; <span style="color:#75715e">// may change to float (but breaks mex functions) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">using</span> Vector <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> Matrix <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> Cube <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Cube<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> View <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>subview<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> fill <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>fill; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>size_t kControlTypeDeltaU <span style="color:#f92672">=</span> <span style="color:#ae81ff">0x1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>size_t kControlTypeIntY <span style="color:#f92672">=</span> kControlTypeDeltaU <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>size_t kControlTypeAdaptM <span style="color:#f92672">=</span> kControlTypeDeltaU <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kInf <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>numeric_limits<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;::</span>infinity(); +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kPi <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>datum<span style="color:#f92672">::</span>pi; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kDefaultP0 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-6</span>; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kDefaultQ0 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-6</span>; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kDefaultR0 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">enum</span> <span style="color:#a6e22e">SSIDWt</span> { +</span></span><span style="display:flex;"><span> kSSIDNone, +</span></span><span style="display:flex;"><span> kSSIDMOESP, +</span></span><span style="display:flex;"><span> kSSIDCVA +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">enum</span> <span style="color:#a6e22e">MatrixListFreeDim</span> { +</span></span><span style="display:flex;"><span> kMatFreeDimNone, +</span></span><span style="display:flex;"><span> kMatFreeDim1, +</span></span><span style="display:flex;"><span> kMatFreeDim2 +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// TODO(mfbolus): for SwitchedController, may want systems to have differing +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// numbers of states. Use this enum as template parameter? +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// enum SystemListFreeDim { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// kSysFreeDimNone, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// kSysFreeDimX ///&lt; allow state dim (x) of systems in list to be hetero +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// }; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// place hard limits on contents of vecors/mats +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Limit</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;&amp;</span> x, data_t lb, data_t ub); +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Limit</span>(Vector<span style="color:#f92672">&amp;</span> x, data_t lb, data_t ub); +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Limit</span>(Matrix<span style="color:#f92672">&amp;</span> x, data_t lb, data_t ub); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// in-place assign that errs if there are dimension mismatches: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Reassign</span>(Vector<span style="color:#f92672">&amp;</span> some, <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> other, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>string<span style="color:#f92672">&amp;</span> parenthetical <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Reassign&#34;</span>); +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Reassign</span>(Matrix<span style="color:#f92672">&amp;</span> some, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> other, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>string<span style="color:#f92672">&amp;</span> parenthetical <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;Reassign&#34;</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// TODO(mfbolus): this is a fudge, but for some reason, cov mats often going +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// numerically asymm. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">ForceSymPD</span>(Matrix<span style="color:#f92672">&amp;</span> X); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">ForceSymMinEig</span>(Matrix<span style="color:#f92672">&amp;</span> X, data_t eig_min <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> <span style="color:#a6e22e">lq</span>(Matrix<span style="color:#f92672">&amp;</span> L, Matrix<span style="color:#f92672">&amp;</span> Qt, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> X); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>Matrix <span style="color:#a6e22e">calcCov</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> A, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> B); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Limit</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;&amp;</span> x, data_t lb, data_t ub) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (data_t<span style="color:#f92672">&amp;</span> el : x) { +</span></span><span style="display:flex;"><span> el <span style="color:#f92672">=</span> el <span style="color:#f92672">&lt;</span> lb <span style="color:#f92672">?</span> lb : el; +</span></span><span style="display:flex;"><span> el <span style="color:#f92672">=</span> el <span style="color:#f92672">&gt;</span> ub <span style="color:#f92672">?</span> ub : el; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Limit</span>(Vector<span style="color:#f92672">&amp;</span> x, data_t lb, data_t ub) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (data_t<span style="color:#f92672">&amp;</span> el : x) { +</span></span><span style="display:flex;"><span> el <span style="color:#f92672">=</span> el <span style="color:#f92672">&lt;</span> lb <span style="color:#f92672">?</span> lb : el; +</span></span><span style="display:flex;"><span> el <span style="color:#f92672">=</span> el <span style="color:#f92672">&gt;</span> ub <span style="color:#f92672">?</span> ub : el; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Limit</span>(Matrix<span style="color:#f92672">&amp;</span> x, data_t lb, data_t ub) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (data_t<span style="color:#f92672">&amp;</span> el : x) { +</span></span><span style="display:flex;"><span> el <span style="color:#f92672">=</span> el <span style="color:#f92672">&lt;</span> lb <span style="color:#f92672">?</span> lb : el; +</span></span><span style="display:flex;"><span> el <span style="color:#f92672">=</span> el <span style="color:#f92672">&gt;</span> ub <span style="color:#f92672">?</span> ub : el; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Reassign</span>(Vector<span style="color:#f92672">&amp;</span> some, <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> other, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>string<span style="color:#f92672">&amp;</span> parenthetical) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (other.n_elem <span style="color:#f92672">!=</span> some.n_elem) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign vector of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> some.n_elem +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; with vector of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> other.n_elem <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;(&#34;</span> <span style="color:#f92672">&lt;&lt;</span> parenthetical +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;)&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> some.n_elem; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> some[k] <span style="color:#f92672">=</span> other[k]; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Reassign</span>(Matrix<span style="color:#f92672">&amp;</span> some, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> other, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>string<span style="color:#f92672">&amp;</span> parenthetical) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> ((other.n_rows <span style="color:#f92672">!=</span> some.n_rows) <span style="color:#f92672">||</span> (other.n_cols <span style="color:#f92672">!=</span> some.n_cols)) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign matrix of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> some.n_rows <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> <span style="color:#f92672">&lt;&lt;</span> some.n_cols +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; with matrix of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> other.n_rows <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> <span style="color:#f92672">&lt;&lt;</span> other.n_cols <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;(&#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> parenthetical <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;)&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> some.n_elem; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> some[k] <span style="color:#f92672">=</span> other[k]; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hldsh">ldsCtrlEst_h/lds.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__ctrl_8h/index.html b/docs/docs/api/files/lds__ctrl_8h/index.html new file mode 100644 index 00000000..587fc28b --- /dev/null +++ b/docs/docs/api/files/lds__ctrl_8h/index.html @@ -0,0 +1,808 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Controller."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_ctrl.h"> + <meta property="og:description" content="Controller."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_ctrl.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_ctrl.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_ctrlh">ldsCtrlEst_h/lds_ctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_ctrlh"> + ldsCtrlEst_h/lds_ctrl.h + <a class="anchor" href="#ldsctrlest_hlds_ctrlh">#</a> +</h1> +<p>Controller. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares the type for control of a linear dynamical system (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller</a>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// system type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_sys.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Controller</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">static_assert</span>(std<span style="color:#f92672">::</span>is_base_of<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>System, System<span style="color:#f92672">&gt;::</span>value, +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;System must be derived from lds::System type.&#34;</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> Controller() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Controller(<span style="color:#66d9ef">const</span> System<span style="color:#f92672">&amp;</span> sys, data_t u_lb, data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Controller(System<span style="color:#f92672">&amp;&amp;</span> sys, data_t u_lb, data_t u_ub, size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> Control(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> z, <span style="color:#66d9ef">bool</span> do_control <span style="color:#f92672">=</span> true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_lock_control <span style="color:#f92672">=</span> false, +</span></span><span style="display:flex;"><span> data_t sigma_soft_start <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>, data_t sigma_u_noise <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset <span style="color:#f92672">=</span> true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> ControlOutputReference(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> z, <span style="color:#66d9ef">bool</span> do_control <span style="color:#f92672">=</span> true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_estimation <span style="color:#f92672">=</span> true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_lock_control <span style="color:#f92672">=</span> false, +</span></span><span style="display:flex;"><span> data_t sigma_soft_start <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> data_t sigma_u_noise <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset <span style="color:#f92672">=</span> true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// get methods: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> System<span style="color:#f92672">&amp;</span> sys() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> sys_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Kc() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> Kc_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Kc_inty() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> Kc_inty_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Kc_u() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> Kc_u_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> g_design() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> g_design_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_ref() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> u_ref_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x_ref() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> x_ref_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> y_ref() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> y_ref_; }; +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">control_type</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> control_type_; }; +</span></span><span style="display:flex;"><span> data_t <span style="color:#a6e22e">tau_awu</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> tau_awu_; }; +</span></span><span style="display:flex;"><span> data_t <span style="color:#a6e22e">u_lb</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> u_lb_; }; +</span></span><span style="display:flex;"><span> data_t <span style="color:#a6e22e">u_ub</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> u_ub_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set methods +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_sys</span>(<span style="color:#66d9ef">const</span> System<span style="color:#f92672">&amp;</span> sys) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> does_match <span style="color:#f92672">=</span> sys_.n_u() <span style="color:#f92672">==</span> sys.n_u(); +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (sys_.n_x() <span style="color:#f92672">==</span> sys.n_x()); +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (sys_.n_y() <span style="color:#f92672">==</span> sys.n_y()); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (does_match) { +</span></span><span style="display:flex;"><span> sys_ <span style="color:#f92672">=</span> sys; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;new system argument to `set_sys` does not match dimensionality of &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;existing system&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_g_design</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> g_design) { Reassign(g_design_, g_design); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_u_ref</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_ref) { Reassign(u_ref_, u_ref); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_x_ref</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x_ref) { +</span></span><span style="display:flex;"><span> Reassign(x_ref_, x_ref); +</span></span><span style="display:flex;"><span> cx_ref_ <span style="color:#f92672">=</span> sys_.C() <span style="color:#f92672">*</span> x_ref_; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// y_ref needs to be handled differently depending on output fn. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (need to populate cx_ref_ too, which depends on output fn) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_y_ref</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> y_ref) { Reassign(y_ref_, y_ref); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Kc) { Reassign(Kc_, Kc); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc_inty</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc_u</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Kc_u) { Reassign(Kc_u_, Kc_u); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_tau_awu</span>(data_t tau) { +</span></span><span style="display:flex;"><span> tau_awu_ <span style="color:#f92672">=</span> tau; +</span></span><span style="display:flex;"><span> k_awu_ <span style="color:#f92672">=</span> sys_.dt() <span style="color:#f92672">/</span> tau_awu_; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_control_type</span>(size_t control_type); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// There is no reason u_lb/ub should not be public, but making set methods +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// anyway. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_u_lb</span>(data_t u_lb) { u_lb_ <span style="color:#f92672">=</span> u_lb; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_u_ub</span>(data_t u_ub) { u_ub_ <span style="color:#f92672">=</span> u_ub; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Reset</span>() { +</span></span><span style="display:flex;"><span> sys_.Reset(); +</span></span><span style="display:flex;"><span> u_ref_.zeros(); +</span></span><span style="display:flex;"><span> u_ref_prev_.zeros(); +</span></span><span style="display:flex;"><span> int_e_.zeros(); +</span></span><span style="display:flex;"><span> int_e_awu_adjust_.zeros(); +</span></span><span style="display:flex;"><span> u_sat_.zeros(); +</span></span><span style="display:flex;"><span> u_saturated_ <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> t_since_control_onset_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Print</span>() { +</span></span><span style="display:flex;"><span> sys_.Print(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;g_design : &#34;</span> <span style="color:#f92672">&lt;&lt;</span> g_design_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;u_lb : &#34;</span> <span style="color:#f92672">&lt;&lt;</span> u_lb_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;u_ub : &#34;</span> <span style="color:#f92672">&lt;&lt;</span> u_ub_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> System sys_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector u_; +</span></span><span style="display:flex;"><span> Vector u_return_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector g_design_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// reference signals +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector u_ref_; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create no set method for this: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector u_ref_prev_; +</span></span><span style="display:flex;"><span> Vector x_ref_; +</span></span><span style="display:flex;"><span> Vector y_ref_; +</span></span><span style="display:flex;"><span> Vector cx_ref_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix Kc_; +</span></span><span style="display:flex;"><span> Matrix +</span></span><span style="display:flex;"><span> Kc_u_; +</span></span><span style="display:flex;"><span> Matrix Kc_inty_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// control after g inversion +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// do not need set methods for these. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector du_ref_; +</span></span><span style="display:flex;"><span> Vector dv_ref_; +</span></span><span style="display:flex;"><span> Vector v_ref_; +</span></span><span style="display:flex;"><span> Vector dv_; +</span></span><span style="display:flex;"><span> Vector v_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// integral error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// do not need set method for this +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector int_e_; +</span></span><span style="display:flex;"><span> Vector int_e_awu_adjust_; +</span></span><span style="display:flex;"><span> Vector u_sat_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_control_prev_ <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_lock_control_prev_ <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// whether the g of system has become inverted from what you think it is +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (gain_ref) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> u_saturated_ <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> false; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// should be safe to have references here bc nothing needs to be done +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (like reset vars) when it changes... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t u_lb_{}; +</span></span><span style="display:flex;"><span> data_t u_ub_{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t tau_awu_{}; +</span></span><span style="display:flex;"><span> data_t k_awu_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t t_since_control_onset_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> size_t control_type_{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> CalcControl(<span style="color:#66d9ef">bool</span> do_control <span style="color:#f92672">=</span> true, <span style="color:#66d9ef">bool</span> do_estimation <span style="color:#f92672">=</span> true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_lock_control <span style="color:#f92672">=</span> false, data_t sigma_soft_start <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> data_t sigma_u_noise <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset <span style="color:#f92672">=</span> true); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">CalcSteadyStateSetPoint</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">AntiWindup</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">InitVars</span>(size_t control_type); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// Implement the above: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Controller(<span style="color:#66d9ef">const</span> System<span style="color:#f92672">&amp;</span> sys, data_t u_lb, +</span></span><span style="display:flex;"><span> data_t u_ub, size_t control_type) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> sys_(sys), +</span></span><span style="display:flex;"><span> u_lb_(u_lb), +</span></span><span style="display:flex;"><span> u_ub_(u_ub), +</span></span><span style="display:flex;"><span> tau_awu_(lds<span style="color:#f92672">::</span>kInf) { +</span></span><span style="display:flex;"><span> InitVars(control_type); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Controller(System<span style="color:#f92672">&amp;&amp;</span> sys, data_t u_lb, data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> sys_(std<span style="color:#f92672">::</span>move(sys)), +</span></span><span style="display:flex;"><span> u_lb_(u_lb), +</span></span><span style="display:flex;"><span> u_ub_(u_ub), +</span></span><span style="display:flex;"><span> tau_awu_(lds<span style="color:#f92672">::</span>kInf) { +</span></span><span style="display:flex;"><span> InitVars(control_type); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_control_type(size_t control_type) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (control_type_ <span style="color:#f92672">==</span> control_type) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// creating a blank slate... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> control_type_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> Kc_inty_.zeros(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> Kc_u_.zeros(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> int_e_.zeros(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> int_e_awu_adjust_.zeros(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// controller was designed to minimize integral error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (control_type <span style="color:#f92672">&amp;</span> kControlTypeIntY) { +</span></span><span style="display:flex;"><span> Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); +</span></span><span style="display:flex;"><span> int_e_.zeros(sys_.n_y()); +</span></span><span style="display:flex;"><span> int_e_awu_adjust_.zeros(sys_.n_u()); +</span></span><span style="display:flex;"><span> control_type_ <span style="color:#f92672">=</span> control_type_ <span style="color:#f92672">|</span> kControlTypeIntY; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// controller was designed to minimize deltaU +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (i.e. state augmented with u) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (control_type <span style="color:#f92672">&amp;</span> kControlTypeDeltaU) { +</span></span><span style="display:flex;"><span> Kc_u_.zeros(sys_.n_u(), sys_.n_u()); +</span></span><span style="display:flex;"><span> control_type_ <span style="color:#f92672">=</span> control_type_ <span style="color:#f92672">|</span> kControlTypeDeltaU; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// whether to adapt set point calculate with (re-estimated) process +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// disturbance (m) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (control_type <span style="color:#f92672">&amp;</span> kControlTypeAdaptM) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (sys_.do_adapt_m) <span style="color:#75715e">// only if adapting m... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> control_type_ <span style="color:#f92672">=</span> control_type_ <span style="color:#f92672">|</span> kControlTypeAdaptM; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// set_control_type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Control( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> z, <span style="color:#66d9ef">bool</span> do_control, <span style="color:#66d9ef">bool</span> do_lock_control, +</span></span><span style="display:flex;"><span> data_t sigma_soft_start, data_t sigma_u_noise, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update state estimates, given latest measurement +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sys_.Filter(u_, z); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_estimation <span style="color:#f92672">=</span> true; <span style="color:#75715e">// always have estimator on in this case +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// calculate control signal +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, +</span></span><span style="display:flex;"><span> sigma_u_noise, do_reset_at_control_onset); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> u_return_; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>ControlOutputReference( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> z, <span style="color:#66d9ef">bool</span> do_control, <span style="color:#66d9ef">bool</span> do_estimation, <span style="color:#66d9ef">bool</span> do_lock_control, +</span></span><span style="display:flex;"><span> data_t sigma_soft_start, data_t sigma_u_noise, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update state estimates, given latest measurement +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (do_estimation) { +</span></span><span style="display:flex;"><span> sys_.Filter(u_, z); +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> sys_.f(u_); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// calculate the set point +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// solves for u_ref and x_ref when output is at y_ref at steady state. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (do_control) { +</span></span><span style="display:flex;"><span> CalcSteadyStateSetPoint(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// calculate control signal +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, +</span></span><span style="display:flex;"><span> sigma_u_noise, do_reset_at_control_onset); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> u_return_; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>CalcControl(<span style="color:#66d9ef">bool</span> do_control, <span style="color:#66d9ef">bool</span> do_estimation, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_lock_control, +</span></span><span style="display:flex;"><span> data_t sigma_soft_start, +</span></span><span style="display:flex;"><span> data_t sigma_u_noise, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_reset_at_control_onset) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_control <span style="color:#f92672">&amp;&amp;</span> do_estimation) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>do_control_prev_) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_reset_at_control_onset) { +</span></span><span style="display:flex;"><span> Reset(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> t_since_control_onset_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> t_since_control_onset_ <span style="color:#f92672">+=</span> sys_.dt(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// enforce softstart on control vars. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (sigma_soft_start <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// half-Gaussian soft-start scaling factor +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t soft_start_sf <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> exp(<span style="color:#f92672">-</span>pow(t_since_control_onset_, <span style="color:#ae81ff">2</span>) <span style="color:#f92672">/</span> +</span></span><span style="display:flex;"><span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> pow(sigma_soft_start, <span style="color:#ae81ff">2</span>))); +</span></span><span style="display:flex;"><span> u_ref_ <span style="color:#f92672">*=</span> soft_start_sf; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_ref_ *= soft_start_sf; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// cx_ref_ *= soft_start_sf; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// y_ref_ *= soft_start_sf; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>do_lock_control) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// first do u -&gt; v change of vars. (v = g.*u) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// control voltage u[=]V) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> v_ref_ <span style="color:#f92672">=</span> g_design_ <span style="color:#f92672">%</span> u_ref_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Given FB, calc. the change in control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (control_type_ <span style="color:#f92672">&amp;</span> kControlTypeDeltaU) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if control designed to minimize not u but deltaU (i.e. state aug with +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// u): +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): Commented out for now. See note below. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// du_ref_ = u_ref_ - u_ref_prev_; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// dv_ref_ = g_design_ % du_ref_; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): Assuming users want *smooth* control signals if using +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// kControlTypeDeltaU, it should be the case that dv_ref_ is --&gt; 0. May +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// want to revisit, but I am going to force it to be zero unless a +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// situation arises that argues for keeping the above. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> dv_ref_.zeros(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> dv_ <span style="color:#f92672">=</span> dv_ref_; <span style="color:#75715e">// nominally-optimal. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> dv_ <span style="color:#f92672">-=</span> Kc_ <span style="color:#f92672">*</span> (sys_.x() <span style="color:#f92672">-</span> x_ref_); <span style="color:#75715e">// instantaneous state error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> dv_ <span style="color:#f92672">-=</span> Kc_u_ <span style="color:#f92672">*</span> (v_ <span style="color:#f92672">-</span> v_ref_); <span style="color:#75715e">// penalty on amp u (rel to ref) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (control_type_ <span style="color:#f92672">&amp;</span> kControlTypeIntY) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): one approach to protection against integral windup +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// would be to not integrate error when control signal saturated: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if(!uSaturated) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> int_e_ <span style="color:#f92672">+=</span> (sys_.cx() <span style="color:#f92672">-</span> cx_ref_) <span style="color:#f92672">*</span> sys_.dt(); <span style="color:#75715e">// integrated error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> dv_ <span style="color:#f92672">-=</span> Kc_inty_ <span style="color:#f92672">*</span> int_e_; <span style="color:#75715e">// control for integrated error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update the control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> v_ <span style="color:#f92672">+=</span> dv_; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> v_ <span style="color:#f92672">=</span> v_ref_; <span style="color:#75715e">// nominally-optimal. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> v_ <span style="color:#f92672">-=</span> Kc_ <span style="color:#f92672">*</span> (sys_.x() <span style="color:#f92672">-</span> x_ref_); <span style="color:#75715e">// instantaneous state error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (control_type_ <span style="color:#f92672">&amp;</span> kControlTypeIntY) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): one approach to protection against integral windup +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// would be to not integrate error when control signal saturated: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if (!uSaturated) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> int_e_ <span style="color:#f92672">+=</span> (sys_.cx() <span style="color:#f92672">-</span> cx_ref_) <span style="color:#f92672">*</span> sys_.dt(); <span style="color:#75715e">// integrated error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> v_ <span style="color:#f92672">-=</span> Kc_inty_ <span style="color:#f92672">*</span> int_e_; <span style="color:#75715e">// control for integrated error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// convert back to control voltage u[=]V +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u_ <span style="color:#f92672">=</span> v_ <span style="color:#f92672">/</span> sys_.g(); +</span></span><span style="display:flex;"><span> } <span style="color:#75715e">// else do nothing until lock is low +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } <span style="color:#66d9ef">else</span> { <span style="color:#75715e">// if not control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// feed through u_ref in open loop +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u_ <span style="color:#f92672">=</span> u_ref_ <span style="color:#f92672">%</span> g_design_ <span style="color:#f92672">/</span> sys_.g(); +</span></span><span style="display:flex;"><span> v_ <span style="color:#f92672">=</span> sys_.g() <span style="color:#f92672">%</span> u_; +</span></span><span style="display:flex;"><span> u_ref_.zeros(); +</span></span><span style="display:flex;"><span> int_e_.zeros(); +</span></span><span style="display:flex;"><span> int_e_awu_adjust_.zeros(); +</span></span><span style="display:flex;"><span> u_sat_.zeros(); +</span></span><span style="display:flex;"><span> } <span style="color:#75715e">// ends do_control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// enforce box constraints (and antiwindup) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> AntiWindup(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// add noise to input? +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// The value for u that is *returned* to user after addition of any noise, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// while keeping controller/estimator blind to this addition. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u_return_ <span style="color:#f92672">=</span> u_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ((sigma_u_noise <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0.0</span>) <span style="color:#f92672">&amp;&amp;</span> (do_control <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">!</span>do_lock_control)) { +</span></span><span style="display:flex;"><span> u_return_ <span style="color:#f92672">+=</span> sigma_u_noise <span style="color:#f92672">*</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>randn); +</span></span><span style="display:flex;"><span> Limit(u_return_, u_lb_, u_ub_); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// For next time step: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u_ref_prev_ <span style="color:#f92672">=</span> u_ref_; +</span></span><span style="display:flex;"><span> do_control_prev_ <span style="color:#f92672">=</span> do_control; +</span></span><span style="display:flex;"><span> do_lock_control_prev_ <span style="color:#f92672">=</span> do_lock_control; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// CalcControl +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>CalcSteadyStateSetPoint() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Linearly-constrained least squares (ls). +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// _reference: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Boyd &amp; Vandenberghe (2018) Introduction to Applied Linear Algebra +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix a_ls <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill<span style="color:#f92672">::</span>zeros)); +</span></span><span style="display:flex;"><span> Vector b_ls <span style="color:#f92672">=</span> cx_ref_; +</span></span><span style="display:flex;"><span> Matrix c_ls <span style="color:#f92672">=</span> join_horiz(sys_.A() <span style="color:#f92672">-</span> Matrix(sys_.n_x(), sys_.n_x(), fill<span style="color:#f92672">::</span>eye), +</span></span><span style="display:flex;"><span> sys_.B() <span style="color:#f92672">*</span> arma<span style="color:#f92672">::</span>diagmat(sys_.g())); +</span></span><span style="display:flex;"><span> Vector d_ls <span style="color:#f92672">=</span> <span style="color:#f92672">-</span>sys_.m0(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (control_type_ <span style="color:#f92672">&amp;</span> kControlTypeAdaptM) { +</span></span><span style="display:flex;"><span> d_ls <span style="color:#f92672">=</span> <span style="color:#f92672">-</span>sys_.m(); <span style="color:#75715e">// adapt setpoint calc with disturbance? +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix a_ls_t <span style="color:#f92672">=</span> a_ls.t(); <span style="color:#75715e">// TODO(mfbolus): not sure why but causes seg +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// fault if I do not do this. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix phi_ls <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> join_vert(join_horiz(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> a_ls_t <span style="color:#f92672">*</span> a_ls, c_ls.t()), +</span></span><span style="display:flex;"><span> join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill<span style="color:#f92672">::</span>zeros))); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix inv_phi <span style="color:#f92672">=</span> pinv(phi_ls); +</span></span><span style="display:flex;"><span> Vector xulam <span style="color:#f92672">=</span> inv_phi <span style="color:#f92672">*</span> join_vert(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> a_ls_t <span style="color:#f92672">*</span> b_ls, d_ls); +</span></span><span style="display:flex;"><span> x_ref_ <span style="color:#f92672">=</span> xulam.subvec(<span style="color:#ae81ff">0</span>, sys_.n_x() <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> u_ref_ <span style="color:#f92672">=</span> xulam.subvec(sys_.n_x(), sys_.n_x() <span style="color:#f92672">+</span> sys_.n_u() <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> cx_ref_ <span style="color:#f92672">=</span> sys_.C() <span style="color:#f92672">*</span> x_ref_; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// CalcSteadyStateSetPoint +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>AntiWindup() { +</span></span><span style="display:flex;"><span> u_saturated_ <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> u_sat_ <span style="color:#f92672">=</span> u_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// limit u and flag whether saturated +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> u_.n_elem; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (u_[k] <span style="color:#f92672">&lt;</span> u_lb_) { +</span></span><span style="display:flex;"><span> u_sat_[k] <span style="color:#f92672">=</span> u_lb_; +</span></span><span style="display:flex;"><span> u_saturated_ <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (u_[k] <span style="color:#f92672">&gt;</span> u_ub_) { +</span></span><span style="display:flex;"><span> u_sat_[k] <span style="color:#f92672">=</span> u_ub_; +</span></span><span style="display:flex;"><span> u_saturated_ <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ((control_type_ <span style="color:#f92672">&amp;</span> kControlTypeIntY) <span style="color:#f92672">&amp;&amp;</span> (tau_awu_ <span style="color:#f92672">&lt;</span> lds<span style="color:#f92672">::</span>kInf)) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// one-step back-calculation (calculate intE for u=u_sat) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (Astroem, Rundqwist 1989 warn against using this...) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// int_e_awu_adjust_ = +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// gradual: see Astroem, Rundqwist 1989 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// this is a fudge for doing MIMO gradual +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// n.b., went ahead and multiplied 1/T by dt so don&#39;t have to do that here. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> int_e_awu_adjust_ <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> k_awu_ <span style="color:#f92672">*</span> (sign(Kc_inty_).t() <span style="color:#f92672">/</span> sys_.n_u()) <span style="color:#f92672">*</span> (u_ <span style="color:#f92672">-</span> u_sat_); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> int_e_ <span style="color:#f92672">+=</span> int_e_awu_adjust_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set u to saturated version +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u_ <span style="color:#f92672">=</span> u_sat_; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>InitVars(size_t control_type) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// initialize to default values +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u_ref_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> u_ref_prev_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> x_ref_ <span style="color:#f92672">=</span> Vector(sys_.n_x(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> y_ref_ <span style="color:#f92672">=</span> Vector(sys_.n_y(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> cx_ref_ <span style="color:#f92672">=</span> Vector(sys_.n_y(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> u_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> u_return_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> u_sat_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Might not need all these, so zero elements until later. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Kc_ <span style="color:#f92672">=</span> Matrix(sys_.n_u(), sys_.n_x(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Kc_u_ <span style="color:#f92672">=</span> Matrix(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Kc_inty_ <span style="color:#f92672">=</span> Matrix(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> g_design_ <span style="color:#f92672">=</span> sys_.g(); <span style="color:#75715e">// by default, same as model +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> dv_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> v_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> du_ref_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> dv_ref_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> v_ref_ <span style="color:#f92672">=</span> Vector(sys_.n_u(), fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> int_e_ <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">0</span>, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> int_e_awu_adjust_ <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">0</span>, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> set_control_type(control_type); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_ctrlh">ldsCtrlEst_h/lds_ctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__fit_8h/index.html b/docs/docs/api/files/lds__fit_8h/index.html new file mode 100644 index 00000000..ac17cc53 --- /dev/null +++ b/docs/docs/api/files/lds__fit_8h/index.html @@ -0,0 +1,408 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="LDS base fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_fit.h"> + <meta property="og:description" content="LDS base fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_fit.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_fit.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_fith">ldsCtrlEst_h/lds_fit.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_fith"> + ldsCtrlEst_h/lds_fit.h + <a class="anchor" href="#ldsctrlest_hlds_fith">#</a> +</h1> +<p>LDS base fit type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></strong> <br>LDS <a href="">Fit</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDS_FIT_HPP +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDS_FIT_HPP +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_uniform_mats.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Fit</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> Fit() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#f92672">~</span>Fit() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// get methods +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t <span style="color:#a6e22e">n_u</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> n_u_; }; +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">n_x</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> n_x_; }; +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">n_y</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> n_y_; }; +</span></span><span style="display:flex;"><span> data_t <span style="color:#a6e22e">dt</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> dt_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> A() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> A_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> B() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> B_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> g() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> g_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> m() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> m_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> Q_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x0() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> x0_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P0() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> P0_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> C() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> C_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> d() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> d_; }; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// gets measurement noise +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set methods (e.g., seeding initial fit values) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_A</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> A) { Reassign(A_, A); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_B</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> B) { Reassign(B_, B); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_g</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> g) { Reassign(g_, g); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_m</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> m) { Reassign(m_, m); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Q</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q) { +</span></span><span style="display:flex;"><span> Reassign(Q_, Q); +</span></span><span style="display:flex;"><span> ForceSymPD(Q_); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_R</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R) <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_x0</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x0) { Reassign(x0_, x0); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_P0</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P0) { +</span></span><span style="display:flex;"><span> Reassign(P0_, P0); +</span></span><span style="display:flex;"><span> ForceSymPD(P0_); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_C</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> C) { Reassign(C_, C); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_d</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> d) { Reassign(d_, d); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> View <span style="color:#a6e22e">f</span>(Matrix<span style="color:#f92672">&amp;</span> x, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> u, size_t t) { +</span></span><span style="display:flex;"><span> x.col(t) <span style="color:#f92672">=</span> A_ <span style="color:#f92672">*</span> x.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+</span> B_ <span style="color:#f92672">*</span> (g_ <span style="color:#f92672">%</span> u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)) <span style="color:#f92672">+</span> m_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> x.col(t); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> View <span style="color:#a6e22e">f</span>(Matrix<span style="color:#f92672">&amp;</span> x_pre, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> x_post, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> u, size_t t) { +</span></span><span style="display:flex;"><span> x_pre.col(t) <span style="color:#f92672">=</span> A_ <span style="color:#f92672">*</span> x_post.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+</span> B_ <span style="color:#f92672">*</span> (g_ <span style="color:#f92672">%</span> u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)) <span style="color:#f92672">+</span> m_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> x_pre.col(t); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> View <span style="color:#a6e22e">h</span>(Matrix<span style="color:#f92672">&amp;</span> y, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> x, size_t t) <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> data_t dt_{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Dynamics +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix A_; +</span></span><span style="display:flex;"><span> Matrix B_; +</span></span><span style="display:flex;"><span> Vector g_; +</span></span><span style="display:flex;"><span> Vector m_; +</span></span><span style="display:flex;"><span> Matrix Q_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Output +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix C_; +</span></span><span style="display:flex;"><span> Vector d_; +</span></span><span style="display:flex;"><span> Matrix R_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_; +</span></span><span style="display:flex;"><span> Matrix P0_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t n_u_{}; +</span></span><span style="display:flex;"><span> size_t n_x_{}; +</span></span><span style="display:flex;"><span> size_t n_y_{}; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_fith">ldsCtrlEst_h/lds_fit.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__fit__em_8h/index.html b/docs/docs/api/files/lds__fit__em_8h/index.html new file mode 100644 index 00000000..e48627e9 --- /dev/null +++ b/docs/docs/api/files/lds__fit__em_8h/index.html @@ -0,0 +1,891 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="subspace identification"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_fit_em.h"> + <meta property="og:description" content="subspace identification"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_fit_em.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_fit_em.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_fit_emh">ldsCtrlEst_h/lds_fit_em.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_fit_emh"> + ldsCtrlEst_h/lds_fit_em.h + <a class="anchor" href="#ldsctrlest_hlds_fit_emh">#</a> +</h1> +<p>subspace identification <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares the type for fitting a linear dynamical system by expectation-maximization (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM</a>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_EMAX_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_EMAX_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">EM</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">static_assert</span>(std<span style="color:#f92672">::</span>is_base_of<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>Fit, Fit<span style="color:#f92672">&gt;::</span>value, +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Fit must be derived from lds::Fit type.&#34;</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> EM() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> EM(size_t n_x, data_t dt, UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> z_train); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> EM(<span style="color:#66d9ef">const</span> Fit<span style="color:#f92672">&amp;</span> fit0, UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> z_train); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#f92672">~</span>EM() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Fit<span style="color:#f92672">&amp;</span> Run(<span style="color:#66d9ef">bool</span> calc_dynamics <span style="color:#f92672">=</span> true, <span style="color:#66d9ef">bool</span> calc_Q <span style="color:#f92672">=</span> true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_init <span style="color:#f92672">=</span> true, <span style="color:#66d9ef">bool</span> calc_output <span style="color:#f92672">=</span> true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_measurement <span style="color:#f92672">=</span> true, size_t max_iter <span style="color:#f92672">=</span> <span style="color:#ae81ff">100</span>, +</span></span><span style="display:flex;"><span> data_t tol <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-2</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span>UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span>, UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&gt;</span> +</span></span><span style="display:flex;"><span> ReturnData() { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> tuple <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>make_tuple(std<span style="color:#f92672">::</span>move(u_), std<span style="color:#f92672">::</span>move(z_)); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// auto tuple = std::make_tuple(u_, z_); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u_ <span style="color:#f92672">=</span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span>(); +</span></span><span style="display:flex;"><span> z_ <span style="color:#f92672">=</span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span>(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> tuple; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;&amp;</span> x() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> x_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;&amp;</span> y() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> y_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> sum_E_x_t_x_t() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> sum_E_x_t_x_t_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> sum_E_xu_tm1_xu_tm1() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> sum_E_xu_tm1_xu_tm1_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> sum_E_xu_t_xu_tm1() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> sum_E_xu_t_xu_tm1_; }; +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">n_t_tot</span>() { <span style="color:#66d9ef">return</span> n_t_tot_; } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> theta() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> theta_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> Expectation(<span style="color:#66d9ef">bool</span> force_common_initial <span style="color:#f92672">=</span> false); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Maximization</span>(<span style="color:#66d9ef">bool</span> calc_dynamics <span style="color:#f92672">=</span> true, <span style="color:#66d9ef">bool</span> calc_Q <span style="color:#f92672">=</span> true, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_init <span style="color:#f92672">=</span> false, <span style="color:#66d9ef">bool</span> calc_output <span style="color:#f92672">=</span> false, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_measurement <span style="color:#f92672">=</span> false); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">MaximizeDynamics</span>(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">MaximizeQ</span>(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">MaximizeInitial</span>(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">MaximizeOutput</span>() <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">MaximizeMeasurement</span>() <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Smooth</span>(<span style="color:#66d9ef">bool</span> force_common_initial); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">RecurseKe</span>(Matrix<span style="color:#f92672">&amp;</span> Ke, Cube<span style="color:#f92672">&amp;</span> P_pre, Cube<span style="color:#f92672">&amp;</span> P_post, size_t t) <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Reset</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">InitVars</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">UpdateTheta</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// input/output training data +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> u_; +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> z_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span> x_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Cube<span style="color:#f92672">&gt;</span> P_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Cube<span style="color:#f92672">&gt;</span> P_t_tm1_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span> y_; +</span></span><span style="display:flex;"><span> Matrix diag_y_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// expectations calculated in E-step +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix sum_E_x_t_x_t_; +</span></span><span style="display:flex;"><span> Matrix sum_E_xu_tm1_xu_tm1_; +</span></span><span style="display:flex;"><span> Matrix sum_E_xu_t_xu_tm1_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Fit fit_; +</span></span><span style="display:flex;"><span> Vector theta_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t dt_{}; +</span></span><span style="display:flex;"><span> size_t n_u_{}; +</span></span><span style="display:flex;"><span> size_t n_x_{}; +</span></span><span style="display:flex;"><span> size_t n_y_{}; +</span></span><span style="display:flex;"><span> size_t n_trials_{}; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span> n_t_; +</span></span><span style="display:flex;"><span> size_t n_t_tot_{}; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>EM(size_t n_x, data_t dt, UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> z_train) { +</span></span><span style="display:flex;"><span> n_u_ <span style="color:#f92672">=</span> u_train.at(<span style="color:#ae81ff">0</span>).n_rows; +</span></span><span style="display:flex;"><span> n_y_ <span style="color:#f92672">=</span> z_train.at(<span style="color:#ae81ff">0</span>).n_rows; +</span></span><span style="display:flex;"><span> fit_ <span style="color:#f92672">=</span> Fit(n_u_, n_x, n_y_, dt); +</span></span><span style="display:flex;"><span> u_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(u_train); +</span></span><span style="display:flex;"><span> z_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(z_train); +</span></span><span style="display:flex;"><span> InitVars(); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>EM(<span style="color:#66d9ef">const</span> Fit<span style="color:#f92672">&amp;</span> fit0, UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> z_train) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure fit dims match I/O data +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (fit0.n_u() <span style="color:#f92672">!=</span> u_train.at(<span style="color:#ae81ff">0</span>).n_rows) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Initial fit and input training data have inconsistent dimensions&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (fit0.n_y() <span style="color:#f92672">!=</span> z_train.at(<span style="color:#ae81ff">0</span>).n_rows) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Initial fit and output training data have inconsistent dimensions&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> fit_ <span style="color:#f92672">=</span> fit0; +</span></span><span style="display:flex;"><span> u_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(u_train); +</span></span><span style="display:flex;"><span> z_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(z_train); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> InitVars(); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>InitVars() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check input/output data dimensions are consistent +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (z_.size() <span style="color:#f92672">!=</span> u_.size()) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;I/O training data have different number of trials.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> n_trials_ <span style="color:#f92672">=</span> u_.size(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> n_t_tot_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> n_t_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(n_trials_); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> n_trials_; trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (z_.at(trial).n_cols <span style="color:#f92672">!=</span> u_.at(trial).n_cols) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;I/O training data have different number of time steps.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> n_t_[trial] <span style="color:#f92672">=</span> u_.at(trial).n_cols; +</span></span><span style="display:flex;"><span> n_t_tot_ <span style="color:#f92672">+=</span> n_t_[trial]; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> n_u_ <span style="color:#f92672">=</span> fit_.n_u(); +</span></span><span style="display:flex;"><span> n_x_ <span style="color:#f92672">=</span> fit_.n_x(); +</span></span><span style="display:flex;"><span> n_y_ <span style="color:#f92672">=</span> fit_.n_y(); +</span></span><span style="display:flex;"><span> dt_ <span style="color:#f92672">=</span> fit_.dt(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span>(n_trials_); +</span></span><span style="display:flex;"><span> P_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Cube<span style="color:#f92672">&gt;</span>(n_trials_); +</span></span><span style="display:flex;"><span> P_t_tm1_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Cube<span style="color:#f92672">&gt;</span>(n_trials_); +</span></span><span style="display:flex;"><span> y_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span>(n_trials_); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> n_trials_; trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> x_[trial] <span style="color:#f92672">=</span> Matrix(n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> P_[trial] <span style="color:#f92672">=</span> Cube(n_x_, n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> P_t_tm1_[trial] <span style="color:#f92672">=</span> Cube(n_x_, n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> y_[trial] <span style="color:#f92672">=</span> Matrix(n_y_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> diag_y_ <span style="color:#f92672">=</span> Matrix(n_y_, n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// covariances in expectation step +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sum_E_x_t_x_t_ <span style="color:#f92672">=</span> Matrix(n_x_, n_x_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> sum_E_xu_tm1_xu_tm1_ <span style="color:#f92672">=</span> Matrix(n_x_ <span style="color:#f92672">+</span> n_u_, n_x_ <span style="color:#f92672">+</span> n_u_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> sum_E_xu_t_xu_tm1_ <span style="color:#f92672">=</span> Matrix(n_x_ <span style="color:#f92672">+</span> n_u_, n_x_ <span style="color:#f92672">+</span> n_u_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> Fit<span style="color:#f92672">&amp;</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Run(<span style="color:#66d9ef">bool</span> calc_dynamics, <span style="color:#66d9ef">bool</span> calc_Q, <span style="color:#66d9ef">bool</span> calc_init, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_output, <span style="color:#66d9ef">bool</span> calc_measurement, +</span></span><span style="display:flex;"><span> size_t max_iter, data_t tol) { +</span></span><span style="display:flex;"><span> Reset(); <span style="color:#75715e">// to initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> size_t n_params <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">3</span> <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">*</span> n_y_; +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">theta</span>(n_params); +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">theta_new</span>(n_params); +</span></span><span style="display:flex;"><span> data_t max_dtheta <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if solving for initial conditions, allow them be varied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// otherwise, freeze at provided values. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> force_common_initial <span style="color:#f92672">=</span> <span style="color:#f92672">!</span>calc_init; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// go until parameter convergence +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> (size_t l <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; l <span style="color:#f92672">&lt;</span> max_iter; l<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> theta_ <span style="color:#f92672">=</span> UpdateTheta(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Iteration &#34;</span> <span style="color:#f92672">&lt;&lt;</span> l <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;/&#34;</span> <span style="color:#f92672">&lt;&lt;</span> max_iter <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; ...</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Expectation(force_common_initial); +</span></span><span style="display:flex;"><span> Maximization(calc_dynamics, calc_Q, calc_init, calc_output, +</span></span><span style="display:flex;"><span> calc_measurement); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check convergence +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> theta_new <span style="color:#f92672">=</span> UpdateTheta(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector dtheta <span style="color:#f92672">=</span> abs(theta_new <span style="color:#f92672">-</span> theta_) <span style="color:#f92672">/</span> abs(theta_); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// some parameters could be zero... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>uvec ubi_finite <span style="color:#f92672">=</span> find_finite(dtheta); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> max_dtheta <span style="color:#f92672">=</span> max(dtheta.elem(ubi_finite)); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;max dtheta: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> max_dtheta <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (max_dtheta <span style="color:#f92672">&lt;</span> tol) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Converged.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> fit_; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Smooth(<span style="color:#66d9ef">bool</span> force_common_initial) { +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">k_e</span>(n_x_, n_y_); <span style="color:#75715e">// estimator gain +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Cube k_backfilt; <span style="color:#75715e">// back-filtering gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): this loop could be made parallel +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">x_pre</span>(n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Cube <span style="color:#a6e22e">p_pre</span>(n_x_, n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">x_post</span>(n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Cube <span style="color:#a6e22e">p_post</span>(n_x_, n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (force_common_initial) <span style="color:#75715e">// forces all trials to have same initial +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// conditions. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> x_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> fit_.x0(); +</span></span><span style="display:flex;"><span> P_[trial].slice(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> fit_.P0(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> y_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> fit_.C() <span style="color:#f92672">*</span> x_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">+</span> fit_.d(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// This *should not* be necessary but make sure P is symmetric. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> ForceSymPD(P_[trial].slice(<span style="color:#ae81ff">0</span>)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_pre.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> x_[trial].col(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> p_pre.slice(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> P_[trial].slice(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_post.col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> x_[trial].col(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> p_post.slice(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> P_[trial].slice(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// filter +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t_[trial]; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// predict +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> fit_.f(x_pre, x_post, u_.at(trial), t); +</span></span><span style="display:flex;"><span> fit_.h(y_[trial], x_pre, t); +</span></span><span style="display:flex;"><span> diag_y_.diag() <span style="color:#f92672">=</span> y_[trial].col(t); <span style="color:#75715e">// TODO(mfbolus): change if parallel +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update --&gt; posterior estimation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> RecurseKe(k_e, p_pre, p_post, t); +</span></span><span style="display:flex;"><span> x_post.col(t) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> x_pre.col(t) <span style="color:#f92672">+</span> k_e <span style="color:#f92672">*</span> (z_.at(trial).col(t) <span style="color:#f92672">-</span> y_[trial].col(t)); +</span></span><span style="display:flex;"><span> y_[trial].col(t) <span style="color:#f92672">=</span> fit_.C() <span style="color:#f92672">*</span> x_post.col(t) <span style="color:#f92672">+</span> fit_.d(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// backfilter -&gt; Smoothed estimate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Reference: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Shumway et Stoffer (1982) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> ForceSymPD(p_post.slice(n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> k_backfilt <span style="color:#f92672">=</span> Cube(n_x_, n_x_, n_t_[trial], fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> x_[trial].col(n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> x_post.col(n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> P_[trial].slice(n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> p_post.slice(n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> (n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); t <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>; t<span style="color:#f92672">--</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfmbolus): should not be necessary to force symm positive def +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> ForceSymPD(p_pre.slice(t)); +</span></span><span style="display:flex;"><span> ForceSymPD(p_post.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> ForceSymPD(P_[trial].slice(t)); +</span></span><span style="display:flex;"><span> k_backfilt.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> p_post.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> fit_.A().t() <span style="color:#f92672">*</span> inv_sympd(p_pre.slice(t)); +</span></span><span style="display:flex;"><span> x_[trial].col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> x_post.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+</span> +</span></span><span style="display:flex;"><span> k_backfilt.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> (x_[trial].col(t) <span style="color:#f92672">-</span> x_pre.col(t)); +</span></span><span style="display:flex;"><span> P_[trial].slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> p_post.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+</span> k_backfilt.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> (P_[trial].slice(t) <span style="color:#f92672">-</span> p_pre.slice(t)) <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> k_backfilt.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>).t(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// do the same for P_t_tm1 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">id</span>(n_x_, n_x_, fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> P_t_tm1_[trial].slice(n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> (id <span style="color:#f92672">-</span> k_e <span style="color:#f92672">*</span> fit_.C()) <span style="color:#f92672">*</span> fit_.A() <span style="color:#f92672">*</span> p_post.slice(n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">2</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> (n_t_[trial] <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); t <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">1</span>; t<span style="color:#f92672">--</span>) { +</span></span><span style="display:flex;"><span> P_t_tm1_[trial].slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> p_post.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> k_backfilt.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">2</span>).t() <span style="color:#f92672">+</span> +</span></span><span style="display:flex;"><span> k_backfilt.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> (P_t_tm1_[trial].slice(t) <span style="color:#f92672">-</span> fit_.A() <span style="color:#f92672">*</span> p_post.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)) <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> k_backfilt.slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">2</span>).t(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// finally, get smoothed estimate of output +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; t <span style="color:#f92672">&lt;</span> n_t_[trial]; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> fit_.h(y_[trial], x_[trial], t); +</span></span><span style="display:flex;"><span> } <span style="color:#75715e">// samps loop +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } <span style="color:#75715e">// trial loop +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// Smooth +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// template &lt;typename Fit&gt; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// void EM&lt;Fit&gt;::RecurseKe(Matrix&amp; Ke, Cube&amp; P_pre, Cube&amp; P_post, size_t t) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // predict covar +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// // update Ke +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Ke = P_pre.slice(t) * fit_.C().t() * +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// // update cov +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // Reference: Ghahramani et Hinton (1996) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// // // n.b. for poisson : +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// fit_.Q(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // // update cov +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // fit_.C()); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // // update Ke +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // Ke = P_post.slice(t) * fit_.C(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Expectation(<span style="color:#66d9ef">bool</span> force_common_initial) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// calculate the mean/cov of state needed for maximizing E[pr(z|theta)] +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Smooth(force_common_initial); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// now get the various forms of sum(E[xx&#39;]) needed +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// n.b. Going to start at t=1 rather than 0 bc most max terms need that. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// so really &#34;n_t_tot_&#34; is (n_t_tot_-1) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> n_t_tot_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> sum_E_x_t_x_t_ <span style="color:#f92672">=</span> Matrix(n_x_, n_x_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> sum_E_xu_tm1_xu_tm1_ <span style="color:#f92672">=</span> Matrix(n_x_ <span style="color:#f92672">+</span> n_u_, n_x_ <span style="color:#f92672">+</span> n_u_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> sum_E_xu_t_xu_tm1_ <span style="color:#f92672">=</span> Matrix(n_x_ <span style="color:#f92672">+</span> n_u_, n_x_ <span style="color:#f92672">+</span> n_u_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">xu_tm1</span>(n_x_ <span style="color:#f92672">+</span> n_u_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">xu_t</span>(n_x_ <span style="color:#f92672">+</span> n_u_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t_[trial]; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// ------------ sum_E_x_t_x_t ------------ +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sum_E_x_t_x_t_ <span style="color:#f92672">+=</span> x_[trial].col(t) <span style="color:#f92672">*</span> x_[trial].col(t).t(); +</span></span><span style="display:flex;"><span> sum_E_x_t_x_t_ <span style="color:#f92672">+=</span> P_[trial].slice(t); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// ------------ sum_E_xu_tm1_xu_tm1 ------------ +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> xu_tm1 <span style="color:#f92672">=</span> join_vert(x_[trial].col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), u_.at(trial).col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> sum_E_xu_tm1_xu_tm1_ <span style="color:#f92672">+=</span> xu_tm1 <span style="color:#f92672">*</span> xu_tm1.t(); +</span></span><span style="display:flex;"><span> sum_E_xu_tm1_xu_tm1_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+=</span> +</span></span><span style="display:flex;"><span> P_[trial].slice(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// ------------ sum_E_xu_t_xu_tm1 ------------ +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> xu_t <span style="color:#f92672">=</span> join_vert(x_[trial].col(t), u_.at(trial).col(t)); +</span></span><span style="display:flex;"><span> sum_E_xu_t_xu_tm1_ <span style="color:#f92672">+=</span> xu_t <span style="color:#f92672">*</span> xu_tm1.t(); +</span></span><span style="display:flex;"><span> sum_E_xu_t_xu_tm1_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+=</span> +</span></span><span style="display:flex;"><span> P_t_tm1_[trial].slice(t); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> n_t_tot_ <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> } <span style="color:#75715e">// time +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } <span style="color:#75715e">// trial +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// Expectation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Maximization(<span style="color:#66d9ef">bool</span> calc_dynamics, <span style="color:#66d9ef">bool</span> calc_Q, <span style="color:#66d9ef">bool</span> calc_init, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> calc_output, <span style="color:#66d9ef">bool</span> calc_measurement) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (calc_output) { +</span></span><span style="display:flex;"><span> MaximizeOutput(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (calc_measurement) { +</span></span><span style="display:flex;"><span> MaximizeMeasurement(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (calc_dynamics) { +</span></span><span style="display:flex;"><span> MaximizeDynamics(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (calc_Q) { +</span></span><span style="display:flex;"><span> MaximizeQ(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (calc_init) { +</span></span><span style="display:flex;"><span> MaximizeInitial(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// Maximization +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>MaximizeDynamics() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix ab <span style="color:#f92672">=</span> sum_E_xu_t_xu_tm1_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> inv_sympd(sum_E_xu_tm1_xu_tm1_); +</span></span><span style="display:flex;"><span> fit_.set_A(ab.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> fit_.set_B(ab.submat(<span style="color:#ae81ff">0</span>, n_x_, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;A_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> fit_.A()[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;B_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> fit_.B()[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>MaximizeQ() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// View sum_e_x_t_xu_tm1 = +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// inv_sympd(sum_E_xu_tm1_xu_tm1_) * +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// sum_e_x_t_xu_tm1.t(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// q /= n_t_tot_; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// this way is same as above iff dynamics were just updated: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// View sum_e_x_t_xu_tm1 = +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// q /= n_t_tot_; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// From scratch method: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Q is covariance of the error between state and dynamics-predicted state +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (aka process noise) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})&#39;] +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// t-1 terms: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> View sum_e_x_tm1_x_tm1 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> sum_E_xu_tm1_xu_tm1_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> View sum_e_u_tm1_u_tm1 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> View sum_e_x_tm1_u_tm1 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> sum_E_xu_tm1_xu_tm1_.submat(<span style="color:#ae81ff">0</span>, n_x_, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// t, t-1 terms: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> View sum_e_x_t_x_tm1 <span style="color:#f92672">=</span> sum_E_xu_t_xu_tm1_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> View sum_e_x_t_u_tm1 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> sum_E_xu_t_xu_tm1_.submat(<span style="color:#ae81ff">0</span>, n_x_, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix q <span style="color:#f92672">=</span> sum_E_x_t_x_t_; +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">+=</span> fit_.A() <span style="color:#f92672">*</span> sum_e_x_tm1_x_tm1 <span style="color:#f92672">*</span> fit_.A().t(); +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">-=</span> sum_e_x_t_x_tm1 <span style="color:#f92672">*</span> fit_.A().t(); +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">-=</span> fit_.A() <span style="color:#f92672">*</span> sum_e_x_t_x_tm1.t(); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// input-related terms: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> q <span style="color:#f92672">+=</span> fit_.B() <span style="color:#f92672">*</span> sum_e_u_tm1_u_tm1 <span style="color:#f92672">*</span> fit_.B().t(); +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">-=</span> sum_e_x_t_u_tm1 <span style="color:#f92672">*</span> fit_.B().t(); +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">-=</span> fit_.B() <span style="color:#f92672">*</span> sum_e_x_t_u_tm1.t(); +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">+=</span> fit_.A() <span style="color:#f92672">*</span> sum_e_x_tm1_u_tm1 <span style="color:#f92672">*</span> fit_.B().t(); +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">+=</span> fit_.B() <span style="color:#f92672">*</span> sum_e_x_tm1_u_tm1.t() <span style="color:#f92672">*</span> fit_.A().t(); +</span></span><span style="display:flex;"><span> q <span style="color:#f92672">/=</span> n_t_tot_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> fit_.set_Q(q); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Q_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> fit_.Q()[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// std::cout &lt;&lt; &#34;Q_new: \n&#34; &lt;&lt; fit_.Q() &lt;&lt; &#34;\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>MaximizeInitial() { +</span></span><span style="display:flex;"><span> Vector x0 <span style="color:#f92672">=</span> fit_.x0(); +</span></span><span style="display:flex;"><span> x0.zeros(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> x0 <span style="color:#f92672">+=</span> x_[trial].col(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> x0 <span style="color:#f92672">/=</span> z_.size(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x0_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> x0[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// always recalc P0 even if the initial state is fixed (at zero, for +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// example) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">e_var</span>(n_x_, n_x_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> e_var <span style="color:#f92672">+=</span> (x_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">-</span> x0) <span style="color:#f92672">*</span> (x_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">-</span> x0).t(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> e_var <span style="color:#f92672">/=</span> z_.size(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// go ahead and subtract x0*x0&#39; so don&#39;t have to below. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> e_var <span style="color:#f92672">-=</span> x0 <span style="color:#f92672">*</span> x0.t(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// To get P0, going to get initial P_ per trial and average. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (which might be wrong, but need a single number) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix p0 <span style="color:#f92672">=</span> fit_.P0(); +</span></span><span style="display:flex;"><span> p0.zeros(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> p0 <span style="color:#f92672">+=</span> +</span></span><span style="display:flex;"><span> (x_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">*</span> x_[trial].col(<span style="color:#ae81ff">0</span>).t()) <span style="color:#f92672">+</span> P_[trial].slice(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">+</span> e_var; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> p0 <span style="color:#f92672">/=</span> z_.size(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> fit_.set_P0(p0); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;P0_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> fit_.P0()[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>MaximizeOutput() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// solve for C+d: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">sum_zx</span>(n_y_, n_x_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">x1</span>(n_x_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> x1[n_x_] <span style="color:#f92672">=</span> <span style="color:#ae81ff">1.0</span>; <span style="color:#75715e">// augment with one to solve for bias +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">sum_e_x1_x1</span>(n_x_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t_[trial]; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> x1.subvec(<span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> x_[trial].col(t); +</span></span><span style="display:flex;"><span> sum_zx <span style="color:#f92672">+=</span> z_.at(trial).col(t) <span style="color:#f92672">*</span> x1.t(); +</span></span><span style="display:flex;"><span> sum_e_x1_x1 <span style="color:#f92672">+=</span> x1 <span style="color:#f92672">*</span> x1.t(); +</span></span><span style="display:flex;"><span> sum_e_x1_x1.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+=</span> P_[trial].slice(t); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> Matrix cd <span style="color:#f92672">=</span> sum_zx <span style="color:#f92672">*</span> inv_sympd(sum_e_x1_x1); +</span></span><span style="display:flex;"><span> fit_.set_C(cd.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> fit_.set_d(vectorise(cd.submat(<span style="color:#ae81ff">0</span>, n_x_, n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_))); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;C_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> fit_.C()[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;d_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> fit_.d()[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>MaximizeMeasurement() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Solve for measurement noise covar +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t n_t_tot <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Ghahgramani, Hinton 1996: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">sum_zz</span>(n_y_, n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">sum_yz</span>(n_y_, n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; t <span style="color:#f92672">&lt;</span> n_t_[trial]; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> sum_zz <span style="color:#f92672">+=</span> z_.at(trial).col(t) <span style="color:#f92672">*</span> z_.at(trial).col(t).t(); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Use Cnew: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sum_yz <span style="color:#f92672">+=</span> +</span></span><span style="display:flex;"><span> (fit_.C() <span style="color:#f92672">*</span> x_[trial].col(t) <span style="color:#f92672">+</span> fit_.d()) <span style="color:#f92672">*</span> z_.at(trial).col(t).t(); +</span></span><span style="display:flex;"><span> n_t_tot <span style="color:#f92672">+=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> fit_.set_R((sum_zz <span style="color:#f92672">-</span> sum_yz) <span style="color:#f92672">/</span> n_t_tot); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;R_new[0]: &#34;</span> <span style="color:#f92672">&lt;&lt;</span> fit_.R()[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Reset() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// reset to initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> n_trials_; trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> x_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> fit_.x0(); +</span></span><span style="display:flex;"><span> P_[trial].slice(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> fit_.P0(); +</span></span><span style="display:flex;"><span> y_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">=</span> fit_.C() <span style="color:#f92672">*</span> x_[trial].col(<span style="color:#ae81ff">0</span>) <span style="color:#f92672">+</span> fit_.d(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>Vector EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>UpdateTheta() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): This should include n_y_ more params for d. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t n_params <span style="color:#f92672">=</span> <span style="color:#ae81ff">3</span> <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">+</span> n_y_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (fit_.R().n_elem <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> n_params <span style="color:#f92672">+=</span> n_y_ <span style="color:#f92672">*</span> n_y_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">theta</span>(n_params); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t idx_start <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.A()); +</span></span><span style="display:flex;"><span> idx_start <span style="color:#f92672">+=</span> n_x_ <span style="color:#f92672">*</span> n_x_; +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.B()); +</span></span><span style="display:flex;"><span> idx_start <span style="color:#f92672">+=</span> n_x_ <span style="color:#f92672">*</span> n_u_; +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.Q()); +</span></span><span style="display:flex;"><span> idx_start <span style="color:#f92672">+=</span> n_x_ <span style="color:#f92672">*</span> n_x_; +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.x0()); +</span></span><span style="display:flex;"><span> idx_start <span style="color:#f92672">+=</span> n_x_; +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_x_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.P0()); +</span></span><span style="display:flex;"><span> idx_start <span style="color:#f92672">+=</span> n_x_ <span style="color:#f92672">*</span> n_x_; +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">*</span> n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.C()); +</span></span><span style="display:flex;"><span> idx_start <span style="color:#f92672">+=</span> n_y_ <span style="color:#f92672">*</span> n_x_; +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.d()); +</span></span><span style="display:flex;"><span> idx_start <span style="color:#f92672">+=</span> n_y_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (fit_.R().n_elem <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> theta.subvec(idx_start, idx_start <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">*</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> vectorise(fit_.R()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> theta; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_fit_emh">ldsCtrlEst_h/lds_fit_em.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__fit__ssid_8h/index.html b/docs/docs/api/files/lds__fit__ssid_8h/index.html new file mode 100644 index 00000000..0e71380f --- /dev/null +++ b/docs/docs/api/files/lds__fit__ssid_8h/index.html @@ -0,0 +1,837 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="subspace identification"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_fit_ssid.h"> + <meta property="og:description" content="subspace identification"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_fit_ssid.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_fit_ssid.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_fit_ssidh">ldsCtrlEst_h/lds_fit_ssid.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_fit_ssidh"> + ldsCtrlEst_h/lds_fit_ssid.h + <a class="anchor" href="#ldsctrlest_hlds_fit_ssidh">#</a> +</h1> +<p>subspace identification <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm (<code>[lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)&lt;Fit&gt;</code>).</p> +<p>References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_FIT_SSID_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_FIT_SSID_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SSID</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">static_assert</span>(std<span style="color:#f92672">::</span>is_base_of<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>Fit, Fit<span style="color:#f92672">&gt;::</span>value, +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Fit must be derived from lds::Fit type.&#34;</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> SSID() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> SSID(size_t n_x, size_t n_h, data_t dt, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> z_train, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> d <span style="color:#f92672">=</span> Vector(<span style="color:#ae81ff">1</span>).fill(<span style="color:#f92672">-</span>kInf)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span>Fit, Vector<span style="color:#f92672">&gt;</span> Run(SSIDWt ssid_wt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span>UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span>, UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&gt;</span> +</span></span><span style="display:flex;"><span> ReturnData() { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> tuple <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>make_tuple(std<span style="color:#f92672">::</span>move(u_), std<span style="color:#f92672">::</span>move(z_)); +</span></span><span style="display:flex;"><span> u_ <span style="color:#f92672">=</span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span>(); +</span></span><span style="display:flex;"><span> z_ <span style="color:#f92672">=</span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span>(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> tuple; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> CalcD(data_t t_silence <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.1</span>, data_t thresh_silence <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.001</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">CreateHankelDataMat</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">DecomposeData</span>() <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">CalcSVD</span>(SSIDWt wt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Solve</span>(data_t wt_dc); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">RecomputeExtObs</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// input/output training data +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> u_; +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> z_; +</span></span><span style="display:flex;"><span> Matrix D_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Fit fit_; +</span></span><span style="display:flex;"><span> Matrix g_dc_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t dt_{}; +</span></span><span style="display:flex;"><span> size_t n_u_{}; +</span></span><span style="display:flex;"><span> size_t n_x_{}; +</span></span><span style="display:flex;"><span> size_t n_y_{}; +</span></span><span style="display:flex;"><span> size_t n_h_{}; +</span></span><span style="display:flex;"><span> size_t n_trials_{}; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span> n_t_; +</span></span><span style="display:flex;"><span> size_t n_t_tot_{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix L_; +</span></span><span style="display:flex;"><span> Vector s_; +</span></span><span style="display:flex;"><span> Matrix ext_obs_t_; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>SSID(size_t n_x, size_t n_h, data_t dt, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> u_train, +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&amp;&amp;</span> z_train, <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> d) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check input/output data dimensions are consistent +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (z_train.size() <span style="color:#f92672">!=</span> u_train.size()) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;I/O training data have different number of trials.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> n_trials_ <span style="color:#f92672">=</span> u_train.size(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> n_t_tot_ <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> n_t_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(n_trials_); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> n_trials_; trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (z_train.at(trial).n_cols <span style="color:#f92672">!=</span> u_train.at(trial).n_cols) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;I/O training data have different number of time steps.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> n_t_[trial] <span style="color:#f92672">=</span> u_train.at(trial).n_cols; +</span></span><span style="display:flex;"><span> n_t_tot_ <span style="color:#f92672">+=</span> n_t_[trial]; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> dt_ <span style="color:#f92672">=</span> dt; +</span></span><span style="display:flex;"><span> n_x_ <span style="color:#f92672">=</span> n_x; +</span></span><span style="display:flex;"><span> n_u_ <span style="color:#f92672">=</span> u_train.at(<span style="color:#ae81ff">0</span>).n_rows; +</span></span><span style="display:flex;"><span> n_y_ <span style="color:#f92672">=</span> z_train.at(<span style="color:#ae81ff">0</span>).n_rows; +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">=</span> n_h; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// dimensionality check for eventual block-hankel data matrix +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t len <span style="color:#f92672">=</span> n_t_tot_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (len <span style="color:#f92672">&lt;</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> (n_u_ <span style="color:#f92672">+</span> n_y_))) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Dataset problem! More rows than columns in block-hankel data &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;matrix: 2*(n_u+n_y)*n_h &gt; data-length! Need higher data-length or &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;lower n_h.&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> fit_ <span style="color:#f92672">=</span> Fit(n_u_, n_x_, n_y_, dt_); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> u_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(u_train); +</span></span><span style="display:flex;"><span> z_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(z_train); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>d.is_finite() <span style="color:#f92672">||</span> (d.n_rows <span style="color:#f92672">!=</span> n_y_)) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): implement least-square solution for impulse response with +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// a second input of ones. Data-driven way of accounting for offset *not* +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// driven by an input. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// For now, calculate output bias (d) as the +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// output wherever the stimulus has not been on for some amount of time. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// convolve u with rectangle and take all samples. This is a reasonable +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// approach, since often when autonomous systems are fit (i.e., systems with +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// no input), they will subtract off the mean of the output. This +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// essentially amounts to setting output bias to the mean of the output when +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// there is no stimulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t t_silence <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.1</span>; +</span></span><span style="display:flex;"><span> data_t thresh_silence <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.001</span>; +</span></span><span style="display:flex;"><span> CalcD(t_silence, thresh_silence); +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> fit_.set_d(d); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>std<span style="color:#f92672">::</span>tuple<span style="color:#f92672">&lt;</span>Fit, Vector<span style="color:#f92672">&gt;</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Run(SSIDWt ssid_wt) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// the weight on minimizing dc I/O gain only works for gaussian, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// and hopefully not necessary with appropriate dataset. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t wt_dc <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// std::cout &lt;&lt; &#34;creating hankel mat\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> CreateHankelDataMat(); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// std::cout &lt;&lt; &#34;decomposing data\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> DecomposeData(); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// std::cout &lt;&lt; &#34;calculating svd\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> CalcSVD(ssid_wt); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// std::cout &lt;&lt; &#34;solving for params\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Solve(wt_dc); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// std::cout &lt;&lt; &#34;fin\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">return</span> std<span style="color:#f92672">::</span>make_tuple(fit_, s_); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>CalcD(data_t t_silence, data_t thresh_silence) { +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">d</span>(z_.at(<span style="color:#ae81ff">0</span>).n_rows, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">win</span>(<span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(t_silence <span style="color:#f92672">/</span> dt_), fill<span style="color:#f92672">::</span>ones); +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">sum_z_silence</span>(n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">n_silence</span>(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> u_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// find silent samples +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// start by convolving with +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector sum_u <span style="color:#f92672">=</span> vectorise(sum(abs(u_.at(trial)), <span style="color:#ae81ff">0</span>)); +</span></span><span style="display:flex;"><span> Vector u_conv <span style="color:#f92672">=</span> conv(sum_u, win, <span style="color:#e6db74">&#34;same&#34;</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// get only the samples that are silent... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>uvec ubi_silence <span style="color:#f92672">=</span> find(u_conv <span style="color:#f92672">&lt;=</span> thresh_silence); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (ubi_silence.n_elem <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> sum_z_silence <span style="color:#f92672">+=</span> arma<span style="color:#f92672">::</span>sum(z_.at(trial).cols(ubi_silence), <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> n_silence <span style="color:#f92672">+=</span> ubi_silence.n_elem; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (n_silence <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> d <span style="color:#f92672">=</span> sum_z_silence <span style="color:#f92672">/</span> n_silence; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> fit_.set_d(d); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>CreateHankelDataMat() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// temporary copy of data +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">z</span>(n_y_, n_t_tot_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">u</span>(n_u_, n_t_tot_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">so_far</span>(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t trial <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; trial <span style="color:#f92672">&lt;</span> z_.size(); trial<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> z.submat(<span style="color:#ae81ff">0</span>, so_far, n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, so_far <span style="color:#f92672">+</span> n_t_.at(trial) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> z_.at(trial); +</span></span><span style="display:flex;"><span> u.submat(<span style="color:#ae81ff">0</span>, so_far, n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, so_far <span style="color:#f92672">+</span> n_t_.at(trial) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> u_.at(trial); +</span></span><span style="display:flex;"><span> so_far <span style="color:#f92672">+=</span> n_t_.at(trial); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// remove output bias +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.each_col() <span style="color:#f92672">-=</span> fit_.d(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// calculate I/O gain @ DC while data in convenient form +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> g_dc_ <span style="color:#f92672">=</span> z <span style="color:#f92672">*</span> pinv(u); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// std::cout &lt;&lt; &#34;G0_data = &#34; &lt;&lt; g_dc_ &lt;&lt; &#34;\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create hankel data matrix +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t len <span style="color:#f92672">=</span> z.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// data length in hankel mat +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// block-hankel data matrix +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> D_ <span style="color:#f92672">=</span> Matrix(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> (n_u_ <span style="color:#f92672">+</span> n_y_), len, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// past input +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> u_p <span style="color:#f92672">=</span> D_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, len <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// future input +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> u_f <span style="color:#f92672">=</span> D_.submat(n_h_ <span style="color:#f92672">*</span> n_u_, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, len <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// past output +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> y_p <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> D_.submat(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, len <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// future output +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> y_f <span style="color:#f92672">=</span> D_.submat(n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_), <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> (n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, len <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t idx <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> len; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t kk <span style="color:#f92672">=</span> k; kk <span style="color:#f92672">&lt;</span> (n_h_ <span style="color:#f92672">+</span> k); kk<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> u_p.col(k).subvec(idx, idx <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> u.col(kk); +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">+=</span> n_u_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t kk <span style="color:#f92672">=</span> (n_h_ <span style="color:#f92672">+</span> k); kk <span style="color:#f92672">&lt;</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">+</span> k); kk<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> u_f.col(k).subvec(idx, idx <span style="color:#f92672">+</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> u.col(kk); +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">+=</span> n_u_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t kk <span style="color:#f92672">=</span> k; kk <span style="color:#f92672">&lt;</span> (n_h_ <span style="color:#f92672">+</span> k); kk<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> y_p.col(k).subvec(idx, idx <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> z.col(kk); +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">+=</span> n_y_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t kk <span style="color:#f92672">=</span> (n_h_ <span style="color:#f92672">+</span> k); kk <span style="color:#f92672">&lt;</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">+</span> k); kk<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> y_f.col(k).subvec(idx, idx <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> z.col(kk); +</span></span><span style="display:flex;"><span> idx <span style="color:#f92672">+=</span> n_y_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> D_ <span style="color:#f92672">/=</span> sqrt(<span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>(len)); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// template &lt;typename Fit&gt; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// void SSID&lt;Fit&gt;::DecomposeData() { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // do LQ decomp instead of calculating covariance expensive way +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // Note that &#34;R&#34; in van Overschee is lower-triangular (L), not &#34;R&#34; in QR +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // decomp. Very confusing. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Matrix q_t; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// lq(L_, q_t, D_); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // van Overschee zeros out the other elements. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// L_ = trimatl(L_); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>CalcSVD(SSIDWt wt) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// submats that will be needed: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> R_14_14 <span style="color:#f92672">=</span> L_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_11_14 <span style="color:#f92672">=</span> L_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_11_13 <span style="color:#f92672">=</span> L_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_23_13 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> L_.submat(n_h_ <span style="color:#f92672">*</span> n_u_, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_44_14 <span style="color:#f92672">=</span> L_.submat(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">*</span> n_h_, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_44_13 <span style="color:#f92672">=</span> L_.submat(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">*</span> n_h_, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_44 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> L_.submat(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">*</span> n_h_, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">*</span> n_h_, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_56_14 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> L_.submat(n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_), <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix Lup_Luf_Lyp <span style="color:#f92672">=</span> R_56_14 <span style="color:#f92672">*</span> pinv(R_14_14); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> Lup <span style="color:#f92672">=</span> Lup_Luf_Lyp.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> Luf <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> Lup_Luf_Lyp.submat(<span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> n_u_, n_h_ <span style="color:#f92672">*</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> Lyp <span style="color:#f92672">=</span> Lup_Luf_Lyp.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_, n_h_ <span style="color:#f92672">*</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// aka: R_f +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix R_56_16 <span style="color:#f92672">=</span> L_.submat(n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_), <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> (n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, L_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// from van Overschee subid.m: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> Matrix U; +</span></span><span style="display:flex;"><span> Matrix V; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">switch</span> (wt) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">case</span> kSSIDNone: { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// No weighting. (what van Overschee calls &#34;N4SID&#34;) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix O_k_sans_Qt <span style="color:#f92672">=</span> Lup <span style="color:#f92672">*</span> R_11_14 <span style="color:#f92672">+</span> Lyp <span style="color:#f92672">*</span> R_44_14; +</span></span><span style="display:flex;"><span> arma<span style="color:#f92672">::</span>svd(U, s_, V, O_k_sans_Qt, <span style="color:#e6db74">&#34;std&#34;</span>); +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">break</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">case</span> kSSIDMOESP: { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// MOESP weighting +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// This is what they use in the &#34;robust&#34; algorithm van Overschee, de Moor +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// 1996 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix Pi <span style="color:#f92672">=</span> Matrix(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_, fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">-</span> +</span></span><span style="display:flex;"><span> R_23_13.t() <span style="color:#f92672">*</span> inv(R_23_13 <span style="color:#f92672">*</span> R_23_13.t()) <span style="color:#f92672">*</span> R_23_13; +</span></span><span style="display:flex;"><span> Matrix O_k_ortho_Uf_sans_Qt <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> join_horiz((Lup <span style="color:#f92672">*</span> R_11_13 <span style="color:#f92672">+</span> Lyp <span style="color:#f92672">*</span> R_44_13) <span style="color:#f92672">*</span> Pi, Lyp <span style="color:#f92672">*</span> R_44); +</span></span><span style="display:flex;"><span> svd(U, s_, V, O_k_ortho_Uf_sans_Qt, <span style="color:#e6db74">&#34;std&#34;</span>); +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">break</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">case</span> kSSIDCVA: { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// CVA weighting +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// See van Overschee&#39;s matlab code (subid.m): +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix Pi <span style="color:#f92672">=</span> Matrix(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_, fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">-</span> +</span></span><span style="display:flex;"><span> R_23_13.t() <span style="color:#f92672">*</span> inv(R_23_13 <span style="color:#f92672">*</span> R_23_13.t()) <span style="color:#f92672">*</span> R_23_13; +</span></span><span style="display:flex;"><span> Matrix O_k_ortho_Uf_sans_Qt <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> join_horiz((Lup <span style="color:#f92672">*</span> R_11_13 <span style="color:#f92672">+</span> Lyp <span style="color:#f92672">*</span> R_44_13) <span style="color:#f92672">*</span> Pi, Lyp <span style="color:#f92672">*</span> R_44); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix inv_w1; +</span></span><span style="display:flex;"><span> Matrix qt1; +</span></span><span style="display:flex;"><span> lq(inv_w1, qt1, R_56_16); <span style="color:#75715e">// lq decomp of R_f (future output data) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> inv_w1 <span style="color:#f92672">=</span> trimatl(inv_w1); +</span></span><span style="display:flex;"><span> inv_w1 <span style="color:#f92672">=</span> inv_w1.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y_ <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_y_ <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> Matrix w_o_w <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>solve( +</span></span><span style="display:flex;"><span> inv_w1, O_k_ortho_Uf_sans_Qt); <span style="color:#75715e">// alternatively +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// pinv(inv_W1)*O_k_ortho_Uf_sans_Qt +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> svd(U, s_, V, w_o_w, <span style="color:#e6db74">&#34;std&#34;</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> U <span style="color:#f92672">=</span> inv_w1 <span style="color:#f92672">*</span> U; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Truncate to model order (heart of ssid method) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> s_hat <span style="color:#f92672">=</span> s_.subvec(<span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> Matrix diag_sqrt_s <span style="color:#f92672">=</span> diagmat(sqrt(s_hat)); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> u_hat <span style="color:#f92672">=</span> U.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, U.n_rows <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// get extended observability and controllability mats +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> ext_obs_t_ <span style="color:#f92672">=</span> u_hat <span style="color:#f92672">*</span> diag_sqrt_s; <span style="color:#75715e">// extended observability matrix +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Solve(data_t wt_dc) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// required submats +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> R_56_14 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> L_.submat(n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_), <span style="color:#ae81ff">0</span>, n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_23_15 <span style="color:#f92672">=</span> L_.submat(n_h_ <span style="color:#f92672">*</span> n_u_, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_66_15 <span style="color:#f92672">=</span> L_.submat(n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">+</span> n_y_, <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> (n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_55_15 <span style="color:#f92672">=</span> L_.submat(n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_), <span style="color:#ae81ff">0</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> R_56_15 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> L_.submat(n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_), <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_h_ <span style="color:#f92672">*</span> (n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Solve for params using appropriate algorithm: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// robust deterministic/stochastic algorithm in van Overschee 1996 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// algorithm that the authors say &#34;works&#34; in practice. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> ext_obs_tm1 <span style="color:#f92672">=</span> ext_obs_t_.submat( +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, ext_obs_t_.n_rows <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> n_y_, +</span></span><span style="display:flex;"><span> ext_obs_t_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); <span style="color:#75715e">// extended observability matrix +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// This is what textbook (1996) says: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// HOWEVER, do not know why but have to fill the last place with zeros like +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// authors&#39; matlab implementation (see `subid.m`) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Otherwise, get ridiculous covariances (although A,C estimates are close to +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// same...) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix Tr <span style="color:#f92672">=</span> join_vert( +</span></span><span style="display:flex;"><span> join_horiz(pinv(ext_obs_t_) <span style="color:#f92672">*</span> R_56_14, Matrix(n_x_, n_y_, fill<span style="color:#f92672">::</span>zeros)), +</span></span><span style="display:flex;"><span> R_23_15); +</span></span><span style="display:flex;"><span> Matrix Tl <span style="color:#f92672">=</span> join_vert(pinv(ext_obs_tm1) <span style="color:#f92672">*</span> R_66_15, R_55_15); +</span></span><span style="display:flex;"><span> Matrix S <span style="color:#f92672">=</span> Tl <span style="color:#f92672">*</span> pinv(Tr); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Use alternative in van Overschee 1996, p. 129. Apparently, should ensure +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// stability. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> fit_.set_C(ext_obs_t_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, ext_obs_t_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> Matrix ext_obs_t_p1 <span style="color:#f92672">=</span> join_vert( +</span></span><span style="display:flex;"><span> ext_obs_t_.submat(n_y_, <span style="color:#ae81ff">0</span>, ext_obs_t_.n_rows <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, ext_obs_t_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), +</span></span><span style="display:flex;"><span> Matrix(n_y_, ext_obs_t_.n_cols, fill<span style="color:#f92672">::</span>zeros)); +</span></span><span style="display:flex;"><span> fit_.set_A(pinv(ext_obs_t_) <span style="color:#f92672">*</span> ext_obs_t_p1); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// At this point, van Overschee &amp; de Moor suggest re-calculating ext_obs_t_, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// ext_obs_tm1 from (A, C) because it was just an approximation. This is +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> RecomputeExtObs(); +</span></span><span style="display:flex;"><span> ext_obs_tm1 <span style="color:#f92672">=</span> ext_obs_t_.submat( +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, ext_obs_t_.n_rows <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> n_y_, +</span></span><span style="display:flex;"><span> ext_obs_t_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); <span style="color:#75715e">// extended observability matrix +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Tl <span style="color:#f92672">=</span> join_vert(pinv(ext_obs_tm1) <span style="color:#f92672">*</span> R_66_15, R_55_15); +</span></span><span style="display:flex;"><span> Tr <span style="color:#f92672">=</span> join_vert( +</span></span><span style="display:flex;"><span> join_horiz(pinv(ext_obs_t_) <span style="color:#f92672">*</span> R_56_14, Matrix(n_x_, n_y_, fill<span style="color:#f92672">::</span>zeros)), +</span></span><span style="display:flex;"><span> R_23_15); +</span></span><span style="display:flex;"><span> S <span style="color:#f92672">=</span> Tl <span style="color:#f92672">*</span> pinv(Tr); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix Lcurly <span style="color:#f92672">=</span> S.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> pinv(ext_obs_t_); +</span></span><span style="display:flex;"><span> Matrix Mcurly <span style="color:#f92672">=</span> pinv(ext_obs_tm1); +</span></span><span style="display:flex;"><span> Matrix Pcurly <span style="color:#f92672">=</span> Tl <span style="color:#f92672">-</span> Lcurly <span style="color:#f92672">*</span> R_56_15; +</span></span><span style="display:flex;"><span> Vector Pvec <span style="color:#f92672">=</span> vectorise(Pcurly); +</span></span><span style="display:flex;"><span> Matrix Qcurly <span style="color:#f92672">=</span> R_23_15; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Identify [D; B], assuming D=0 and ensuring DC gain is correct +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">sum_QcurlyT_kron_Ncurly</span>( +</span></span><span style="display:flex;"><span> (n_h_ <span style="color:#f92672">*</span> (<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> n_u_ <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">+</span> n_y_) <span style="color:#f92672">*</span> (n_y_ <span style="color:#f92672">+</span> n_x_), n_u_ <span style="color:#f92672">*</span> (n_y_ <span style="color:#f92672">+</span> n_x_), +</span></span><span style="display:flex;"><span> fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">eye_ext_obs_tm1</span>(n_y_ <span style="color:#f92672">+</span> ext_obs_tm1.n_rows, n_y_ <span style="color:#f92672">+</span> ext_obs_tm1.n_cols, +</span></span><span style="display:flex;"><span> fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> eye_ext_obs_tm1.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> ext_obs_tm1; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// van Overschee (1996) p. 126 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix N1_Tl <span style="color:#f92672">=</span> <span style="color:#f92672">-</span>Lcurly; +</span></span><span style="display:flex;"><span> N1_Tl.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, N1_Tl.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+=</span> +</span></span><span style="display:flex;"><span> join_horiz(Matrix(n_x_, n_y_, fill<span style="color:#f92672">::</span>zeros), Mcurly); +</span></span><span style="display:flex;"><span> N1_Tl.submat(n_x_, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">+=</span> +</span></span><span style="display:flex;"><span> Matrix(n_y_, n_y_, fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">Nk_Tl</span>(N1_Tl.n_rows, N1_Tl.n_cols, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix N_k; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> n_h_; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> Qcurly_k <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> Qcurly.submat(n_u_ <span style="color:#f92672">*</span> k, <span style="color:#ae81ff">0</span>, n_u_ <span style="color:#f92672">*</span> (k <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, Qcurly.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Nk_Tl.zeros(); +</span></span><span style="display:flex;"><span> Nk_Tl.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, Nk_Tl.n_cols <span style="color:#f92672">-</span> k <span style="color:#f92672">*</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> N1_Tl.submat(<span style="color:#ae81ff">0</span>, k <span style="color:#f92672">*</span> n_y_, N1_Tl.n_rows <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, N1_Tl.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> N_k <span style="color:#f92672">=</span> Nk_Tl <span style="color:#f92672">*</span> eye_ext_obs_tm1; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> sum_QcurlyT_kron_Ncurly <span style="color:#f92672">+=</span> kron(Qcurly_k.t(), N_k); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix err_vec; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (wt_dc <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Constraints enforced by weighted least squares +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Reference: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Excited Industrial Systems. Conference in Decision and Control. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// constraint 1: assume D=0 --&gt; remove the components for Dvec (this is +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// actually a hard constraint in that it ignores D) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix sum_QcurlyT_kron_Ncurly_db <span style="color:#f92672">=</span> sum_QcurlyT_kron_Ncurly; +</span></span><span style="display:flex;"><span> sum_QcurlyT_kron_Ncurly <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ <span style="color:#f92672">*</span> n_u_); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t kkk <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; k <span style="color:#f92672">&lt;</span> (n_u_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> size_t start_idx <span style="color:#f92672">=</span> k <span style="color:#f92672">*</span> (n_y_ <span style="color:#f92672">+</span> n_x_) <span style="color:#f92672">-</span> n_x_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t kk <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; kk <span style="color:#f92672">&lt;</span> n_x_; kk<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> sum_QcurlyT_kron_Ncurly.col(kkk) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> sum_QcurlyT_kron_Ncurly_db.col(start_idx <span style="color:#f92672">+</span> kk); +</span></span><span style="display:flex;"><span> kkk<span style="color:#f92672">++</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// constraint 2: Make sure DC I/O gain is correct +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix b_to_g0 <span style="color:#f92672">=</span> fit_.C() <span style="color:#f92672">*</span> inv(Matrix(n_x_, n_x_, fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">-</span> fit_.A()); +</span></span><span style="display:flex;"><span> Matrix Pvec_Gvec <span style="color:#f92672">=</span> join_vert(Pvec, vectorise(g_dc_)); +</span></span><span style="display:flex;"><span> Matrix eye_kron_b_to_g0 <span style="color:#f92672">=</span> kron(Matrix(n_u_, n_u_, fill<span style="color:#f92672">::</span>eye), b_to_g0); +</span></span><span style="display:flex;"><span> Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// WEIGHTED LS +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Important in practice because I care a lot about at least getting the DC +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// gain correct. Put x weight on minimizing error at DC, relative to others +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">w</span>(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, +</span></span><span style="display:flex;"><span> sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Make weight on minimizing DC error immense so at least that +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// should be nailed. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t start_row <span style="color:#f92672">=</span> sum_QcurlyT_kron_Ncurly.n_rows; +</span></span><span style="display:flex;"><span> size_t start_col <span style="color:#f92672">=</span> sum_QcurlyT_kron_Ncurly.n_rows; +</span></span><span style="display:flex;"><span> size_t stop_row <span style="color:#f92672">=</span> w.n_rows <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t stop_col <span style="color:#f92672">=</span> w.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// weight with data length? +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> w.submat(start_row, start_col, stop_row, stop_col) <span style="color:#f92672">*=</span> wt_dc; +</span></span><span style="display:flex;"><span> Vector b_vec <span style="color:#f92672">=</span> inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() <span style="color:#f92672">*</span> w <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> sum_QcurlyT_kron_Ncurly_b_to_g0) <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> sum_QcurlyT_kron_Ncurly_b_to_g0.t() <span style="color:#f92672">*</span> w <span style="color:#f92672">*</span> Pvec_Gvec; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Calculate residuals and their cov. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Because I&#39;ve added constraints, I need to re-calculate the right term +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// with b_vec instead of how van Overschee do in final algorithm. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> err_vec <span style="color:#f92672">=</span> Pvec <span style="color:#f92672">-</span> sum_QcurlyT_kron_Ncurly <span style="color:#f92672">*</span> b_vec; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// default way: *no* constraint on G0 or D=0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector db_vec <span style="color:#f92672">=</span> pinv(sum_QcurlyT_kron_Ncurly) <span style="color:#f92672">*</span> Pvec; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus) n.b., this gets thrown away... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> fit_.set_B(Matrix(db_vec.memptr() <span style="color:#f92672">+</span> (n_u_ <span style="color:#f92672">*</span> n_y_), n_x_, n_u_)); +</span></span><span style="display:flex;"><span> err_vec <span style="color:#f92672">=</span> Pvec <span style="color:#f92672">-</span> sum_QcurlyT_kron_Ncurly <span style="color:#f92672">*</span> db_vec; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): Something is wrong with the error calculation above. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Use the way van overschee does it in `subid.m` +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// WARNING: this ignores any above constraints, so Q, R will be approximate... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix err <span style="color:#f92672">=</span> Tl <span style="color:#f92672">-</span> S <span style="color:#f92672">*</span> Tr; +</span></span><span style="display:flex;"><span> Matrix cov_err <span style="color:#f92672">=</span> err <span style="color:#f92672">*</span> err.t(); +</span></span><span style="display:flex;"><span> fit_.set_Q(cov_err.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, n_x_ <span style="color:#f92672">+</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> Fit<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>RecomputeExtObs() { +</span></span><span style="display:flex;"><span> ext_obs_t_.submat(<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, ext_obs_t_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> fit_.C(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>; k <span style="color:#f92672">&lt;</span> (n_h_ <span style="color:#f92672">+</span> <span style="color:#ae81ff">1</span>); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> ext_obs_t_.submat((k <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> n_y_, <span style="color:#ae81ff">0</span>, k <span style="color:#f92672">*</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, ext_obs_t_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> ext_obs_t_.submat((k <span style="color:#f92672">-</span> <span style="color:#ae81ff">2</span>) <span style="color:#f92672">*</span> n_y_, <span style="color:#ae81ff">0</span>, (k <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> n_y_ <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>, +</span></span><span style="display:flex;"><span> ext_obs_t_.n_cols <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> +</span></span><span style="display:flex;"><span> fit_.A(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_fit_ssidh">ldsCtrlEst_h/lds_fit_ssid.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian_8h/index.html b/docs/docs/api/files/lds__gaussian_8h/index.html new file mode 100644 index 00000000..8d5ad73b --- /dev/null +++ b/docs/docs/api/files/lds__gaussian_8h/index.html @@ -0,0 +1,316 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="glds namespace"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_gaussian.h"> + <meta property="og:description" content="glds namespace"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_gaussian.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_gaussian.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussianh">ldsCtrlEst_h/lds_gaussian.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_gaussianh"> + ldsCtrlEst_h/lds_gaussian.h + <a class="anchor" href="#ldsctrlest_hlds_gaussianh">#</a> +</h1> +<p><code>glds</code> namespace <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations (<code>[lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)</code>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_GAUSSIAN_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_GAUSSIAN_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> gaussian { +</span></span><span style="display:flex;"><span><span style="color:#75715e">// insert any Gaussian-specific things here... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace gaussian +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussianh">ldsCtrlEst_h/lds_gaussian.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian__ctrl_8h/index.html b/docs/docs/api/files/lds__gaussian__ctrl_8h/index.html new file mode 100644 index 00000000..3f801588 --- /dev/null +++ b/docs/docs/api/files/lds__gaussian__ctrl_8h/index.html @@ -0,0 +1,375 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS Controller."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_gaussian_ctrl.h"> + <meta property="og:description" content="GLDS Controller."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_gaussian_ctrl.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_gaussian_ctrl.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_ctrlh">ldsCtrlEst_h/lds_gaussian_ctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_gaussian_ctrlh"> + ldsCtrlEst_h/lds_gaussian_ctrl.h + <a class="anchor" href="#ldsctrlest_hlds_gaussian_ctrlh">#</a> +</h1> +<p>GLDS Controller. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/">lds::gaussian::Controller</a></strong> <br>Gaussian-observation <a href="">Controller</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/">lds::gaussian::Controller</a>). It inherits functionality from the underlying GLDS model type (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">lds::gaussian::System</a>), including state estimation.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_gaussian.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_gaussian_sys.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_ctrl.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> gaussian { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Controller</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> set_y_ref(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> y_ref) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> Reassign(y_ref_,y_ref); +</span></span><span style="display:flex;"><span> cx_ref_ <span style="color:#f92672">=</span> y_ref <span style="color:#f92672">-</span> sys_.d(); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure base class template methods available +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Controller; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Control; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>ControlOutputReference; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>sys; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_sys; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_tau_awu; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Reset; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Print; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace gaussian +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_ctrlh">ldsCtrlEst_h/lds_gaussian_ctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian__fit_8h/index.html b/docs/docs/api/files/lds__gaussian__fit_8h/index.html new file mode 100644 index 00000000..297c7577 --- /dev/null +++ b/docs/docs/api/files/lds__gaussian__fit_8h/index.html @@ -0,0 +1,353 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_gaussian_fit.h"> + <meta property="og:description" content="GLDS fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_gaussian_fit.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_gaussian_fit.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_fith">ldsCtrlEst_h/lds_gaussian_fit.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_gaussian_fith"> + ldsCtrlEst_h/lds_gaussian_fit.h + <a class="anchor" href="#ldsctrlest_hlds_gaussian_fith">#</a> +</h1> +<p>GLDS fit type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">lds::gaussian::Fit</a></strong> <br>GLDS <a href="">Fit</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_GAUSSIAN_FIT_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_gaussian.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// fit type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> gaussian { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Fit</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>Fit { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> Fit() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">override</span> { <span style="color:#66d9ef">return</span> R_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_R</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> Reassign(R_, R); +</span></span><span style="display:flex;"><span> ForceSymPD(R_); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> View <span style="color:#a6e22e">h</span>(Matrix<span style="color:#f92672">&amp;</span> y, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> x, size_t t) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> y.col(t) <span style="color:#f92672">=</span> C_ <span style="color:#f92672">*</span> x.col(t) <span style="color:#f92672">+</span> d_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> y.col(t); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>}; <span style="color:#75715e">// namespace gaussian +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_fith">ldsCtrlEst_h/lds_gaussian_fit.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian__fit__em_8h/index.html b/docs/docs/api/files/lds__gaussian__fit__em_8h/index.html new file mode 100644 index 00000000..c017915a --- /dev/null +++ b/docs/docs/api/files/lds__gaussian__fit__em_8h/index.html @@ -0,0 +1,349 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS E-M fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_gaussian_fit_em.h"> + <meta property="og:description" content="GLDS E-M fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_gaussian_fit_em.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_gaussian_fit_em.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_fit_emh">ldsCtrlEst_h/lds_gaussian_fit_em.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_gaussian_fit_emh"> + ldsCtrlEst_h/lds_gaussian_fit_em.h + <a class="anchor" href="#ldsctrlest_hlds_gaussian_fit_emh">#</a> +</h1> +<p>GLDS E-M fit type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/">lds::gaussian::FitEM</a></strong> <br>GLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">Fit</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (<code>lds::gaussian::emFit_t</code>).</p> +<p>References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).</p> +<p>[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit_em.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_gaussian_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> gaussian { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">FitEM</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>EM; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> MaximizeOutput() <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">MaximizeMeasurement</span>() <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">RecurseKe</span>(Matrix<span style="color:#f92672">&amp;</span> Ke, Cube<span style="color:#f92672">&amp;</span> P_pre, Cube<span style="color:#f92672">&amp;</span> P_post, size_t t) <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace gaussian +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_fit_emh">ldsCtrlEst_h/lds_gaussian_fit_em.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html b/docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html new file mode 100644 index 00000000..a2962bcf --- /dev/null +++ b/docs/docs/api/files/lds__gaussian__fit__ssid_8h/index.html @@ -0,0 +1,349 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS SSID fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_gaussian_fit_ssid.h"> + <meta property="og:description" content="GLDS SSID fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_gaussian_fit_ssid.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_gaussian_fit_ssid.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_fit_ssidh">ldsCtrlEst_h/lds_gaussian_fit_ssid.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_gaussian_fit_ssidh"> + ldsCtrlEst_h/lds_gaussian_fit_ssid.h + <a class="anchor" href="#ldsctrlest_hlds_gaussian_fit_ssidh">#</a> +</h1> +<p>GLDS SSID fit type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/">lds::gaussian::FitSSID</a></strong> <br>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for GLDS.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (<code>lds::gaussian::ssidFit_t</code>).</p> +<p>References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit_ssid.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_gaussian_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> gaussian { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">FitSSID</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>SSID; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Run; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>CreateHankelDataMat; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>CalcSVD; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>Solve; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">DecomposeData</span>() <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">SolveVanOverschee</span>(); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace gaussian +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_fit_ssidh">ldsCtrlEst_h/lds_gaussian_fit_ssid.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian__sctrl_8h/index.html b/docs/docs/api/files/lds__gaussian__sctrl_8h/index.html new file mode 100644 index 00000000..8fdfae2f --- /dev/null +++ b/docs/docs/api/files/lds__gaussian__sctrl_8h/index.html @@ -0,0 +1,373 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS switched controller type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_gaussian_sctrl.h"> + <meta property="og:description" content="GLDS switched controller type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_gaussian_sctrl.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_gaussian_sctrl.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_sctrlh">ldsCtrlEst_h/lds_gaussian_sctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_gaussian_sctrlh"> + ldsCtrlEst_h/lds_gaussian_sctrl.h + <a class="anchor" href="#ldsctrlest_hlds_gaussian_sctrlh">#</a> +</h1> +<p>GLDS switched controller type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/">lds::gaussian::SwitchedController</a></strong> <br>Gaussian-observation <a href="">SwitchedController</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/">lds::gaussian::SwitchedController</a>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// controller type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_gaussian_ctrl.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// switched controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_sctrl.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> gaussian { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SwitchedController</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> set_y_ref(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> y_ref) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> Reassign(y_ref_, y_ref); +</span></span><span style="display:flex;"><span> cx_ref_ <span style="color:#f92672">=</span> y_ref <span style="color:#f92672">-</span> sys_.d(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure base class template methods available +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>SwitchedController; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Switch; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Control; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>ControlOutputReference; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>sys; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_tau_awu; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Reset; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Print; +</span></span><span style="display:flex;"><span>}; <span style="color:#75715e">// SwitchedController +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace gaussian +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_sctrlh">ldsCtrlEst_h/lds_gaussian_sctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian__sys_8cpp/index.html b/docs/docs/api/files/lds__gaussian__sys_8cpp/index.html new file mode 100644 index 00000000..f14c089f --- /dev/null +++ b/docs/docs/api/files/lds__gaussian__sys_8cpp/index.html @@ -0,0 +1,329 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS base type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="src/lds_gaussian_sys.cpp"> + <meta property="og:description" content="GLDS base type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>src/lds_gaussian_sys.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>src/lds_gaussian_sys.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_gaussian_syscpp">src/lds_gaussian_sys.cpp</a> + <ul> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="srclds_gaussian_syscpp"> + src/lds_gaussian_sys.cpp + <a class="anchor" href="#srclds_gaussian_syscpp">#</a> +</h1> +<p>GLDS base type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (<code>lds::gaussian::sys_t</code>). It inherits functionality from the underlying linear dynamical system (<code>lds::sys_t</code>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst_h/lds_gaussian_sys.h&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span>lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, +</span></span><span style="display:flex;"><span> data_t q0, data_t r0) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> lds<span style="color:#f92672">::</span>System(n_u, n_x, n_y, dt, p0, q0) { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> R_.zeros(n_y, n_y); +</span></span><span style="display:flex;"><span> R_.diag().fill(r0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> do_recurse_Ke_<span style="color:#f92672">=</span>true; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// recursively estimate Ke +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">void</span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>RecurseKe() { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>do_recurse_Ke_) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// predict covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P_ <span style="color:#f92672">=</span> A_ <span style="color:#f92672">*</span> P_ <span style="color:#f92672">*</span> A_.t() <span style="color:#f92672">+</span> Q_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// calc Kalman gain +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Ke_ <span style="color:#f92672">=</span> P_ <span style="color:#f92672">*</span> C_.t() <span style="color:#f92672">*</span> inv_sympd(C_ <span style="color:#f92672">*</span> P_ <span style="color:#f92672">*</span> C_.t() <span style="color:#f92672">+</span> R_); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Reference: Ghahramani et Hinton (1996) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P_ <span style="color:#f92672">=</span> P_ <span style="color:#f92672">-</span> Ke_ <span style="color:#f92672">*</span> C_ <span style="color:#f92672">*</span> P_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_adapt_m) { +</span></span><span style="display:flex;"><span> P_m_ <span style="color:#f92672">+=</span> Q_m_; <span style="color:#75715e">// A_m = I (i.e., random walk) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Ke_m_ <span style="color:#f92672">=</span> P_m_ <span style="color:#f92672">*</span> C_.t() <span style="color:#f92672">*</span> inv_sympd(C_ <span style="color:#f92672">*</span> P_m_ <span style="color:#f92672">*</span> C_.t() <span style="color:#f92672">+</span> R_); +</span></span><span style="display:flex;"><span> P_m_ <span style="color:#f92672">=</span> P_m_ <span style="color:#f92672">-</span> Ke_m_ <span style="color:#f92672">*</span> C_ <span style="color:#f92672">*</span> P_m_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// Simulate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> lds<span style="color:#f92672">::</span>Vector<span style="color:#f92672">&amp;</span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>Simulate(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_tm1){ +</span></span><span style="display:flex;"><span> f(u_tm1, true);<span style="color:#75715e">//simulate dynamics with noise added +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> h();<span style="color:#75715e">//output +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z_ <span style="color:#f92672">=</span> y_ <span style="color:#f92672">+</span> arma<span style="color:#f92672">::</span>mvnrnd(Vector(n_y_).fill(<span style="color:#ae81ff">0</span>), R_);<span style="color:#75715e">//measure +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">return</span> z_; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>Print() { +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>Print(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;R: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> R_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span>} +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_gaussian_syscpp">src/lds_gaussian_sys.cpp</a> + <ul> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__gaussian__sys_8h/index.html b/docs/docs/api/files/lds__gaussian__sys_8h/index.html new file mode 100644 index 00000000..de1f5c23 --- /dev/null +++ b/docs/docs/api/files/lds__gaussian__sys_8h/index.html @@ -0,0 +1,387 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="GLDS base type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_gaussian_sys.h"> + <meta property="og:description" content="GLDS base type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_gaussian_sys.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_gaussian_sys.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_sysh">ldsCtrlEst_h/lds_gaussian_sys.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_gaussian_sysh"> + ldsCtrlEst_h/lds_gaussian_sys.h + <a class="anchor" href="#ldsctrlest_hlds_gaussian_sysh">#</a> +</h1> +<p>GLDS base type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">lds::gaussian::System</a></strong> <br>Gaussian LDS Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (<code>[lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)</code>). It inherits functionality from the underlying linear dynamical system (<code>[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)</code>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_GAUSSIAN_SYS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_gaussian.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_sys.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> gaussian { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">System</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>System { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> System() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> System(std<span style="color:#f92672">::</span>size_t n_u, std<span style="color:#f92672">::</span>size_t n_x, std<span style="color:#f92672">::</span>size_t n_y, data_t dt, +</span></span><span style="display:flex;"><span> data_t p0 <span style="color:#f92672">=</span> kDefaultP0, data_t q0 <span style="color:#f92672">=</span> kDefaultQ0, +</span></span><span style="display:flex;"><span> data_t r0 <span style="color:#f92672">=</span> kDefaultR0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> Simulate(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_tm1) <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// get methods +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> R_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set methods +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Q</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q) { +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>set_Q(Q); +</span></span><span style="display:flex;"><span> do_recurse_Ke_ <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_R</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R) { +</span></span><span style="display:flex;"><span> Reassign(R_, R); +</span></span><span style="display:flex;"><span> do_recurse_Ke_ <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Ke</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Ke) { +</span></span><span style="display:flex;"><span> Reassign(Ke_, Ke); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if users have set Ke, they must not want to calculate it online. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> do_recurse_Ke_ <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Ke_m</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Ke_m) { +</span></span><span style="display:flex;"><span> Reassign(Ke_m_, Ke_m); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if users have set Ke, they must not want to calculate it online. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> do_recurse_Ke_ <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Print</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> h() <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> cx_ <span style="color:#f92672">=</span> C_ <span style="color:#f92672">*</span> x_; +</span></span><span style="display:flex;"><span> y_ <span style="color:#f92672">=</span> cx_ <span style="color:#f92672">+</span> d_; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">h_</span>(Vector x) <span style="color:#66d9ef">override</span> { <span style="color:#66d9ef">return</span> C_ <span style="color:#f92672">*</span> x <span style="color:#f92672">+</span> d_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">RecurseKe</span>() <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Gaussian-output-specific +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix R_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_recurse_Ke_{}; +</span></span><span style="display:flex;"><span>}; <span style="color:#75715e">// System +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace gaussian +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_gaussian_sysh">ldsCtrlEst_h/lds_gaussian_sys.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson_8h/index.html b/docs/docs/api/files/lds__poisson_8h/index.html new file mode 100644 index 00000000..5f177a82 --- /dev/null +++ b/docs/docs/api/files/lds__poisson_8h/index.html @@ -0,0 +1,320 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="plds namespace"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_poisson.h"> + <meta property="og:description" content="plds namespace"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_poisson.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_poisson.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poissonh">ldsCtrlEst_h/lds_poisson.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_poissonh"> + ldsCtrlEst_h/lds_poisson.h + <a class="anchor" href="#ldsctrlest_hlds_poissonh">#</a> +</h1> +<p><code>plds</code> namespace <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the namespace for linear dynamical systems with Poisson observations (<code>[lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)</code>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_POISSON_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_POISSON_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> poisson { +</span></span><span style="display:flex;"><span><span style="color:#75715e">// TODO(mfbolus): Not sure if defining these as static here makes the most +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// sense. Is there a downside to letting multiple poisson System objects share a +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// common random number generator? +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">static</span> std<span style="color:#f92672">::</span>random_device rd; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">static</span> std<span style="color:#f92672">::</span>mt19937 rng <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>mt19937( +</span></span><span style="display:flex;"><span> rd()); +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace poisson +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poissonh">ldsCtrlEst_h/lds_poisson.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson__ctrl_8h/index.html b/docs/docs/api/files/lds__poisson__ctrl_8h/index.html new file mode 100644 index 00000000..259088ff --- /dev/null +++ b/docs/docs/api/files/lds__poisson__ctrl_8h/index.html @@ -0,0 +1,380 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS controller type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_poisson_ctrl.h"> + <meta property="og:description" content="PLDS controller type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_poisson_ctrl.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_poisson_ctrl.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_ctrlh">ldsCtrlEst_h/lds_poisson_ctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_poisson_ctrlh"> + ldsCtrlEst_h/lds_poisson_ctrl.h + <a class="anchor" href="#ldsctrlest_hlds_poisson_ctrlh">#</a> +</h1> +<p>PLDS controller type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/">lds::poisson::Controller</a></strong> <br>PLDS <a href="">Controller</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system (<code>[lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)</code>). It inherits functionality from the underlying PLDS model type (<code>[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)</code>), including state estimation.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_POISSON_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_POISSON_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_poisson.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// system type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_poisson_sys.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// control type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_ctrl.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> poisson { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Controller</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> set_y_ref(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> y_ref) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> Reassign(y_ref_, y_ref); +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>Limit(y_ref_, kYRefLb, lds<span style="color:#f92672">::</span>kInf); +</span></span><span style="display:flex;"><span> cx_ref_ <span style="color:#f92672">=</span> log(y_ref_) <span style="color:#f92672">-</span> sys_.d(); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure base class template methods available +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Controller; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Control; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>ControlOutputReference; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>sys; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_sys; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_tau_awu; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Reset; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Print; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">constexpr</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kYRefLb <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">1e-4</span>; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace poisson +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_ctrlh">ldsCtrlEst_h/lds_poisson_ctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson__fit_8h/index.html b/docs/docs/api/files/lds__poisson__fit_8h/index.html new file mode 100644 index 00000000..e4cba33c --- /dev/null +++ b/docs/docs/api/files/lds__poisson__fit_8h/index.html @@ -0,0 +1,361 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS base fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_poisson_fit.h"> + <meta property="og:description" content="PLDS base fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_poisson_fit.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_poisson_fit.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_fith">ldsCtrlEst_h/lds_poisson_fit.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_poisson_fith"> + ldsCtrlEst_h/lds_poisson_fit.h + <a class="anchor" href="#ldsctrlest_hlds_poisson_fith">#</a> +</h1> +<p>PLDS base fit type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">lds::poisson::Fit</a></strong> <br>PLDS <a href="">Fit</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_POISSON_FIT_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_POISSON_FIT_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_poisson.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// fit +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> poisson { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Fit</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>Fit { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> Fit() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> lds<span style="color:#f92672">::</span>Fit(n_u, n_x, n_y, dt){}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> View <span style="color:#a6e22e">h</span>(Matrix<span style="color:#f92672">&amp;</span> y, <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> x, size_t t) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> y.col(t) <span style="color:#f92672">=</span> exp(C_ <span style="color:#f92672">*</span> x.col(t) <span style="color:#f92672">+</span> d_); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> y.col(t); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_R</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;WARNING: Cannot set R (R[0] = &#34;</span> <span style="color:#f92672">&lt;&lt;</span> R.at(<span style="color:#ae81ff">0</span>) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;). No Gaussian measurement noise in Poisson observation model.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> R() <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> R_; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>}; <span style="color:#75715e">// namespace poisson +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_fith">ldsCtrlEst_h/lds_poisson_fit.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson__fit__em_8h/index.html b/docs/docs/api/files/lds__poisson__fit__em_8h/index.html new file mode 100644 index 00000000..100fccd5 --- /dev/null +++ b/docs/docs/api/files/lds__poisson__fit__em_8h/index.html @@ -0,0 +1,354 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS E-M fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_poisson_fit_em.h"> + <meta property="og:description" content="PLDS E-M fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_poisson_fit_em.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_poisson_fit_em.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_fit_emh">ldsCtrlEst_h/lds_poisson_fit_em.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_poisson_fit_emh"> + ldsCtrlEst_h/lds_poisson_fit_em.h + <a class="anchor" href="#ldsctrlest_hlds_poisson_fit_emh">#</a> +</h1> +<p>PLDS E-M fit type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/">lds::poisson::FitEM</a></strong> <br>PLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">Fit</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (<code>lds::gaussian::emFit_t</code>).</p> +<p>References: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).</p> +<p>[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.</p> +<p>[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_POISSON_FIT_EM_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit_em.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_poisson_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> poisson { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">FitEM</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> EM<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>EM; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> MaximizeOutput() <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">MaximizeMeasurement</span>() <span style="color:#66d9ef">override</span>{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">RecurseKe</span>(Matrix<span style="color:#f92672">&amp;</span> Ke, Cube<span style="color:#f92672">&amp;</span> P_pre, Cube<span style="color:#f92672">&amp;</span> P_post, size_t t) <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t <span style="color:#a6e22e">NewtonSolveC</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">AnalyticalSolveD</span>(); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace poisson +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_fit_emh">ldsCtrlEst_h/lds_poisson_fit_em.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html b/docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html new file mode 100644 index 00000000..3ec8ad4f --- /dev/null +++ b/docs/docs/api/files/lds__poisson__fit__ssid_8h/index.html @@ -0,0 +1,348 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS SSID fit type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_poisson_fit_ssid.h"> + <meta property="og:description" content="PLDS SSID fit type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_poisson_fit_ssid.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_poisson_fit_ssid.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_fit_ssidh">ldsCtrlEst_h/lds_poisson_fit_ssid.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_poisson_fit_ssidh"> + ldsCtrlEst_h/lds_poisson_fit_ssid.h + <a class="anchor" href="#ldsctrlest_hlds_poisson_fit_ssidh">#</a> +</h1> +<p>PLDS SSID fit type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/">lds::poisson::FitSSID</a></strong> <br>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for PLDS.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm (<code>[lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)</code>).</p> +<p>References: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_POISSON_FIT_SSID_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_fit_ssid.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_poisson_fit.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> poisson { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">FitSSID</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> SSID<span style="color:#f92672">&lt;</span>Fit<span style="color:#f92672">&gt;::</span>SSID; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> DecomposeData() <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">CalcCov</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">PoissonToGaussianMoments</span>(); +</span></span><span style="display:flex;"><span> Matrix cov_; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace poisson +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_fit_ssidh">ldsCtrlEst_h/lds_poisson_fit_ssid.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson__sctrl_8h/index.html b/docs/docs/api/files/lds__poisson__sctrl_8h/index.html new file mode 100644 index 00000000..a80a1244 --- /dev/null +++ b/docs/docs/api/files/lds__poisson__sctrl_8h/index.html @@ -0,0 +1,376 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS switched controller type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_poisson_sctrl.h"> + <meta property="og:description" content="PLDS switched controller type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_poisson_sctrl.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_poisson_sctrl.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_sctrlh">ldsCtrlEst_h/lds_poisson_sctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_poisson_sctrlh"> + ldsCtrlEst_h/lds_poisson_sctrl.h + <a class="anchor" href="#ldsctrlest_hlds_poisson_sctrlh">#</a> +</h1> +<p>PLDS switched controller type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/">lds::poisson::SwitchedController</a></strong> <br>Poisson-observation <a href="">SwitchedController</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/">lds::poisson::SwitchedController</a>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_poisson_ctrl.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_sctrl.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> poisson { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SwitchedController</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> set_y_ref(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> y_ref) <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> Reassign(y_ref_,y_ref); +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>Limit(y_ref_, kYRefLB, lds<span style="color:#f92672">::</span>kInf); +</span></span><span style="display:flex;"><span> cx_ref_ <span style="color:#f92672">=</span> log(y_ref_) <span style="color:#f92672">-</span> sys_.d(); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure base class template methods available +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>SwitchedController; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Switch; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Control; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>ControlOutputReference; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>sys; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_tau_awu; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Reset; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Print; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">constexpr</span> <span style="color:#66d9ef">static</span> data_t kYRefLB <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> <span style="color:#ae81ff">1e-4</span>; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace poisson +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_sctrlh">ldsCtrlEst_h/lds_poisson_sctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson__sys_8cpp/index.html b/docs/docs/api/files/lds__poisson__sys_8cpp/index.html new file mode 100644 index 00000000..3e000bef --- /dev/null +++ b/docs/docs/api/files/lds__poisson__sys_8cpp/index.html @@ -0,0 +1,325 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS base type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="src/lds_poisson_sys.cpp"> + <meta property="og:description" content="PLDS base type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>src/lds_poisson_sys.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>src/lds_poisson_sys.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_poisson_syscpp">src/lds_poisson_sys.cpp</a> + <ul> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="srclds_poisson_syscpp"> + src/lds_poisson_sys.cpp + <a class="anchor" href="#srclds_poisson_syscpp">#</a> +</h1> +<p>PLDS base type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (<code>lds::poisson::sys_t</code>). It inherits functionality from the underlying linear dynamical system (<code>lds::sys_t</code>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst_h/lds_poisson_sys.h&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span>lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>System(size_t n_u, size_t n_x, size_t n_y, data_t dt, +</span></span><span style="display:flex;"><span> data_t p0, data_t q0) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> lds<span style="color:#f92672">::</span>System(n_u, n_x, n_y, dt, p0, q0) { +</span></span><span style="display:flex;"><span> diag_y_ <span style="color:#f92672">=</span> diagmat(y_); +</span></span><span style="display:flex;"><span> pd_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>poisson_distribution<span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// Correct: Given measurement (z) and current input (u), update estimate of the +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// state, covar, output. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// see Eden et al. 2004 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">void</span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>RecurseKe() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// predict covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P_ <span style="color:#f92672">=</span> A_ <span style="color:#f92672">*</span> P_ <span style="color:#f92672">*</span> A_.t() <span style="color:#f92672">+</span> Q_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update cov +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P_ <span style="color:#f92672">=</span> pinv(pinv(P_) <span style="color:#f92672">+</span> C_.t() <span style="color:#f92672">*</span> diag_y_ <span style="color:#f92672">*</span> C_); +</span></span><span style="display:flex;"><span> Ke_ <span style="color:#f92672">=</span> P_ <span style="color:#f92672">*</span> C_.t(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_adapt_m) { +</span></span><span style="display:flex;"><span> P_m_ <span style="color:#f92672">+=</span> Q_m_; <span style="color:#75715e">// predict (A_m = I) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P_m_ <span style="color:#f92672">=</span> pinv(pinv(P_m_) <span style="color:#f92672">+</span> C_.t() <span style="color:#f92672">*</span> diag_y_ <span style="color:#f92672">*</span> C_); <span style="color:#75715e">// update +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Ke_m_ <span style="color:#f92672">=</span> P_m_ <span style="color:#f92672">*</span> C_.t(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// Simulate Measurement: z ~ Poisson(y) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> lds<span style="color:#f92672">::</span>Vector<span style="color:#f92672">&amp;</span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>Simulate(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_tm1) { +</span></span><span style="display:flex;"><span> f(u_tm1, true); <span style="color:#75715e">// simulate dynamics with noise added +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> h(); <span style="color:#75715e">// output +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> z_.zeros(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (std<span style="color:#f92672">::</span>size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> n_y_; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// construct a Poisson distribution object with mean y[k] +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> pd_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>poisson_distribution<span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(y_[k]); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// pull random sample from this distribution +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z_[k] <span style="color:#f92672">=</span> pd_(rng); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> z_; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span><span style="color:#75715e">// ******************* SYS_T ******************* +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_poisson_syscpp">src/lds_poisson_sys.cpp</a> + <ul> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__poisson__sys_8h/index.html b/docs/docs/api/files/lds__poisson__sys_8h/index.html new file mode 100644 index 00000000..dd218ebf --- /dev/null +++ b/docs/docs/api/files/lds__poisson__sys_8h/index.html @@ -0,0 +1,367 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="PLDS base type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_poisson_sys.h"> + <meta property="og:description" content="PLDS base type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_poisson_sys.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_poisson_sys.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_sysh">ldsCtrlEst_h/lds_poisson_sys.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_poisson_sysh"> + ldsCtrlEst_h/lds_poisson_sys.h + <a class="anchor" href="#ldsctrlest_hlds_poisson_sysh">#</a> +</h1> +<p>PLDS base type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">lds::poisson::System</a></strong> <br>Poisson <a href="">System</a> type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (<code>[lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)</code>). It inherits functionality from the underlying linear dynamical system (<code>[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)</code>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_POISSON_SYS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_POISSON_SYS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_poisson.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_sys.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// needed for Poisson random number generation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;random&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> poisson { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">System</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> lds<span style="color:#f92672">::</span>System { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> System() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> System(std<span style="color:#f92672">::</span>size_t n_u, std<span style="color:#f92672">::</span>size_t n_x, std<span style="color:#f92672">::</span>size_t n_y, data_t dt, +</span></span><span style="display:flex;"><span> data_t p0 <span style="color:#f92672">=</span> kDefaultP0, data_t q0 <span style="color:#f92672">=</span> kDefaultQ0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> Simulate(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_tm1) <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> h() <span style="color:#66d9ef">override</span> { +</span></span><span style="display:flex;"><span> cx_ <span style="color:#f92672">=</span> C_ <span style="color:#f92672">*</span> x_; +</span></span><span style="display:flex;"><span> y_ <span style="color:#f92672">=</span> exp(cx_ <span style="color:#f92672">+</span> d_); +</span></span><span style="display:flex;"><span> diag_y_.diag() <span style="color:#f92672">=</span> y_; +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Vector <span style="color:#a6e22e">h_</span>(Vector x) <span style="color:#66d9ef">override</span> { <span style="color:#66d9ef">return</span> exp(C_ <span style="color:#f92672">*</span> x <span style="color:#f92672">+</span> d_); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">RecurseKe</span>() <span style="color:#66d9ef">override</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Poisson-output-specific +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix diag_y_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>poisson_distribution<span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span> pd_; +</span></span><span style="display:flex;"><span>}; <span style="color:#75715e">// System +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace poisson +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_poisson_sysh">ldsCtrlEst_h/lds_poisson_sys.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__sctrl_8h/index.html b/docs/docs/api/files/lds__sctrl_8h/index.html new file mode 100644 index 00000000..29c26028 --- /dev/null +++ b/docs/docs/api/files/lds__sctrl_8h/index.html @@ -0,0 +1,532 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="SwitchedController type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_sctrl.h"> + <meta property="og:description" content="SwitchedController type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_sctrl.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_sctrl.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_sctrlh">ldsCtrlEst_h/lds_sctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_sctrlh"> + ldsCtrlEst_h/lds_sctrl.h + <a class="anchor" href="#ldsctrlest_hlds_sctrlh">#</a> +</h1> +<p>SwitchedController type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController</a></strong> <br><a href="">SwitchedController</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/">lds::gaussian::SwitchedController</a>).</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_SCTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_SCTRL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_ctrl.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_uniform_mats.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_uniform_vecs.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SwitchedController</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> SwitchedController() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> SwitchedController(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;</span> systems, data_t u_lb, +</span></span><span style="display:flex;"><span> data_t u_ub, size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> SwitchedController(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;&amp;</span> systems, data_t u_lb, data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Switch</span>(size_t idx, <span style="color:#66d9ef">bool</span> do_force_switch <span style="color:#f92672">=</span> false); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc</span>(<span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;&amp;</span> Kc) { +</span></span><span style="display:flex;"><span> Kc_list_ <span style="color:#f92672">=</span> Kc; +</span></span><span style="display:flex;"><span> Kc_ <span style="color:#f92672">=</span> Kc_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc</span>(UniformMatrixList<span style="color:#f92672">&lt;&gt;&amp;&amp;</span> Kc) { +</span></span><span style="display:flex;"><span> Kc_list_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(Kc); +</span></span><span style="display:flex;"><span> Kc_ <span style="color:#f92672">=</span> Kc_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc_inty</span>(<span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;&amp;</span> Kc_inty) { +</span></span><span style="display:flex;"><span> Kc_inty_list_ <span style="color:#f92672">=</span> Kc_inty; +</span></span><span style="display:flex;"><span> Kc_inty_ <span style="color:#f92672">=</span> Kc_inty_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc_inty</span>(UniformMatrixList<span style="color:#f92672">&lt;&gt;&amp;&amp;</span> Kc_inty) { +</span></span><span style="display:flex;"><span> Kc_inty_list_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(Kc_inty); +</span></span><span style="display:flex;"><span> Kc_inty_ <span style="color:#f92672">=</span> Kc_inty_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc_u</span>(<span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;&amp;</span> Kc_u) { +</span></span><span style="display:flex;"><span> Kc_u_list_ <span style="color:#f92672">=</span> Kc_u; +</span></span><span style="display:flex;"><span> Kc_u_ <span style="color:#f92672">=</span> Kc_u_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Kc_u</span>(UniformMatrixList<span style="color:#f92672">&lt;&gt;&amp;&amp;</span> Kc_u) { +</span></span><span style="display:flex;"><span> Kc_u_list_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(Kc_u); +</span></span><span style="display:flex;"><span> Kc_u_ <span style="color:#f92672">=</span> Kc_u_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_g_design</span>(<span style="color:#66d9ef">const</span> UniformVectorList<span style="color:#f92672">&amp;</span> g) { +</span></span><span style="display:flex;"><span> g_design_list_ <span style="color:#f92672">=</span> g; +</span></span><span style="display:flex;"><span> g_design_ <span style="color:#f92672">=</span> g_design_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_g_design</span>(UniformVectorList<span style="color:#f92672">&amp;&amp;</span> g) { +</span></span><span style="display:flex;"><span> g_design_list_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(g); +</span></span><span style="display:flex;"><span> g_design_ <span style="color:#f92672">=</span> g_design_list_.at(<span style="color:#ae81ff">0</span>); <span style="color:#75715e">// set to first +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (idx_ <span style="color:#f92672">!=</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Switch(idx_, true); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure base class template methods available +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Controller; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Control; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>ControlOutputReference; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>sys; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_inty; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_u; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>g_design; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_u_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_x_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_y_ref; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_tau_awu; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_control_type; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Reset; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Print; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span> systems_; +</span></span><span style="display:flex;"><span> size_t n_sys_{}; +</span></span><span style="display:flex;"><span> size_t idx_{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// controller gains could be different for each +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> Kc_list_; +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> Kc_inty_list_; +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> Kc_u_list_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// design-phase input gain could also be different +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> UniformVectorList g_design_list_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): not sure why I need to do this. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_inty_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Kc_u_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>g_design_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>sys_; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// using Controller&lt;System&gt;::u_ref_; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using Controller&lt;System&gt;::x_ref_; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using Controller&lt;System&gt;::y_ref_; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>control_type_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> InitVars(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>set_sys; +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// using Controller&lt;System&gt;::set_Kc; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using Controller&lt;System&gt;::set_Kc_inty; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using Controller&lt;System&gt;::set_Kc_u; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using Controller&lt;System&gt;::set_g_design; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>SwitchedController( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;</span> systems, data_t u_lb, data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span>(systems.at(<span style="color:#ae81ff">0</span>), u_lb, u_ub, control_type), +</span></span><span style="display:flex;"><span> systems_(systems) { +</span></span><span style="display:flex;"><span> InitVars(); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>SwitchedController( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;&amp;</span> systems, data_t u_lb, data_t u_ub, +</span></span><span style="display:flex;"><span> size_t control_type) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> Controller<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span>(System(systems.at(<span style="color:#ae81ff">0</span>).n_u(), systems.at(<span style="color:#ae81ff">0</span>).n_x(), +</span></span><span style="display:flex;"><span> systems.at(<span style="color:#ae81ff">0</span>).n_y(), systems.at(<span style="color:#ae81ff">0</span>).dt()), +</span></span><span style="display:flex;"><span> u_lb, u_ub, control_type), +</span></span><span style="display:flex;"><span> systems_(std<span style="color:#f92672">::</span>move(systems)) { +</span></span><span style="display:flex;"><span> InitVars(); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>InitVars() { +</span></span><span style="display:flex;"><span> n_sys_ <span style="color:#f92672">=</span> systems_.size(); +</span></span><span style="display:flex;"><span> sys_ <span style="color:#f92672">=</span> systems_.at(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Kc_list_ <span style="color:#f92672">=</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span>(n_sys_, Kc_)); +</span></span><span style="display:flex;"><span> Kc_inty_list_ <span style="color:#f92672">=</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span>(n_sys_, Kc_inty_)); +</span></span><span style="display:flex;"><span> Kc_u_list_ <span style="color:#f92672">=</span> UniformMatrixList<span style="color:#f92672">&lt;&gt;</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span>(n_sys_, Kc_inty_)); +</span></span><span style="display:flex;"><span> g_design_list_ <span style="color:#f92672">=</span> UniformVectorList(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;</span>(n_sys_, g_design_)); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> SwitchedController<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Switch(size_t idx, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> do_force_switch) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ((idx <span style="color:#f92672">==</span> idx_) <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">!</span>do_force_switch) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; <span style="color:#75715e">// already there. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// put old up and get new one out +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> systems_.at(idx_) <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(sys_); +</span></span><span style="display:flex;"><span> sys_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(systems_.at(idx)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set the state of this system to that of the previous system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// TODO(mfbolus): This will only work as intended if state matrix is the same. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sys_.set_m(systems_.at(idx_).m(), true); +</span></span><span style="display:flex;"><span> sys_.set_x(systems_.at(idx_).x()); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// swap controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Kc_list_.Swap(Kc_, idx_); +</span></span><span style="display:flex;"><span> Kc_list_.Swap(Kc_, idx); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (control_type_ <span style="color:#f92672">&amp;</span> kControlTypeIntY) { +</span></span><span style="display:flex;"><span> Kc_inty_list_.Swap(Kc_inty_, idx_); +</span></span><span style="display:flex;"><span> Kc_inty_list_.Swap(Kc_inty_, idx); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (control_type_ <span style="color:#f92672">&amp;</span> kControlTypeDeltaU) { +</span></span><span style="display:flex;"><span> Kc_u_list_.Swap(Kc_u_, idx_); +</span></span><span style="display:flex;"><span> Kc_u_list_.Swap(Kc_u_, idx); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> g_design_list_.Swap(g_design_, idx_); +</span></span><span style="display:flex;"><span> g_design_list_.Swap(g_design_, idx); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> idx_ <span style="color:#f92672">=</span> idx; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// Switch +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_sctrlh">ldsCtrlEst_h/lds_sctrl.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__sys_8cpp/index.html b/docs/docs/api/files/lds__sys_8cpp/index.html new file mode 100644 index 00000000..59d312c9 --- /dev/null +++ b/docs/docs/api/files/lds__sys_8cpp/index.html @@ -0,0 +1,415 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="LDS base type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="src/lds_sys.cpp"> + <meta property="og:description" content="LDS base type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>src/lds_sys.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>src/lds_sys.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_syscpp">src/lds_sys.cpp</a> + <ul> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="srclds_syscpp"> + src/lds_sys.cpp + <a class="anchor" href="#srclds_syscpp">#</a> +</h1> +<p>LDS base type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file implements the base type for linear dynamical systems (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a>). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- lds_sys.cpp - LDS -------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst_h/lds_sys.h&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;vector&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span>lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, +</span></span><span style="display:flex;"><span> data_t q0) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { +</span></span><span style="display:flex;"><span> InitVars(p0, q0); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>InitVars(data_t p0, data_t q0) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// initial conditions. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> x0_ <span style="color:#f92672">=</span> Vector(n_x_, fill<span style="color:#f92672">::</span>zeros); <span style="color:#75715e">// includes bias (nY) and g (nU) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P0_ <span style="color:#f92672">=</span> p0 <span style="color:#f92672">*</span> Matrix(n_x_, n_x_, fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> m0_ <span style="color:#f92672">=</span> x0_; +</span></span><span style="display:flex;"><span> P0_m_ <span style="color:#f92672">=</span> P0_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// signals +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> x_ <span style="color:#f92672">=</span> x0_; +</span></span><span style="display:flex;"><span> P_ <span style="color:#f92672">=</span> P0_; +</span></span><span style="display:flex;"><span> m_ <span style="color:#f92672">=</span> m0_; +</span></span><span style="display:flex;"><span> P_m_ <span style="color:#f92672">=</span> P0_m_; +</span></span><span style="display:flex;"><span> y_ <span style="color:#f92672">=</span> Vector(n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> cx_ <span style="color:#f92672">=</span> Vector(n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> z_ <span style="color:#f92672">=</span> Vector(n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// By default, random walk where each state is independent +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// In this way, provides independent estimates of rate per channel of output. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> A_ <span style="color:#f92672">=</span> Matrix(n_x_, n_x_, fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> B_ <span style="color:#f92672">=</span> Matrix(n_x_, n_u_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> g_ <span style="color:#f92672">=</span> Vector(n_u_, fill<span style="color:#f92672">::</span>ones); +</span></span><span style="display:flex;"><span> Q_ <span style="color:#f92672">=</span> q0 <span style="color:#f92672">*</span> Matrix(n_x_, n_x_, fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> Q_m_ <span style="color:#f92672">=</span> Q_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> C_ <span style="color:#f92672">=</span> Matrix(n_y_, n_x_, fill<span style="color:#f92672">::</span>eye); <span style="color:#75715e">// each state will map to an output by +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> d_ <span style="color:#f92672">=</span> Vector(n_y_, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Ke_ <span style="color:#f92672">=</span> Matrix(n_x_, n_y_, fill<span style="color:#f92672">::</span>zeros); <span style="color:#75715e">// estimator gain. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Ke_m_ <span style="color:#f92672">=</span> Matrix(n_x_, n_y_, fill<span style="color:#f92672">::</span>zeros); <span style="color:#75715e">// estimator gain for m adaptation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> do_adapt_m <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// and update estimate of the state, covar, output using Kalman filter +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">void</span> lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>Filter(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_tm1, <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> z_t) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// predict mean +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> f(u_tm1); <span style="color:#75715e">// dynamics +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> h(); <span style="color:#75715e">// output +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// recursively calculate esimator gains (or just keep existing values) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (also predicts+updates estimate covariance) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> RecurseKe(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// update +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> x_ <span style="color:#f92672">+=</span> Ke_ <span style="color:#f92672">*</span> (z_t <span style="color:#f92672">-</span> y_); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_adapt_m) { +</span></span><span style="display:flex;"><span> m_ <span style="color:#f92672">+=</span> Ke_m_ <span style="color:#f92672">*</span> (z_t <span style="color:#f92672">-</span> y_); <span style="color:#75715e">// adaptively estimating disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// With new state, estimate output. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> h(); <span style="color:#75715e">// --&gt; posterior +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>Reset() { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// reset to initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> x_ <span style="color:#f92672">=</span> x0_; <span style="color:#75715e">// mean +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P_ <span style="color:#f92672">=</span> P0_; <span style="color:#75715e">// cov of state estimate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> m_ <span style="color:#f92672">=</span> m0_; <span style="color:#75715e">// process disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> P_m_ <span style="color:#f92672">=</span> P0_m_; <span style="color:#75715e">// cov of disturbance estimate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> h(); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>kMatFreeDim2<span style="color:#f92672">&gt;&gt;</span> +</span></span><span style="display:flex;"><span>lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>nstep_pred_block(lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> u, +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> z, +</span></span><span style="display:flex;"><span> size_t n_pred) { +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> x_filt; +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> x_pred; +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> y_pred; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> u.size(); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> Reset(); +</span></span><span style="display:flex;"><span> size_t n_t <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>size(u[k])[<span style="color:#ae81ff">1</span>]; +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">x_filt_k</span>(n_x_, n_t, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">x_pred_k</span>(n_x_, n_t <span style="color:#f92672">-</span> n_pred, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">y_pred_k</span>(n_y_, n_t <span style="color:#f92672">-</span> n_pred, fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; t <span style="color:#f92672">&lt;</span> n_t <span style="color:#f92672">-</span> n_pred; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> Vector x_pred_ahead <span style="color:#f92672">=</span> x_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t_u <span style="color:#f92672">=</span> t; t_u <span style="color:#f92672">&lt;</span> t <span style="color:#f92672">+</span> n_pred; t_u<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> x_pred_ahead <span style="color:#f92672">=</span> A_ <span style="color:#f92672">*</span> x_pred_ahead <span style="color:#f92672">+</span> B_ <span style="color:#f92672">*</span> u[k].col(t_u); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> x_pred_k.col(t) <span style="color:#f92672">=</span> x_pred_ahead; +</span></span><span style="display:flex;"><span> y_pred_k.col(t) <span style="color:#f92672">=</span> h_(x_pred_ahead); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (t <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Filter(u[k].col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), z[k].col(t)); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> x_filt_k.col(t) <span style="color:#f92672">=</span> x_; <span style="color:#75715e">// given previous measurment +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t t <span style="color:#f92672">=</span> n_t <span style="color:#f92672">-</span> n_pred; t <span style="color:#f92672">&lt;</span> n_t; t<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (t <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>) { +</span></span><span style="display:flex;"><span> Filter(u[k].col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), z[k].col(t)); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> x_filt_k.col(t) <span style="color:#f92672">=</span> x_; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> x_filt.append(x_filt_k); +</span></span><span style="display:flex;"><span> x_pred.append(x_pred_k); +</span></span><span style="display:flex;"><span> y_pred.append(y_pred_k); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> {x_filt, x_pred, y_pred}; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> lds<span style="color:#f92672">::</span>System<span style="color:#f92672">::</span>Print() { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74"> ********** SYSTEM ********** </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> x_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;P: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> P_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;A: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> A_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;B: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> B_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;g: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> g_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;m: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> m_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Q: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> Q_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Q_m: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> Q_m_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;d: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> d_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;C: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> C_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cout <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;y: </span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span> <span style="color:#f92672">&lt;&lt;</span> y_ <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">//******************* SYS_T ******************* +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_syscpp">src/lds_sys.cpp</a> + <ul> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__sys_8h/index.html b/docs/docs/api/files/lds__sys_8h/index.html new file mode 100644 index 00000000..7d5dd0a7 --- /dev/null +++ b/docs/docs/api/files/lds__sys_8h/index.html @@ -0,0 +1,448 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="LDS base type."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_sys.h"> + <meta property="og:description" content="LDS base type."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_sys.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_sys.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_sysh">ldsCtrlEst_h/lds_sys.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_sysh"> + ldsCtrlEst_h/lds_sys.h + <a class="anchor" href="#ldsctrlest_hlds_sysh">#</a> +</h1> +<p>LDS base type. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong> <br>Linear Dynamical <a href="">System</a> Type.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file declares and partially defines the base type for linear dynamical systems (<code>[lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)</code>). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_SYS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_SYS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_uniform_mats.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">System</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> System() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 <span style="color:#f92672">=</span> kDefaultP0, +</span></span><span style="display:flex;"><span> data_t q0 <span style="color:#f92672">=</span> kDefaultQ0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#f92672">~</span>System() {} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Filter</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_tm1, <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> z); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> Simulate(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u_tm1) <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">f</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> u, <span style="color:#66d9ef">bool</span> do_add_noise <span style="color:#f92672">=</span> false) { +</span></span><span style="display:flex;"><span> x_ <span style="color:#f92672">=</span> A_ <span style="color:#f92672">*</span> x_ <span style="color:#f92672">+</span> B_ <span style="color:#f92672">*</span> (g_ <span style="color:#f92672">%</span> u) <span style="color:#f92672">+</span> m_; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_add_noise) { +</span></span><span style="display:flex;"><span> x_ <span style="color:#f92672">+=</span> arma<span style="color:#f92672">::</span>mvnrnd(Vector(n_x_).fill(<span style="color:#ae81ff">0</span>), Q_); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">h</span>() <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> Vector <span style="color:#a6e22e">h_</span>(Vector x) <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">n_u</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> n_u_; }; +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">n_x</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> n_x_; }; +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">n_y</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> n_y_; }; +</span></span><span style="display:flex;"><span> data_t <span style="color:#a6e22e">dt</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> dt_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> x_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> P_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> m() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> m_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P_m() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> P_m_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> cx() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> cx_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> y() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> y_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x0() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> x0_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> m0() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> m0_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> A() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> A_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> B() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> B_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> g() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> g_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> C() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> C_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> d() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> d_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Ke() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> Ke_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Ke_m() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> Ke_m_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q() { <span style="color:#66d9ef">return</span> Q_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q_m() { <span style="color:#66d9ef">return</span> Q_m_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P0() { <span style="color:#66d9ef">return</span> P0_; }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P0_m() { <span style="color:#66d9ef">return</span> P0_m_; }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_A</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> A) { Reassign(A_, A); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_B</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> B) { Reassign(B_, B); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_m</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> m, <span style="color:#66d9ef">bool</span> do_force_assign <span style="color:#f92672">=</span> false) { +</span></span><span style="display:flex;"><span> Reassign(m0_, m); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ((<span style="color:#f92672">!</span>do_adapt_m) <span style="color:#f92672">||</span> do_force_assign) { +</span></span><span style="display:flex;"><span> Reassign(m_, m); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_g</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> g) { Reassign(g_, g); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Q</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q) { Reassign(Q_, Q); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_Q_m</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> Q_m) { Reassign(Q_m_, Q_m); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_x0</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x0) { Reassign(x0_, x0); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_P0</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P0) { Reassign(P0_, P0); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_P0_m</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> P0_m) { Reassign(P0_m_, P0_m); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_C</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> C) { Reassign(C_, C); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_d</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> d) { Reassign(d_, d); }; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">set_x</span>(<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> x) { +</span></span><span style="display:flex;"><span> Reassign(x_, x); +</span></span><span style="display:flex;"><span> h(); +</span></span><span style="display:flex;"><span> }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Reset</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;&gt;</span> nstep_pred_block( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> u, UniformMatrixList<span style="color:#f92672">&lt;</span>kMatFreeDim2<span style="color:#f92672">&gt;</span> z, +</span></span><span style="display:flex;"><span> size_t n_pred <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Print</span>(); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// safe to leave this public and non-const +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> do_adapt_m{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">protected</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">virtual</span> <span style="color:#66d9ef">void</span> RecurseKe() <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">InitVars</span>(data_t p0 <span style="color:#f92672">=</span> kDefaultP0, data_t q0 <span style="color:#f92672">=</span> kDefaultQ0); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_x_{}; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_u_{}; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>size_t n_y_{}; +</span></span><span style="display:flex;"><span> data_t dt_{}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Signals: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x_; +</span></span><span style="display:flex;"><span> Matrix P_; +</span></span><span style="display:flex;"><span> Vector m_; +</span></span><span style="display:flex;"><span> Matrix P_m_; +</span></span><span style="display:flex;"><span> Vector cx_; +</span></span><span style="display:flex;"><span> Vector y_; +</span></span><span style="display:flex;"><span> Vector z_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Parameters: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_; +</span></span><span style="display:flex;"><span> Matrix P0_; +</span></span><span style="display:flex;"><span> Vector m0_; +</span></span><span style="display:flex;"><span> Matrix P0_m_; +</span></span><span style="display:flex;"><span> Matrix A_; +</span></span><span style="display:flex;"><span> Matrix B_; +</span></span><span style="display:flex;"><span> Vector g_; +</span></span><span style="display:flex;"><span> Matrix Q_; +</span></span><span style="display:flex;"><span> Matrix Q_m_; +</span></span><span style="display:flex;"><span> Matrix C_; +</span></span><span style="display:flex;"><span> Vector d_; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix Ke_; +</span></span><span style="display:flex;"><span> Matrix Ke_m_; +</span></span><span style="display:flex;"><span>}; <span style="color:#75715e">// System +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_sysh">ldsCtrlEst_h/lds_sys.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__uniform__mats_8h/index.html b/docs/docs/api/files/lds__uniform__mats_8h/index.html new file mode 100644 index 00000000..4c8135ca --- /dev/null +++ b/docs/docs/api/files/lds__uniform__mats_8h/index.html @@ -0,0 +1,600 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="List of uniformly sized matrices."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_uniform_mats.h"> + <meta property="og:description" content="List of uniformly sized matrices."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_uniform_mats.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_uniform_mats.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_uniform_matsh">ldsCtrlEst_h/lds_uniform_mats.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_uniform_matsh"> + ldsCtrlEst_h/lds_uniform_mats.h + <a class="anchor" href="#ldsctrlest_hlds_uniform_matsh">#</a> +</h1> +<p>List of uniformly sized matrices. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">lds::UniformMatrixList</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_UNIFORM_MATS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;array&gt;</span><span style="color:#75715e"> </span><span style="color:#75715e">// std::array +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;vector&gt;</span><span style="color:#75715e"> </span><span style="color:#75715e">// std::vector +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D <span style="color:#f92672">=</span> kMatFreeDimNone<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UniformMatrixList</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): would rather *uncomment* the below for sake of conversion +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using std::vector&lt;Matrix&gt;::vector; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// don&#39;t allow push_back to be used since it doesn&#39;t check dims +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>push_back; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span>[]; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>begin; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>end; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>size; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>at; +</span></span><span style="display:flex;"><span> UniformMatrixList() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">explicit</span> <span style="color:#a6e22e">UniformMatrixList</span>(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;&amp;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">explicit</span> <span style="color:#a6e22e">UniformMatrixList</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;&amp;&amp;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformMatrixList(std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformMatrixList(<span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> that); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformMatrixList(UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">~</span>UniformMatrixList() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;&amp;</span> dim(size_t n <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>) <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> dim_.at(n); } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">size</span>() { <span style="color:#66d9ef">return</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>size(); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> at(size_t n) { <span style="color:#66d9ef">return</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>at(n); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Swap</span>(Matrix<span style="color:#f92672">&amp;</span> that, size_t n); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(<span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> that); +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">append</span>(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> mat); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> CheckDimensions(std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;&gt;</span> dim_; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>Swap(Matrix<span style="color:#f92672">&amp;</span> that, size_t n) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure request in range +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (n <span style="color:#f92672">&gt;=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size()) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Requested UniformMatrixList element out of bounds. Skipping.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dim +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> does_match <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim1)) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (dim_[<span style="color:#ae81ff">0</span>][<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> that.n_rows); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim2)) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (dim_[<span style="color:#ae81ff">0</span>][<span style="color:#ae81ff">1</span>] <span style="color:#f92672">==</span> that.n_cols); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>does_match) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Cannot swap a UniformMatrixList element for an element of &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;different size. Skipping.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if checks pass, perform swap +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// not moving, since it causes memory issues. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// so this method isn&#39;t a memory-saver as designed for now +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix tmp <span style="color:#f92672">=</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n]; +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n] <span style="color:#f92672">=</span> that; +</span></span><span style="display:flex;"><span> that <span style="color:#f92672">=</span> tmp; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (D <span style="color:#f92672">==</span> kMatFreeDim1) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>dim_[n][<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n].n_rows; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (D <span style="color:#f92672">==</span> kMatFreeDim2) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>dim_[n][<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n].n_cols; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>append(<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> mat) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim({mat.n_rows, mat.n_cols}); +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span>push_back(mat); +</span></span><span style="display:flex;"><span> dim_.push_back(dim); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> that) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure dim_ vector is initialized +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (dim_.empty()) { +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;&gt;</span>(that.size(), {<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>}); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>empty()) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size() <span style="color:#f92672">!=</span> that.size()) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign &#34;</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; matrices with &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> that.size() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; matrices&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if dimensions a not zero and do not match, skip move with error message. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> dims_nonzero <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">auto</span> d : dim_) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim1) <span style="color:#f92672">&amp;&amp;</span> d[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">1</span>) { +</span></span><span style="display:flex;"><span> dims_nonzero <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim2) <span style="color:#f92672">&amp;&amp;</span> d[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">&lt;</span> <span style="color:#ae81ff">1</span>) { +</span></span><span style="display:flex;"><span> dims_nonzero <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">break</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (dims_nonzero) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> does_match <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim1)) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (dim_[<span style="color:#ae81ff">0</span>][<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> that.at(<span style="color:#ae81ff">0</span>).n_rows); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim2)) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (dim_[<span style="color:#ae81ff">0</span>][<span style="color:#ae81ff">1</span>] <span style="color:#f92672">==</span> that.at(<span style="color:#ae81ff">0</span>).n_cols); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>does_match) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign matrices of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> dim_[<span style="color:#ae81ff">0</span>][<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> dim_[<span style="color:#ae81ff">0</span>][<span style="color:#ae81ff">1</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; with matrices of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> that.at(<span style="color:#ae81ff">0</span>).n_rows +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> <span style="color:#f92672">&lt;&lt;</span> that.at(<span style="color:#ae81ff">0</span>).n_cols; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size(); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[k] <span style="color:#f92672">=</span> that[k]; +</span></span><span style="display:flex;"><span> dim_[k] <span style="color:#f92672">=</span> that.dim(k); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// // check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// // if empty, assume a default constructed object and safe to move +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!this-&gt;empty()) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (this-&gt;size() != that.size()) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// std::cerr &lt;&lt; &#34;Cannot reassign &#34; &lt;&lt; this-&gt;size() &lt;&lt; &#34; matrices with &#34; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// &lt;&lt; that.size() &lt;&lt; &#34; matrices. Skipping.\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// return (*this); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// // if dimensions a not zero and do not match, skip move with error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// message. bool dims_nonzero = true; for (auto d : dim_) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!(D == kMatFreeDim1) &amp;&amp; (d[0] &lt; 1)) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// dims_nonzero = false; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// break; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!(D == kMatFreeDim2) &amp;&amp; (d[1] &lt; 1)) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// dims_nonzero = false; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// break; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (dims_nonzero) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// bool does_match = true; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!(D == kMatFreeDim1)) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// does_match = does_match &amp;&amp; (dim_[0][0] == that.at(0).n_rows); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!(D == kMatFreeDim2)) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// does_match = does_match &amp;&amp; (dim_[0][1] == that.at(0).n_cols); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!does_match) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// this-&gt;at(0).print(&#34;this[0] = &#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// that.at(0).print(&#34;that[0] = &#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// std::cerr +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// &lt;&lt; &#34;Cannot move a UniformMatrixList element of size (&#34; &lt;&lt; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// that.at(0).n_rows &lt;&lt; &#34;,&#34; &lt;&lt; that.at(0).n_cols &lt;&lt; &#34;) for an +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// element of size (&#34; &lt;&lt; dim_[0][0] &lt;&lt; &#34;,&#34; &lt;&lt; dim_[0][1] &lt;&lt; &#34;). +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Skipping.\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// return (*this); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> that.dim_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(std<span style="color:#f92672">::</span>move(that)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>UniformMatrixList(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;&amp;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(mats) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>UniformMatrixList(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;&amp;&amp;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(std<span style="color:#f92672">::</span>move(mats)) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>UniformMatrixList(std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span>Matrix<span style="color:#f92672">&gt;</span> mats, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(mats) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>UniformMatrixList(<span style="color:#66d9ef">const</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;</span> that) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(that) { +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>) <span style="color:#f92672">=</span> that; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>UniformMatrixList(UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(std<span style="color:#f92672">::</span>move(that)) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size(); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim_k({<span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(k).n_rows, <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(k).n_cols}); +</span></span><span style="display:flex;"><span> dim_.push_back(dim_k); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span>MatrixListFreeDim D<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> UniformMatrixList<span style="color:#f92672">&lt;</span>D<span style="color:#f92672">&gt;::</span>CheckDimensions(std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;</span> dim) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// change behavior based on free dim D +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> ((dim[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim1)) { +</span></span><span style="display:flex;"><span> dim[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_rows; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> ((dim[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span>) <span style="color:#f92672">&amp;&amp;</span> <span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim2)) { +</span></span><span style="display:flex;"><span> dim[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_cols; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure dimensiolaties are all uniform +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> <span style="color:#a6e22e">does_match</span>(true); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">const</span> Matrix<span style="color:#f92672">&amp;</span> mat : <span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim1)) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (mat.n_rows <span style="color:#f92672">==</span> dim[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>(D <span style="color:#f92672">==</span> kMatFreeDim2)) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (mat.n_cols <span style="color:#f92672">==</span> dim[<span style="color:#ae81ff">1</span>]); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>does_match) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Dimensionality of one or more input matrices are not uniform.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">2</span><span style="color:#f92672">&gt;&gt;</span>(<span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size(), dim); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size(); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> dim_[k][<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[k].n_rows; +</span></span><span style="display:flex;"><span> dim_[k][<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[k].n_cols; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_uniform_matsh">ldsCtrlEst_h/lds_uniform_mats.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__uniform__systems_8h/index.html b/docs/docs/api/files/lds__uniform__systems_8h/index.html new file mode 100644 index 00000000..42dc8a9f --- /dev/null +++ b/docs/docs/api/files/lds__uniform__systems_8h/index.html @@ -0,0 +1,525 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="List of uniformly sized Systems."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_uniform_systems.h"> + <meta property="og:description" content="List of uniformly sized Systems."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_uniform_systems.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_uniform_systems.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_uniform_systemsh">ldsCtrlEst_h/lds_uniform_systems.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_uniform_systemsh"> + ldsCtrlEst_h/lds_uniform_systems.h + <a class="anchor" href="#ldsctrlest_hlds_uniform_systemsh">#</a> +</h1> +<p>List of uniformly sized Systems. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">lds::UniformSystemList</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file provides a container for uniformly sized Systems.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;array&gt;</span><span style="color:#75715e"> </span><span style="color:#75715e">// std::array +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;vector&gt;</span><span style="color:#75715e"> </span><span style="color:#75715e">// std::vector +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// namespace +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">// System type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds_sys.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UniformSystemList</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">static_assert</span>(std<span style="color:#f92672">::</span>is_base_of<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>System, System<span style="color:#f92672">&gt;::</span>value, +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;System must be derived from lds::System type.&#34;</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): would rather *uncomment* the below for sake of conversion +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using std::vector&lt;System&gt;::vector; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span>[]; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>at; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>begin; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>end; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>size; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> UniformSystemList() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">explicit</span> <span style="color:#a6e22e">UniformSystemList</span>(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">explicit</span> <span style="color:#a6e22e">UniformSystemList</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;&amp;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformSystemList(std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformSystemList(<span style="color:#66d9ef">const</span> UniformSystemList<span style="color:#f92672">&amp;</span> that); +</span></span><span style="display:flex;"><span> UniformSystemList(UniformSystemList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">~</span>UniformSystemList() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;&amp;</span> dim() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> dim_; } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">size</span>() { <span style="color:#66d9ef">return</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>size(); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> System<span style="color:#f92672">&amp;</span> at(size_t n) { <span style="color:#66d9ef">return</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>at(n); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Swap</span>(System<span style="color:#f92672">&amp;</span> that, size_t n); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformSystemList<span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(<span style="color:#66d9ef">const</span> UniformSystemList<span style="color:#f92672">&amp;</span> that); +</span></span><span style="display:flex;"><span> UniformSystemList<span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(UniformSystemList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> CheckDimensions(std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim_{}; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>Swap(System<span style="color:#f92672">&amp;</span> that, size_t n) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure request in range +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (n <span style="color:#f92672">&gt;=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size()) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Requested UniformSystemList element out of bounds. Skipping.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dim +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> does_match <span style="color:#f92672">=</span> (dim_[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">==</span> that.n_u()) <span style="color:#f92672">&amp;&amp;</span> (dim_[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">==</span> that.n_x()) <span style="color:#f92672">&amp;&amp;</span> +</span></span><span style="display:flex;"><span> (dim_[<span style="color:#ae81ff">2</span>] <span style="color:#f92672">==</span> that.n_y()); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>does_match) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Cannot swap a UniformSystemList element for an element of &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;different size. Skipping.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if checks pass, perform swap +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> System tmp <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move((<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n]); +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n] <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(that); +</span></span><span style="display:flex;"><span> that <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(tmp); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;</span> UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformSystemList<span style="color:#f92672">&amp;</span> that) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>empty()) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size() <span style="color:#f92672">!=</span> that.size()) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign &#34;</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; systems with &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> that.size() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; systems&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (dim_[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">+</span> dim_[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">+</span> dim_[<span style="color:#ae81ff">2</span>]) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> other_dim(that.dim()); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (dim_ <span style="color:#f92672">!=</span> other_dim) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign systems of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> dim_[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> <span style="color:#f92672">&lt;&lt;</span> dim_[<span style="color:#ae81ff">1</span>] +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> <span style="color:#f92672">&lt;&lt;</span> dim_[<span style="color:#ae81ff">2</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; with systems of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> other_dim[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> other_dim[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;x&#34;</span> <span style="color:#f92672">&lt;&lt;</span> dim_[<span style="color:#ae81ff">2</span>]; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size(); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[k] <span style="color:#f92672">=</span> that[k]; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;</span> UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> UniformSystemList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// // check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// // if empty, assume a default constructed object and safe to move +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!this-&gt;empty()) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (this-&gt;size() != that.size()) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// std::cerr &lt;&lt; &#34;Cannot reassign &#34; &lt;&lt; this-&gt;size() &lt;&lt; &#34; systems with &#34; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// &lt;&lt; that.size() &lt;&lt; &#34; systems. Skipping.\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// return (*this); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// // if dimensions a not zero and do not match, skip move with error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// message. if (dim_[0] + dim_[1] + dim_[2]) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// bool does_match = (dim_[0] == that.at(0).n_u()) &amp;&amp; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (dim_[1] == that.at(0).n_x()) &amp;&amp; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (dim_[2] == that.at(0).n_y()); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!does_match) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// std::cerr +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// &lt;&lt; &#34;Cannot move a UniformSystemList element for an element of &#34; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// &#34;different size. Skipping.\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// return (*this); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> that.dim_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(std<span style="color:#f92672">::</span>move(that)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>UniformSystemList(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span>(systems) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>UniformSystemList(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;&amp;&amp;</span> systems, +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span>(std<span style="color:#f92672">::</span>move(systems)) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>UniformSystemList( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span> systems, std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span>(systems) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>UniformSystemList(<span style="color:#66d9ef">const</span> UniformSystemList<span style="color:#f92672">&amp;</span> that) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span>(that) { +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>) <span style="color:#f92672">=</span> that; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>UniformSystemList(UniformSystemList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;</span>(std<span style="color:#f92672">::</span>move(that)) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>dim_[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_u(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>dim_[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_x(); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>dim_[<span style="color:#ae81ff">2</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_y(); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> System<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> UniformSystemList<span style="color:#f92672">&lt;</span>System<span style="color:#f92672">&gt;::</span>CheckDimensions(std<span style="color:#f92672">::</span>array<span style="color:#f92672">&lt;</span>size_t, <span style="color:#ae81ff">3</span><span style="color:#f92672">&gt;</span> dim) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (dim[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">+</span> dim[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">+</span> dim[<span style="color:#ae81ff">2</span>]) { +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> dim; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> dim_[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_u(); +</span></span><span style="display:flex;"><span> dim_[<span style="color:#ae81ff">1</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_x(); +</span></span><span style="display:flex;"><span> dim_[<span style="color:#ae81ff">2</span>] <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_y(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure dimensiolaties are all uniform +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> <span style="color:#a6e22e">does_match</span>(true); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">const</span> System<span style="color:#f92672">&amp;</span> sys : <span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (sys.n_u() <span style="color:#f92672">==</span> dim_[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (sys.n_x() <span style="color:#f92672">==</span> dim_[<span style="color:#ae81ff">1</span>]); +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (sys.n_y() <span style="color:#f92672">==</span> dim_[<span style="color:#ae81ff">2</span>]); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>does_match) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Dimensionality of one or more input systems are not uniform.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_uniform_systemsh">ldsCtrlEst_h/lds_uniform_systems.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__uniform__vecs_8cpp/index.html b/docs/docs/api/files/lds__uniform__vecs_8cpp/index.html new file mode 100644 index 00000000..cb45df5d --- /dev/null +++ b/docs/docs/api/files/lds__uniform__vecs_8cpp/index.html @@ -0,0 +1,350 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Uniformly sized vectors."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="src/lds_uniform_vecs.cpp"> + <meta property="og:description" content="Uniformly sized vectors."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>src/lds_uniform_vecs.cpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>src/lds_uniform_vecs.cpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_uniform_vecscpp">src/lds_uniform_vecs.cpp</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="srclds_uniform_vecscpp"> + src/lds_uniform_vecs.cpp + <a class="anchor" href="#srclds_uniform_vecscpp">#</a> +</h1> +<p>Uniformly sized vectors. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file provides a container for uniformly sized vectors.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst_h/lds_uniform_vecs.h&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>UniformVectorList<span style="color:#f92672">::</span>UniformVectorList(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;&amp;</span> vecs, +</span></span><span style="display:flex;"><span> size_t dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(vecs) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>UniformVectorList<span style="color:#f92672">::</span>UniformVectorList(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;&amp;&amp;</span> vecs, size_t dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(std<span style="color:#f92672">::</span>move(vecs)) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>UniformVectorList<span style="color:#f92672">::</span>UniformVectorList(std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;</span> vecs, +</span></span><span style="display:flex;"><span> size_t dim) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(vecs) { +</span></span><span style="display:flex;"><span> CheckDimensions(dim); +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>UniformVectorList<span style="color:#f92672">::</span>UniformVectorList(<span style="color:#66d9ef">const</span> UniformVectorList<span style="color:#f92672">&amp;</span> that) +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(that) { +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>) <span style="color:#f92672">=</span> that; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>UniformVectorList<span style="color:#f92672">::</span>UniformVectorList(UniformVectorList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">:</span> vector(std<span style="color:#f92672">::</span>move(that)) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>dim_ <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_elem; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">void</span> UniformVectorList<span style="color:#f92672">::</span>CheckDimensions(size_t dim) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (dim) { +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> dim; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>at(<span style="color:#ae81ff">0</span>).n_elem; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure dimensiolaties are all uniform +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> <span style="color:#a6e22e">does_match</span>(true); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> vec : <span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>) { +</span></span><span style="display:flex;"><span> does_match <span style="color:#f92672">=</span> does_match <span style="color:#f92672">&amp;&amp;</span> (vec.n_elem <span style="color:#f92672">==</span> dim_); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>does_match) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error( +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;Dimensionality of one or more input matrices are not uniform.&#34;</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#srclds_uniform_vecscpp">src/lds_uniform_vecs.cpp</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/lds__uniform__vecs_8h/index.html b/docs/docs/api/files/lds__uniform__vecs_8h/index.html new file mode 100644 index 00000000..2783477d --- /dev/null +++ b/docs/docs/api/files/lds__uniform__vecs_8h/index.html @@ -0,0 +1,449 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="List of uniformly sized vectors."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/lds_uniform_vecs.h"> + <meta property="og:description" content="List of uniformly sized vectors."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/lds_uniform_vecs.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/lds_uniform_vecs.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_uniform_vecsh">ldsCtrlEst_h/lds_uniform_vecs.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hlds_uniform_vecsh"> + ldsCtrlEst_h/lds_uniform_vecs.h + <a class="anchor" href="#ldsctrlest_hlds_uniform_vecsh">#</a> +</h1> +<p>List of uniformly sized vectors. <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/">lds</a></strong> <br>Linear Dynamical Systems (LDS) namespace.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">lds::UniformVectorList</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file provides a container for uniformly sized vectors.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_LDS_UNIFORM_VECS_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;array&gt;</span><span style="color:#75715e"> </span><span style="color:#75715e">// std::array +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;vector&gt;</span><span style="color:#75715e"> </span><span style="color:#75715e">// std::vector +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;lds.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> lds { +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UniformVectorList</span> <span style="color:#f92672">:</span> <span style="color:#66d9ef">public</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): would rather *uncomment* the below for sake of conversion +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// using std::vector&lt;Vector&gt;::vector; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span>[]; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span>at; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span>begin; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span>end; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span>size; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">public</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> UniformVectorList() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">explicit</span> <span style="color:#a6e22e">UniformVectorList</span>(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;&amp;</span> vecs, size_t dim <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">explicit</span> <span style="color:#a6e22e">UniformVectorList</span>(std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;&amp;&amp;</span> vecs, size_t dim <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformVectorList(std<span style="color:#f92672">::</span>initializer_list<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;</span> vecs, size_t dim <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformVectorList(<span style="color:#66d9ef">const</span> UniformVectorList<span style="color:#f92672">&amp;</span> that); +</span></span><span style="display:flex;"><span> UniformVectorList(UniformVectorList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span>; +</span></span><span style="display:flex;"><span> <span style="color:#f92672">~</span>UniformVectorList() <span style="color:#f92672">=</span> <span style="color:#66d9ef">default</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">dim</span>() <span style="color:#66d9ef">const</span> { <span style="color:#66d9ef">return</span> dim_; } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">size</span>() { <span style="color:#66d9ef">return</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span>size(); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector<span style="color:#f92672">&amp;</span> at(size_t n) { <span style="color:#66d9ef">return</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span>at(n); }; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">Swap</span>(Vector<span style="color:#f92672">&amp;</span> that, size_t n); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> UniformVectorList<span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(<span style="color:#66d9ef">const</span> UniformVectorList<span style="color:#f92672">&amp;</span> that); +</span></span><span style="display:flex;"><span> UniformVectorList<span style="color:#f92672">&amp;</span> <span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(UniformVectorList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">private</span><span style="color:#f92672">:</span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">void</span> CheckDimensions(size_t dim); +</span></span><span style="display:flex;"><span> size_t dim_{}; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> UniformVectorList<span style="color:#f92672">::</span>Swap(Vector<span style="color:#f92672">&amp;</span> that, size_t n) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// make sure request in range +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (n <span style="color:#f92672">&gt;=</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size()) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Requested UniformMatrixList element out of bounds. Skipping.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dim +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> does_match <span style="color:#f92672">=</span> dim_ <span style="color:#f92672">==</span> that.n_elem; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span>does_match) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>cerr <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;Cannot swap a UniformMatrixList element for an element of &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#e6db74">&#34;different size. Skipping.</span><span style="color:#ae81ff">\n</span><span style="color:#e6db74">&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// if checks pass, perform swap +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector tmp <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move((<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n]); +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[n] <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(that); +</span></span><span style="display:flex;"><span> that <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move(tmp); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformVectorList<span style="color:#f92672">&amp;</span> UniformVectorList<span style="color:#f92672">::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> UniformVectorList<span style="color:#f92672">&amp;</span> that) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (<span style="color:#f92672">!</span><span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>empty()) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (<span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size() <span style="color:#f92672">!=</span> that.size()) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign &#34;</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; vectors with &#34;</span> +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> that.size() <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; vectors&#34;</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (dim_) { +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">other_dim</span>(that.dim()); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (dim_ <span style="color:#f92672">!=</span> other_dim) { +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>ostringstream ss; +</span></span><span style="display:flex;"><span> ss <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34;cannot reassign vectors of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> dim_ +</span></span><span style="display:flex;"><span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#e6db74">&#34; with vectors of size &#34;</span> <span style="color:#f92672">&lt;&lt;</span> other_dim; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">throw</span> std<span style="color:#f92672">::</span>runtime_error(ss.str()); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">this</span><span style="color:#f92672">-&gt;</span>size(); k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>)[k] <span style="color:#f92672">=</span> that[k]; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> UniformVectorList<span style="color:#f92672">&amp;</span> UniformVectorList<span style="color:#f92672">::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>( +</span></span><span style="display:flex;"><span> UniformVectorList<span style="color:#f92672">&amp;&amp;</span> that) <span style="color:#66d9ef">noexcept</span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// // check dimensions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (!this-&gt;empty()) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (this-&gt;size() != that.size()) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// std::cerr &lt;&lt; &#34;Cannot reassign &#34; &lt;&lt; this-&gt;size() &lt;&lt; &#34; vectors with &#34; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// &lt;&lt; that.size() &lt;&lt; &#34; vectors. Skipping.\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// return (*this); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (dim_) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// size_t other_dim(that.dim()); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if (dim_ != other_dim) { +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// std::cerr &lt;&lt; &#34;Cannot reassign vectors of size &#34; &lt;&lt; dim_ +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// &lt;&lt; &#34; with matrices of size &#34; &lt;&lt; other_dim &lt;&lt; &#34;. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Skipping.\n&#34;; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// return (*this); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// } +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> dim_ <span style="color:#f92672">=</span> that.dim_; +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>Vector<span style="color:#f92672">&gt;::</span><span style="color:#66d9ef">operator</span><span style="color:#f92672">=</span>(std<span style="color:#f92672">::</span>move(that)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> (<span style="color:#f92672">*</span><span style="color:#66d9ef">this</span>); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace lds +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hlds_uniform_vecsh">ldsCtrlEst_h/lds_uniform_vecs.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/mex__c__util_8h/index.html b/docs/docs/api/files/mex__c__util_8h/index.html new file mode 100644 index 00000000..b7eea5c4 --- /dev/null +++ b/docs/docs/api/files/mex__c__util_8h/index.html @@ -0,0 +1,375 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="arma &lt;-&gt; mex interoperability utilities (Matlab C API)"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/mex_c_util.h"> + <meta property="og:description" content="arma &lt;-&gt; mex interoperability utilities (Matlab C API)"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/mex_c_util.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/mex_c_util.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hmex_c_utilh">ldsCtrlEst_h/mex_c_util.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hmex_c_utilh"> + ldsCtrlEst_h/mex_c_util.h + <a class="anchor" href="#ldsctrlest_hmex_c_utilh">#</a> +</h1> +<p>arma &lt;-&gt; mex interoperability utilities (Matlab C API) <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/">armamexc</a></strong> <br>arma/mex interface using Matlab C API</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file defines utility functions for interoperability between armadillo and Matlab/Octave&rsquo;s C mex API.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_MEXC_UTIL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_MEXC_UTIL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;mex.h&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// // If Matlab_FOUND, include matrix.h. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// // (Octave does not need/have it.) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// #ifdef Matlab_FOUND +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// #include &#34;matrix.h&#34; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// #endif +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> armamexc { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">auto</span> m2T_scalar(<span style="color:#66d9ef">const</span> mxArray <span style="color:#f92672">*</span>matlab_scalar) <span style="color:#f92672">-&gt;</span> T { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (mxGetData(matlab_scalar)) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>(mxGetScalar(matlab_scalar)); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> mexErrMsgTxt(<span style="color:#e6db74">&#34;No data available.&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">auto</span> m2a_mat(<span style="color:#66d9ef">const</span> mxArray <span style="color:#f92672">*</span>matlab_mat, <span style="color:#66d9ef">bool</span> copy_aux_mem <span style="color:#f92672">=</span> false, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> strict <span style="color:#f92672">=</span> true) <span style="color:#f92672">-&gt;</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (mxGetData(matlab_mat)) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> mwSize n_dim <span style="color:#f92672">=</span> mxGetNumberOfDimensions(matlab_mat); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (n_dim <span style="color:#f92672">==</span> <span style="color:#ae81ff">2</span>) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>(<span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>T <span style="color:#f92672">*&gt;</span>(mxGetData(matlab_mat)), +</span></span><span style="display:flex;"><span> mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, +</span></span><span style="display:flex;"><span> strict); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> mexErrMsgTxt(<span style="color:#e6db74">&#34;Number of dimensions must be 2.&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>(); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> mexErrMsgTxt(<span style="color:#e6db74">&#34;No data available.&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>(); +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">// TODO(mfbolus): make these templated. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">auto</span> a2m_mat(arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">const</span> <span style="color:#f92672">&amp;</span>arma_mat) <span style="color:#f92672">-&gt;</span> mxArray <span style="color:#f92672">*</span> { +</span></span><span style="display:flex;"><span> mxArray <span style="color:#f92672">*</span>matlab_mat <span style="color:#f92672">=</span> mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, +</span></span><span style="display:flex;"><span> mxDOUBLE_CLASS, mxREAL); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (matlab_mat) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> <span style="color:#f92672">*</span>dst_pointer <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>T <span style="color:#f92672">*&gt;</span>(mxGetData(matlab_mat)); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">auto</span> <span style="color:#f92672">*</span>src_pointer <span style="color:#f92672">=</span> <span style="color:#66d9ef">const_cast</span><span style="color:#f92672">&lt;</span>T <span style="color:#f92672">*&gt;</span>(arma_mat.memptr()); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): I just want to MOVE the data, not copy. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> std<span style="color:#f92672">::</span>memcpy(dst_pointer, src_pointer, <span style="color:#66d9ef">sizeof</span>(T) <span style="color:#f92672">*</span> arma_mat.n_elem); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> matlab_mat; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> mexErrMsgTxt(<span style="color:#e6db74">&#34;Failed to create matlab mat from arma::Mat.&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nullptr</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T<span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">auto</span> a2m_vec(arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">const</span> <span style="color:#f92672">&amp;</span>arma_vec) <span style="color:#f92672">-&gt;</span> mxArray <span style="color:#f92672">*</span> { +</span></span><span style="display:flex;"><span> mxArray <span style="color:#f92672">*</span>matlab_mat <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> mxCreateNumericMatrix(arma_vec.n_elem, <span style="color:#ae81ff">1</span>, mxDOUBLE_CLASS, mxREAL); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (matlab_mat) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> <span style="color:#f92672">*</span>dst_pointer <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>T <span style="color:#f92672">*&gt;</span>(mxGetData(matlab_mat)); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#66d9ef">auto</span> <span style="color:#f92672">*</span>src_pointer <span style="color:#f92672">=</span> <span style="color:#66d9ef">const_cast</span><span style="color:#f92672">&lt;</span>T <span style="color:#f92672">*&gt;</span>(arma_vec.memptr()); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): I just want to MOVE the data, not copy. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> std<span style="color:#f92672">::</span>memcpy(dst_pointer, src_pointer, <span style="color:#66d9ef">sizeof</span>(T) <span style="color:#f92672">*</span> arma_vec.n_elem); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> matlab_mat; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> mexErrMsgTxt(<span style="color:#e6db74">&#34;Failed to create matlab mat from arma::Col.&#34;</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nullptr</span>; +</span></span><span style="display:flex;"><span>} +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace armamexc +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hmex_c_utilh">ldsCtrlEst_h/mex_c_util.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/files/mex__cpp__util_8h/index.html b/docs/docs/api/files/mex__cpp__util_8h/index.html new file mode 100644 index 00000000..fe478212 --- /dev/null +++ b/docs/docs/api/files/mex__cpp__util_8h/index.html @@ -0,0 +1,396 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="arma &lt;-&gt; mex interoperability utilities (Matlab C&#43;&#43; API)"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="ldsCtrlEst_h/mex_cpp_util.h"> + <meta property="og:description" content="arma &lt;-&gt; mex interoperability utilities (Matlab C&#43;&#43; API)"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>ldsCtrlEst_h/mex_cpp_util.h | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>ldsCtrlEst_h/mex_cpp_util.h</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hmex_cpp_utilh">ldsCtrlEst_h/mex_cpp_util.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest_hmex_cpp_utilh"> + ldsCtrlEst_h/mex_cpp_util.h + <a class="anchor" href="#ldsctrlest_hmex_cpp_utilh">#</a> +</h1> +<p>arma &lt;-&gt; mex interoperability utilities (Matlab C++ API) <a href="#detailed-description">More&hellip;</a></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/">armamexcpp</a></strong> <br>arma/mex interface using Matlab C++ API</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>This file defines utility functions for interoperability between armadillo and Matlab&rsquo;s C++ mex API.</p> +<h2 id="source-code"> + Source code + <a class="anchor" href="#source-code">#</a> +</h2> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">//===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Michael Bolus +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Copyright 2021 Georgia Institute of Technology +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Licensed under the Apache License, Version 2.0 (the &#34;License&#34;); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// you may not use this file except in compliance with the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// You may obtain a copy of the License at +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// http://www.apache.org/licenses/LICENSE-2.0 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// Unless required by applicable law or agreed to in writing, software +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// distributed under the License is distributed on an &#34;AS IS&#34; BASIS, +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// See the License for the specific language governing permissions and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// limitations under the License. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">//===----------------------------------------------------------------------===// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#ifndef LDSCTRLEST_MEXCPP_UTIL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#define LDSCTRLEST_MEXCPP_UTIL_H +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;mex.hpp&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&#34;mexAdapter.hpp&#34;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">namespace</span> armamexcpp { +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;&gt;</span> m2a_cellmat(matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>CellArray<span style="color:#f92672">&amp;</span> matlab_cell) { +</span></span><span style="display:flex;"><span> size_t n_cells <span style="color:#f92672">=</span> matlab_cell.getNumberOfElements(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;&gt;</span> arma_mat(n_cells, +</span></span><span style="display:flex;"><span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span>, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros)); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> n_cells; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> matlab_mat <span style="color:#f92672">=</span> matlab_cell[k]; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">auto</span> dims <span style="color:#f92672">=</span> matlab_mat.getDimensions(); +</span></span><span style="display:flex;"><span> arma_mat[k] <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>(matlab_mat.release().get(), dims[<span style="color:#ae81ff">0</span>], dims[<span style="color:#ae81ff">1</span>]); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> arma_mat; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> m2s_vec(matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;&amp;</span> matlab_array) { +</span></span><span style="display:flex;"><span> size_t n_elem <span style="color:#f92672">=</span> matlab_array.getNumberOfElements(); +</span></span><span style="display:flex;"><span> T<span style="color:#f92672">*</span> ptr <span style="color:#f92672">=</span> matlab_array.release().get(); +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> vec(ptr, ptr <span style="color:#f92672">+</span> n_elem); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> vec; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> m2a_vec(matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> matlab_array) { +</span></span><span style="display:flex;"><span> size_t n_elem <span style="color:#f92672">=</span> matlab_array.getNumberOfElements(); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// T* ptr = matlab_array.release().get(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// arma::Col&lt;T&gt; vec(ptr, n_elem); //, false); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// TODO(mfbolus): for some reason, using the above pointer at times leads to +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// getting garbage values. matlab array values may be stored in non-contiguous +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// memory? +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> vec(n_elem, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (size_t k <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; k <span style="color:#f92672">&lt;</span> n_elem; k<span style="color:#f92672">++</span>) { +</span></span><span style="display:flex;"><span> vec[k] <span style="color:#f92672">=</span> matlab_array[k]; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> vec; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> m2a_mat(matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> matlab_array) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// ArrayDimensions == std::vector&lt;size_t&gt; +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> dims <span style="color:#f92672">=</span> matlab_array.getDimensions(); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// T* ptr = matlab_array.release().get(); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// arma::Mat&lt;T&gt; mat(ptr, dims[0], dims[1]); //, false); +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// TODO(mfbolus): for some reason, using the above pointer at times leads to +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// getting garbage values. matlab array values may be stored in non-contiguous +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// memory? +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// armadillo and matlab both use column-major ordering, so this should work: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t n_elem <span style="color:#f92672">=</span> dims[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">*</span> dims[<span style="color:#ae81ff">1</span>]; +</span></span><span style="display:flex;"><span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> mat(dims[<span style="color:#ae81ff">0</span>], dims[<span style="color:#ae81ff">1</span>], arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> size_t <span style="color:#a6e22e">k</span>(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">auto</span> m: matlab_array) { +</span></span><span style="display:flex;"><span> mat[k] <span style="color:#f92672">=</span> m; +</span></span><span style="display:flex;"><span> k<span style="color:#f92672">++</span>; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> mat; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> a2m_mat(<span style="color:#66d9ef">const</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;&amp;</span> arma_mat, +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>ArrayFactory<span style="color:#f92672">&amp;</span> factory) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> matlab_mat <span style="color:#f92672">=</span> factory.createArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>( +</span></span><span style="display:flex;"><span> {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), +</span></span><span style="display:flex;"><span> arma_mat.memptr() <span style="color:#f92672">+</span> arma_mat.n_elem); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> matlab_mat; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> a2m_vec(<span style="color:#66d9ef">const</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;&amp;</span> arma_vec, +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>ArrayFactory<span style="color:#f92672">&amp;</span> factory) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> matlab_mat <span style="color:#f92672">=</span> +</span></span><span style="display:flex;"><span> factory.createArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>({arma_vec.n_elem, <span style="color:#ae81ff">1</span>}, arma_vec.memptr(), +</span></span><span style="display:flex;"><span> arma_vec.memptr() <span style="color:#f92672">+</span> arma_vec.n_elem); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> matlab_mat; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span><span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> s2m_vec(<span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;&amp;</span> std_vec, +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>ArrayFactory<span style="color:#f92672">&amp;</span> factory) { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span> matlab_mat <span style="color:#f92672">=</span> factory.createArray<span style="color:#f92672">&lt;</span>T<span style="color:#f92672">&gt;</span>( +</span></span><span style="display:flex;"><span> {std_vec.size(), <span style="color:#ae81ff">1</span>}, std_vec.data(), std_vec.data() <span style="color:#f92672">+</span> std_vec.size()); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">return</span> matlab_mat; +</span></span><span style="display:flex;"><span>}; +</span></span><span style="display:flex;"><span>} <span style="color:#75715e">// namespace armamexcpp +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#75715e">#endif +</span></span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest_hmex_cpp_utilh">ldsCtrlEst_h/mex_cpp_util.h</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#source-code">Source code</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/index.html b/docs/docs/api/index.html new file mode 100644 index 00000000..56717480 --- /dev/null +++ b/docs/docs/api/index.html @@ -0,0 +1,404 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head><script src="/livereload.js?mindelay=10&amp;v=2&amp;port=62388&amp;path=livereload" data-no-instant defer></script> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + API Reference + # + +The API documentation is organized into the following sections: + +Classes - Documentation for all classes +Namespaces - Documentation for all namespaces +Files - Documentation for all files +Modules - Documentation for all modules +Examples - Documentation for all examples +"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="//localhost:62388/docs/api/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="API Reference"> + <meta property="og:description" content="API Reference # The API documentation is organized into the following sections: +Classes - Documentation for all classes Namespaces - Documentation for all namespaces Files - Documentation for all files Modules - Documentation for all modules Examples - Documentation for all examples"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>API Reference | LDS C&amp;E</title> +<link rel="manifest" href="/manifest.json"> +<link rel="icon" href="/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/flexsearch.min.js"></script> + <script defer src="/en.search.min.ba0bee9096d7214e3ef986b48d32240230f8eaaeb7255908e5f80d952ea7d851.js" integrity="sha256-ugvukJbXIU4&#43;&#43;Ya0jTIkAjD46q63JVkI5fgNlS6n2FE=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="//localhost:62388/docs/api/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/"><img src="/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + +<ul> + + <li> + <a href="/docs/terminology/" target="_blank" rel="noopener"> + Library Terminology + </a> + </li> + + <li> + <a href="/docs/getting-started/" target="_blank" rel="noopener"> + Getting Started + </a> + </li> + + <li> + <a href="/docs/tutorials/" target="_blank" rel="noopener"> + Tutorials + </a> + </li> + + <li> + <a href="/docs/api/" target="_blank" rel="noopener"> + API Reference + </a> + </li> + + <li> + <a href="/issues-contributing/" target="_blank" rel="noopener"> + Reporting Issues &amp; Contributing + </a> + </li> + + <li> + <a href="/acknowledgements/" target="_blank" rel="noopener"> + Acknowledgements + </a> + </li> + +</ul> + + + + + + + + + + + + + <ul> + + + + <li class="book-section-flat" > + + + + + + + <a href="//localhost:62388/docs/tutorials/" class="">LDS C&#43;E Examples</a> + + + + + <ul> + + + <li> + + + + + + + <a href="//localhost:62388/docs/tutorials/eg_glds_control/" class="">GLDS Control</a> + + + </li> + + + + <li> + + + + + + + <a href="//localhost:62388/docs/tutorials/eg_plds_state_estimation/" class="">PLDS State Estimation</a> + + + </li> + + + + <li> + + + + + + + <a href="//localhost:62388/docs/tutorials/eg_switched_plds_control/" class="">PLDS Switched Control</a> + + + </li> + + + </ul> + + + </li> + + + + + <li class="book-section-flat" > + + + + + + + <a href="//localhost:62388/docs/api/" class=" active">API Reference</a> + + + + </li> + + + + <li> + + + + + + + <a href="//localhost:62388/docs/getting-started/getting-started/" class="">Getting Started</a> + + + </li> + + + + <li> + + + + + + + <a href="//localhost:62388/docs/getting-started/windows/" class="">Windows</a> + + + </li> + + + + <li> + + + + + + + <a href="//localhost:62388/docs/terminology/control-estimation/" class="">C&amp;E</a> + + + </li> + + + + <li> + + + + + + + <a href="//localhost:62388/docs/terminology/model/" class="">Models</a> + + + </li> + + + </ul> + + + + + + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>API Reference</strong> + + <label for="toc-control"> + + <img src="/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#api-reference">API Reference</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="api-reference"> + API Reference + <a class="anchor" href="#api-reference">#</a> +</h1> +<p>The API documentation is organized into the following sections:</p> +<ul> +<li><a href="Classes/_index.md">Classes</a> - Documentation for all classes</li> +<li><a href="Namespaces/_index.md">Namespaces</a> - Documentation for all namespaces</li> +<li><a href="Files/_index.md">Files</a> - Documentation for all files</li> +<li><a href="Modules/_index.md">Modules</a> - Documentation for all modules</li> +<li><a href="Examples/_index.md">Examples</a> - Documentation for all examples</li> +</ul> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#api-reference">API Reference</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/index.xml b/docs/docs/api/index.xml new file mode 100644 index 00000000..708b5f95 --- /dev/null +++ b/docs/docs/api/index.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>API Reference on LDS C&amp;E</title> + <link>//localhost:62388/docs/api/</link> + <description>Recent content in API Reference on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="//localhost:62388/docs/api/index.xml" rel="self" type="application/rss+xml" /> + </channel> +</rss> diff --git a/docs/docs/api/modules/group__control__masks/index.html b/docs/docs/api/modules/group__control__masks/index.html new file mode 100644 index 00000000..724736db --- /dev/null +++ b/docs/docs/api/modules/group__control__masks/index.html @@ -0,0 +1,321 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="provides fill types for constructing new armadillo vectors, matrices"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Control Mode Bit Masks"> + <meta property="og:description" content="provides fill types for constructing new armadillo vectors, matrices"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>Control Mode Bit Masks | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Control Mode Bit Masks</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#control-mode-bit-masks">Control Mode Bit Masks</a> + <ul> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#kcontroltypedeltau">kControlTypeDeltaU</a></li> + <li><a href="#kcontroltypeinty">kControlTypeIntY</a></li> + <li><a href="#kcontroltypeadaptm">kControlTypeAdaptM</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="control-mode-bit-masks"> + Control Mode Bit Masks + <a class="anchor" href="#control-mode-bit-masks">#</a> +</h1> +<p>provides fill types for constructing new armadillo vectors, matrices <br> <br><a href="#detailed-description">More&hellip;</a> +<br></p> +<h2 id="attributes"> + Attributes + <a class="anchor" href="#attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>const std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypedeltau">kControlTypeDeltaU</a></strong> <br>control designed to penalize change in input</td> + </tr> + <tr> + <td>const std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeinty">kControlTypeIntY</a></strong> <br>control using integral action</td> + </tr> + <tr> + <td>const std::size_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/modules/group__control__masks/#variable-kcontroltypeadaptm">kControlTypeAdaptM</a></strong> <br>adapt control setpoint with re-estimated disturbance <code>m</code></td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>Control mode bit masks. These can be bit-wise OR&rsquo;d to use in combination.</p> +<h2 id="attribute-details"> + Attribute Details + <a class="anchor" href="#attribute-details">#</a> +</h2> +<h3 id="kcontroltypedeltau"> + kControlTypeDeltaU + <a class="anchor" href="#kcontroltypedeltau">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>size_t kControlTypeDeltaU <span style="color:#f92672">=</span> <span style="color:#ae81ff">0x1</span>; +</span></span></code></pre></div><p>Control was designed to penalize change in input (i.e., the state was augmented with input <code>u</code>)</p> +<h3 id="kcontroltypeinty"> + kControlTypeIntY + <a class="anchor" href="#kcontroltypeinty">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>size_t kControlTypeIntY <span style="color:#f92672">=</span> kControlTypeDeltaU <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">1</span>; +</span></span></code></pre></div><p>Control using integral action (i.e., the state was augmented with output <code>y</code> during design)</p> +<h3 id="kcontroltypeadaptm"> + kControlTypeAdaptM + <a class="anchor" href="#kcontroltypeadaptm">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>size_t kControlTypeAdaptM <span style="color:#f92672">=</span> kControlTypeDeltaU <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">2</span>; +</span></span></code></pre></div><p>Adapt control setpoint adapted with re-estimated process disturbance <code>m</code>.</p> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#control-mode-bit-masks">Control Mode Bit Masks</a> + <ul> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#kcontroltypedeltau">kControlTypeDeltaU</a></li> + <li><a href="#kcontroltypeinty">kControlTypeIntY</a></li> + <li><a href="#kcontroltypeadaptm">kControlTypeAdaptM</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/modules/group__defaults/index.html b/docs/docs/api/modules/group__defaults/index.html new file mode 100644 index 00000000..21096776 --- /dev/null +++ b/docs/docs/api/modules/group__defaults/index.html @@ -0,0 +1,376 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Defaults + # + + More&hellip; + + + Attributes + # + + + + + + Name + + + + + const data_t + kDefaultP0 default state estimate covar + + + const data_t + kDefaultQ0 default process noise covar + + + const data_t + kDefaultR0 default output noise covar + + + + + Detailed Description + # + +Default values for common variables (e.g., default diagonal elements of covariances) + + Attribute Details + # + + + kDefaultP0 + # + +static const data_t kDefaultP0 = 1e-6; + + kDefaultQ0 + # + +static const data_t kDefaultQ0 = 1e-6; + + kDefaultR0 + # + +static const data_t kDefaultR0 = 1e-2; + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Defaults"> + <meta property="og:description" content="Defaults # More… Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances) +Attribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>Defaults | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Defaults</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#defaults">Defaults</a> + <ul> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#kdefaultp0">kDefaultP0</a></li> + <li><a href="#kdefaultq0">kDefaultQ0</a></li> + <li><a href="#kdefaultr0">kDefaultR0</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="defaults"> + Defaults + <a class="anchor" href="#defaults">#</a> +</h1> +<p><br> <br><a href="#detailed-description">More&hellip;</a> +<br></p> +<h2 id="attributes"> + Attributes + <a class="anchor" href="#attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>const data_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultp0">kDefaultP0</a></strong> <br>default state estimate covar</td> + </tr> + <tr> + <td>const data_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultq0">kDefaultQ0</a></strong> <br>default process noise covar</td> + </tr> + <tr> + <td>const data_t</td> + <td><strong><a href="/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultr0">kDefaultR0</a></strong> <br>default output noise covar</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>Default values for common variables (e.g., default diagonal elements of covariances)</p> +<h2 id="attribute-details"> + Attribute Details + <a class="anchor" href="#attribute-details">#</a> +</h2> +<h3 id="kdefaultp0"> + kDefaultP0 + <a class="anchor" href="#kdefaultp0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kDefaultP0 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-6</span>; +</span></span></code></pre></div><h3 id="kdefaultq0"> + kDefaultQ0 + <a class="anchor" href="#kdefaultq0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kDefaultQ0 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-6</span>; +</span></span></code></pre></div><h3 id="kdefaultr0"> + kDefaultR0 + <a class="anchor" href="#kdefaultr0">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kDefaultR0 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-2</span>; +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#defaults">Defaults</a> + <ul> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#kdefaultp0">kDefaultP0</a></li> + <li><a href="#kdefaultq0">kDefaultQ0</a></li> + <li><a href="#kdefaultr0">kDefaultR0</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/modules/index.html b/docs/docs/api/modules/index.html new file mode 100644 index 00000000..2e880b25 --- /dev/null +++ b/docs/docs/api/modules/index.html @@ -0,0 +1,265 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Modules + # + + + +Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices + + +Defaults + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Modules"> + <meta property="og:description" content="Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices +Defaults +Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Modules | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/"class=active>Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Modules</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#modules">Modules</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="modules"> + Modules + <a class="anchor" href="#modules">#</a> +</h1> +<ul> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/modules/group__control__masks/">Control Mode Bit Masks</a></strong> <br>provides fill types for constructing new armadillo vectors, matrices</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/modules/group__defaults/">Defaults</a></strong></p> +</li> +</ul> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#modules">Modules</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/modules/index.xml b/docs/docs/api/modules/index.xml new file mode 100644 index 00000000..1ba5bc5c --- /dev/null +++ b/docs/docs/api/modules/index.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Modules on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/</link> + <description>Recent content in Modules on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>Control Mode Bit Masks</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</guid> + <description>provides fill types for constructing new armadillo vectors, matrices</description> + </item> + <item> + <title>Defaults</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</guid> + <description>&lt;h1 id=&#34;defaults&#34;&gt;&#xA; Defaults&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#defaults&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;br&gt; &lt;br&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&#xA;&lt;br&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;attributes&#34;&gt;&#xA; Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const data_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultp0&#34;&gt;kDefaultP0&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;default state estimate covar&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const data_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultq0&#34;&gt;kDefaultQ0&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;default process noise covar&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const data_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultr0&#34;&gt;kDefaultR0&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;default output noise covar&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Default values for common variables (e.g., default diagonal elements of covariances)&lt;/p&gt;&#xA;&lt;h2 id=&#34;attribute-details&#34;&gt;&#xA; Attribute Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#attribute-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;kdefaultp0&#34;&gt;&#xA; kDefaultP0&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#kdefaultp0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; data_t kDefaultP0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-6&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;kdefaultq0&#34;&gt;&#xA; kDefaultQ0&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#kdefaultq0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; data_t kDefaultQ0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-6&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;kdefaultr0&#34;&gt;&#xA; kDefaultR0&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#kdefaultr0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; data_t kDefaultR0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + </channel> +</rss> diff --git a/docs/docs/api/namespaces/index.html b/docs/docs/api/namespaces/index.html new file mode 100644 index 00000000..8e414384 --- /dev/null +++ b/docs/docs/api/namespaces/index.html @@ -0,0 +1,286 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Namespaces + # + + + +armamexc arma/mex interface using Matlab C API + + +armamexcpp arma/mex interface using Matlab C&#43;&#43; API + + +lds::gaussian Linear Dynamical Systems with Gaussian observations. + + +lds::poisson Linear Dynamical Systems with Poisson observations. + + +std + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Namespaces"> + <meta property="og:description" content="Namespaces # armamexc arma/mex interface using Matlab C API +armamexcpp arma/mex interface using Matlab C&#43;&#43; API +lds::gaussian Linear Dynamical Systems with Gaussian observations. +lds::poisson Linear Dynamical Systems with Poisson observations. +std +Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Namespaces | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/"class=active>Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Namespaces</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h1> +<ul> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/">armamexc</a></strong> <br>arma/mex interface using Matlab C API</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/">armamexcpp</a></strong> <br>arma/mex interface using Matlab C++ API</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</p> +</li> +<li> +<p><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacestd/">std</a></strong></p> +</li> +</ul> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/namespaces/index.xml b/docs/docs/api/namespaces/index.xml new file mode 100644 index 00000000..a07424c7 --- /dev/null +++ b/docs/docs/api/namespaces/index.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Namespaces on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/</link> + <description>Recent content in Namespaces on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>armamexc</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</guid> + <description>arma/mex interface using Matlab C API</description> + </item> + <item> + <title>armamexcpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</guid> + <description>arma/mex interface using Matlab C++ API</description> + </item> + <item> + <title>lds</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</guid> + <description>Linear Dynamical Systems (LDS) namespace.</description> + </item> + <item> + <title>lds::gaussian</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</guid> + <description>Linear Dynamical Systems with Gaussian observations.</description> + </item> + <item> + <title>lds::poisson</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</guid> + <description>Linear Dynamical Systems with Poisson observations.</description> + </item> + <item> + <title>std</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacestd/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacestd/</guid> + <description>&lt;h1 id=&#34;std&#34;&gt;&#xA; std&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#std&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;br&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + </channel> +</rss> diff --git a/docs/docs/api/namespaces/namespacearmamexc/index.html b/docs/docs/api/namespaces/namespacearmamexc/index.html new file mode 100644 index 00000000..4aad9cd5 --- /dev/null +++ b/docs/docs/api/namespaces/namespacearmamexc/index.html @@ -0,0 +1,373 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="arma/mex interface using Matlab C API"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="armamexc"> + <meta property="og:description" content="arma/mex interface using Matlab C API"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>armamexc | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>armamexc</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#armamexc">armamexc</a> + <ul> + <li><a href="#functions">Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#m2t_scalar">m2T_scalar</a></li> + <li><a href="#m2a_mat">m2a_mat</a></li> + <li><a href="#a2m_mat">a2m_mat</a></li> + <li><a href="#a2m_vec">a2m_vec</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="armamexc"> + armamexc + <a class="anchor" href="#armamexc">#</a> +</h1> +<p>arma/mex interface using Matlab C API <br> <br><a href="#detailed-description">More&hellip;</a> +<br></p> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>template &lt;class T &gt; <br>T</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-m2t-scalar">m2T_scalar</a></strong>(const mxArray * matlab_scalar)<br>Convert Matlab mxArray to scalar of type T.</td> + </tr> + <tr> + <td>template &lt;class T &gt; <br>arma::Mat&lt; T &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-m2a-mat">m2a_mat</a></strong>(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)<br>Convert matlab matrix to armadillo.</td> + </tr> + <tr> + <td>template &lt;typename T &gt; <br>mxArray *</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-a2m-mat">a2m_mat</a></strong>(arma::Mat&lt; T &gt; const &amp; arma_mat)<br>Convert armadillo to matlab matrix.</td> + </tr> + <tr> + <td>template &lt;typename T &gt; <br>mxArray *</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/#function-a2m-vec">a2m_vec</a></strong>(arma::Col&lt; T &gt; const &amp; arma_vec)<br>Convert armadillo to matlab vector.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>Utilities for arma/mex interface <em>using Matlab C API</em></p> +<h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="m2t_scalar"> + m2T_scalar + <a class="anchor" href="#m2t_scalar">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> T m2T_scalar( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> mxArray <span style="color:#f92672">*</span> matlab_scalar +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>matlab_scalar</strong> matlab scalar</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: scalar of type T</p> +<h3 id="m2a_mat"> + m2a_mat + <a class="anchor" href="#m2a_mat">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> m2a_mat( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> mxArray <span style="color:#f92672">*</span> matlab_mat, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> copy_aux_mem <span style="color:#f92672">=</span>false, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">bool</span> strict <span style="color:#f92672">=</span>true +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>matlab_mat</strong> matlab matrix</li> +<li><strong>copy_aux_mem</strong> [optional] whether to copy auxiliary memory</li> +<li><strong>strict</strong> [optional] strictly enforce the above</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: armadillo matrix of type T</p> +<h3 id="a2m_mat"> + a2m_mat + <a class="anchor" href="#a2m_mat">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> mxArray <span style="color:#f92672">*</span> a2m_mat( +</span></span><span style="display:flex;"><span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">const</span> <span style="color:#f92672">&amp;</span> arma_mat +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>arma_mat</strong> armadillo matrix</li> +</ul> +<p><strong>Return</strong>: matlab matrix</p> +<h3 id="a2m_vec"> + a2m_vec + <a class="anchor" href="#a2m_vec">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">typename</span> T <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> mxArray <span style="color:#f92672">*</span> a2m_vec( +</span></span><span style="display:flex;"><span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> <span style="color:#66d9ef">const</span> <span style="color:#f92672">&amp;</span> arma_vec +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>arma_vec</strong> armadillo vector</li> +</ul> +<p><strong>Return</strong>: matlab vector</p> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#armamexc">armamexc</a> + <ul> + <li><a href="#functions">Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#m2t_scalar">m2T_scalar</a></li> + <li><a href="#m2a_mat">m2a_mat</a></li> + <li><a href="#a2m_mat">a2m_mat</a></li> + <li><a href="#a2m_vec">a2m_vec</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/namespaces/namespacearmamexcpp/index.html b/docs/docs/api/namespaces/namespacearmamexcpp/index.html new file mode 100644 index 00000000..330b549b --- /dev/null +++ b/docs/docs/api/namespaces/namespacearmamexcpp/index.html @@ -0,0 +1,452 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="arma/mex interface using Matlab C&#43;&#43; API"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="armamexcpp"> + <meta property="og:description" content="arma/mex interface using Matlab C&#43;&#43; API"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>armamexcpp | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>armamexcpp</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#armamexcpp">armamexcpp</a> + <ul> + <li><a href="#functions">Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#m2a_cellmat">m2a_cellmat</a></li> + <li><a href="#m2s_vec">m2s_vec</a></li> + <li><a href="#m2a_vec">m2a_vec</a></li> + <li><a href="#m2a_mat">m2a_mat</a></li> + <li><a href="#a2m_mat">a2m_mat</a></li> + <li><a href="#a2m_vec">a2m_vec</a></li> + <li><a href="#s2m_vec">s2m_vec</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="armamexcpp"> + armamexcpp + <a class="anchor" href="#armamexcpp">#</a> +</h1> +<p>arma/mex interface using Matlab C++ API <br> <br><a href="#detailed-description">More&hellip;</a> +<br></p> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>template &lt;class T &gt; <br>std::vector&lt; arma::Mat&lt; T &gt; &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-cellmat">m2a_cellmat</a></strong>(matlab::data::CellArray &amp; matlab_cell)<br>Convert matlab cell array to vector of armadillo matrices.</td> + </tr> + <tr> + <td>template &lt;class T &gt; <br>std::vector&lt; T &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2s-vec">m2s_vec</a></strong>(matlab::data::TypedArray&lt; T &gt; &amp; matlab_array)<br>Convert matlab matrix to a vector of scalars.</td> + </tr> + <tr> + <td>template &lt;class T &gt; <br>arma::Col&lt; T &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-vec">m2a_vec</a></strong>(matlab::data::TypedArray&lt; T &gt; matlab_array)<br>Convert matlab to armadillo vector.</td> + </tr> + <tr> + <td>template &lt;class T &gt; <br>arma::Mat&lt; T &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-m2a-mat">m2a_mat</a></strong>(matlab::data::TypedArray&lt; T &gt; matlab_array)<br>Convert matlab to armadillo matrix.</td> + </tr> + <tr> + <td>template &lt;class T &gt; <br>matlab::data::TypedArray&lt; T &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-a2m-mat">a2m_mat</a></strong>(const arma::Mat&lt; T &gt; &amp; arma_mat, matlab::data::ArrayFactory &amp; factory)<br>Convert armadillo to matlab matrix.</td> + </tr> + <tr> + <td>template &lt;class T &gt; <br>matlab::data::TypedArray&lt; T &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-a2m-vec">a2m_vec</a></strong>(const arma::Col&lt; T &gt; &amp; arma_vec, matlab::data::ArrayFactory &amp; factory)<br>Convert armadillo to matlab vector.</td> + </tr> + <tr> + <td>template &lt;class T &gt; <br>matlab::data::TypedArray&lt; T &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/#function-s2m-vec">s2m_vec</a></strong>(const std::vector&lt; T &gt; &amp; std_vec, matlab::data::ArrayFactory &amp; factory)<br>Convert vector of scalar T to matlab matrix.</td> + </tr> + </tbody> +</table> +<h2 id="detailed-description"> + Detailed Description + <a class="anchor" href="#detailed-description">#</a> +</h2> +<p>utilities for arma/mex interface <em>using Matlab C++ API</em></p> +<h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="m2a_cellmat"> + m2a_cellmat + <a class="anchor" href="#m2a_cellmat">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&gt;</span> m2a_cellmat( +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>CellArray <span style="color:#f92672">&amp;</span> matlab_cell +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>matlab_cell</strong> matlab cell</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: vector of armadillo matrices of type T</p> +<h3 id="m2s_vec"> + m2s_vec + <a class="anchor" href="#m2s_vec">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> m2s_vec( +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> matlab_array +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>matlab_array</strong> matlab array</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: vector of type T</p> +<h3 id="m2a_vec"> + m2a_vec + <a class="anchor" href="#m2a_vec">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> m2a_vec( +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> matlab_array +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>matlab_array</strong> matlab array</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: armadillo vector of type T</p> +<h3 id="m2a_mat"> + m2a_mat + <a class="anchor" href="#m2a_mat">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> m2a_mat( +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> matlab_array +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>matlab_array</strong> matlab matrix</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: armadillo matrix of type T</p> +<h3 id="a2m_mat"> + a2m_mat + <a class="anchor" href="#a2m_mat">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> a2m_mat( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> arma_mat, +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>ArrayFactory <span style="color:#f92672">&amp;</span> factory +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>arma_mat</strong> arma matrix</li> +<li><strong>factory</strong> matlab &ldquo;array factory&rdquo;</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: matlab matrix</p> +<h3 id="a2m_vec"> + a2m_vec + <a class="anchor" href="#a2m_vec">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> a2m_vec( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> arma_vec, +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>ArrayFactory <span style="color:#f92672">&amp;</span> factory +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>arma_vec</strong> armadillo vector</li> +<li><strong>factory</strong> matlab &ldquo;array factory&rdquo;</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: matlab matrix</p> +<h3 id="s2m_vec"> + s2m_vec + <a class="anchor" href="#s2m_vec">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">template</span> <span style="color:#f92672">&lt;</span><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">T</span> <span style="color:#f92672">&gt;</span> +</span></span><span style="display:flex;"><span>matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>TypedArray<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> s2m_vec( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> T <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> std_vec, +</span></span><span style="display:flex;"><span> matlab<span style="color:#f92672">::</span>data<span style="color:#f92672">::</span>ArrayFactory <span style="color:#f92672">&amp;</span> factory +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>std_vec</strong> standard vector</li> +<li><strong>factory</strong> matlab &ldquo;array factory&rdquo;</li> +</ul> +<p><strong>Template Parameters</strong>:</p> +<ul> +<li><strong>T</strong> type</li> +</ul> +<p><strong>Return</strong>: matlab matrix</p> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#armamexcpp">armamexcpp</a> + <ul> + <li><a href="#functions">Functions</a></li> + <li><a href="#detailed-description">Detailed Description</a></li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#m2a_cellmat">m2a_cellmat</a></li> + <li><a href="#m2s_vec">m2s_vec</a></li> + <li><a href="#m2a_vec">m2a_vec</a></li> + <li><a href="#m2a_mat">m2a_mat</a></li> + <li><a href="#a2m_mat">a2m_mat</a></li> + <li><a href="#a2m_vec">a2m_vec</a></li> + <li><a href="#s2m_vec">s2m_vec</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/namespaces/namespacelds/index.html b/docs/docs/api/namespaces/namespacelds/index.html new file mode 100644 index 00000000..f8a56d82 --- /dev/null +++ b/docs/docs/api/namespaces/namespacelds/index.html @@ -0,0 +1,723 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Linear Dynamical Systems (LDS) namespace."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds"> + <meta property="og:description" content="Linear Dynamical Systems (LDS) namespace."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#lds">lds</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#ssidwt">SSIDWt</a></li> + <li><a href="#matrixlistfreedim">MatrixListFreeDim</a></li> + <li><a href="#data_t">data_t</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#cube">Cube</a></li> + <li><a href="#view">View</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#limit">Limit</a></li> + <li><a href="#limit-1">Limit</a></li> + <li><a href="#limit-2">Limit</a></li> + <li><a href="#reassign">Reassign</a></li> + <li><a href="#reassign-1">Reassign</a></li> + <li><a href="#forcesympd">ForceSymPD</a></li> + <li><a href="#forcesymmineig">ForceSymMinEig</a></li> + <li><a href="#lq">lq</a></li> + <li><a href="#calccov">calcCov</a></li> + </ul> + </li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#kinf">kInf</a></li> + <li><a href="#kpi">kPi</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="lds"> + lds + <a class="anchor" href="#lds">#</a> +</h1> +<p>Linear Dynamical Systems (LDS) namespace. <br></p> +<h2 id="namespaces"> + Namespaces + <a class="anchor" href="#namespaces">#</a> +</h2> +<table> + <thead> + <tr> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/">lds::gaussian</a></strong> <br>Linear Dynamical Systems with Gaussian observations.</td> + </tr> + <tr> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/">lds::poisson</a></strong> <br>Linear Dynamical Systems with Poisson observations.</td> + </tr> + </tbody> +</table> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/">lds::Controller</a></strong></td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/">lds::EM</a></strong></td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/">lds::Fit</a></strong> <br>LDS <a href="">Fit</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">lds::SSID</a></strong></td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/">lds::SwitchedController</a></strong> <br><a href="">SwitchedController</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_system/">lds::System</a></strong> <br>Linear Dynamical <a href="">System</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/">lds::UniformMatrixList</a></strong></td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/">lds::UniformSystemList</a></strong></td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/">lds::UniformVectorList</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="types"> + Types + <a class="anchor" href="#types">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>enum</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt">SSIDWt</a></strong> { kSSIDNone, kSSIDMOESP, kSSIDCVA}<br>weighting options for <a href="">SSID</a></td> + </tr> + <tr> + <td>enum</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-matrixlistfreedim">MatrixListFreeDim</a></strong> { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2}</td> + </tr> + <tr> + <td>using double</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></strong></td> + </tr> + <tr> + <td>using arma::Col&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-vector">Vector</a></strong></td> + </tr> + <tr> + <td>using arma::Mat&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-matrix">Matrix</a></strong></td> + </tr> + <tr> + <td>using arma::Cube&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-cube">Cube</a></strong></td> + </tr> + <tr> + <td>using arma::subview&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> &gt;</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-view">View</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="functions"> + Functions + <a class="anchor" href="#functions">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit">Limit</a></strong>(std::vector&lt; <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> &gt; &amp; x, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> ub)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit">Limit</a></strong>(Vector &amp; x, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> ub)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-limit">Limit</a></strong>(Matrix &amp; x, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> lb, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> ub)</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-reassign">Reassign</a></strong>(Vector &amp; some, const Vector &amp; other, const std::string &amp; parenthetical =&ldquo;Reassign&rdquo;)<br>reassigns contents of some Vector in place</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-reassign">Reassign</a></strong>(Matrix &amp; some, const Matrix &amp; other, const std::string &amp; parenthetical =&ldquo;Reassign&rdquo;)<br>reassigns contents of some Matrix in place</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-forcesympd">ForceSymPD</a></strong>(Matrix &amp; X)<br>forces matrix to be symmetric positive-definite</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-forcesymmineig">ForceSymMinEig</a></strong>(Matrix &amp; X, <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a> eig_min =0)<br>forces matrix to be symmetric and have a minimum eigenvalue</td> + </tr> + <tr> + <td>void</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-lq">lq</a></strong>(Matrix &amp; L, Matrix &amp; Qt, const Matrix &amp; X)<br>LQ decomposition.</td> + </tr> + <tr> + <td>Matrix</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#function-calccov">calcCov</a></strong>(const Matrix &amp; A, const Matrix &amp; B)<br>Calculate covariance matrix.</td> + </tr> + </tbody> +</table> +<h2 id="attributes"> + Attributes + <a class="anchor" href="#attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#variable-kinf">kInf</a></strong> <br>Some useful numbers.</td> + </tr> + <tr> + <td>const <a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t">data_t</a></td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds/#variable-kpi">kPi</a></strong></td> + </tr> + </tbody> +</table> +<h2 id="type-details"> + Type Details + <a class="anchor" href="#type-details">#</a> +</h2> +<h3 id="ssidwt"> + SSIDWt + <a class="anchor" href="#ssidwt">#</a> +</h3> +<table> + <thead> + <tr> + <th>Enumerator</th> + <th>Value</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>kSSIDNone</td> + <td></td> + <td>None.</td> + </tr> + <tr> + <td>kSSIDMOESP</td> + <td></td> + <td>MOESP (AKA &ldquo;robust method&rdquo; in van Overschee 1996)</td> + </tr> + <tr> + <td>kSSIDCVA</td> + <td></td> + <td>CVA &ldquo;Canonical Variate Analysis&rdquo;.</td> + </tr> + </tbody> +</table> +<p>Weighting options for singular value decomposition performed during subspace identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>)</p> +<p>Reference:</p> +<p>van Overschee, de Moor. 1996. Subspace Identification for Linear Systems.</p> +<h3 id="matrixlistfreedim"> + MatrixListFreeDim + <a class="anchor" href="#matrixlistfreedim">#</a> +</h3> +<table> + <thead> + <tr> + <th>Enumerator</th> + <th>Value</th> + <th>Description</th> + </tr> + </thead> + <tbody> + <tr> + <td>kMatFreeDimNone</td> + <td></td> + <td>neither dim free to be hetero in mat list</td> + </tr> + <tr> + <td>kMatFreeDim1</td> + <td></td> + <td>allow 1st dim of mats in list to be hetero</td> + </tr> + <tr> + <td>kMatFreeDim2</td> + <td></td> + <td>allow 2nd dim of mats in list to be hetero</td> + </tr> + </tbody> +</table> +<h3 id="data_t"> + data_t + <a class="anchor" href="#data_t">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">double</span>; +</span></span></code></pre></div><p>Type of all data in library. If need 32b, change <code>double</code> to <code>float</code>. This could be potentially useful for large scale problems where there are memory constraints.</p> +<h3 id="vector"> + Vector + <a class="anchor" href="#vector">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Col<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="matrix"> + Matrix + <a class="anchor" href="#matrix">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Mat<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="cube"> + Cube + <a class="anchor" href="#cube">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Cube <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>Cube<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h3 id="view"> + View + <a class="anchor" href="#view">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>View <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>subview<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;</span>; +</span></span></code></pre></div><h2 id="function-details"> + Function Details + <a class="anchor" href="#function-details">#</a> +</h2> +<h3 id="limit"> + Limit + <a class="anchor" href="#limit">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Limit( +</span></span><span style="display:flex;"><span> std<span style="color:#f92672">::</span>vector<span style="color:#f92672">&lt;</span> data_t <span style="color:#f92672">&gt;</span> <span style="color:#f92672">&amp;</span> x, +</span></span><span style="display:flex;"><span> data_t lb, +</span></span><span style="display:flex;"><span> data_t ub +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><h3 id="limit-1"> + Limit + <a class="anchor" href="#limit-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Limit( +</span></span><span style="display:flex;"><span> Vector <span style="color:#f92672">&amp;</span> x, +</span></span><span style="display:flex;"><span> data_t lb, +</span></span><span style="display:flex;"><span> data_t ub +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><h3 id="limit-2"> + Limit + <a class="anchor" href="#limit-2">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Limit( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> x, +</span></span><span style="display:flex;"><span> data_t lb, +</span></span><span style="display:flex;"><span> data_t ub +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><h3 id="reassign"> + Reassign + <a class="anchor" href="#reassign">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Reassign( +</span></span><span style="display:flex;"><span> Vector <span style="color:#f92672">&amp;</span> some, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Vector <span style="color:#f92672">&amp;</span> other, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>string <span style="color:#f92672">&amp;</span> parenthetical <span style="color:#f92672">=</span><span style="color:#e6db74">&#34;Reassign&#34;</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>some</strong> some Vector</li> +<li><strong>other</strong> other Vector</li> +<li><strong>parenthetical</strong> optional description provided by caller to ease debugging</li> +</ul> +<h3 id="reassign-1"> + Reassign + <a class="anchor" href="#reassign-1">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">inline</span> <span style="color:#66d9ef">void</span> Reassign( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> some, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> other, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> std<span style="color:#f92672">::</span>string <span style="color:#f92672">&amp;</span> parenthetical <span style="color:#f92672">=</span><span style="color:#e6db74">&#34;Reassign&#34;</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>some</strong> some Matrix</li> +<li><strong>other</strong> other Matrix</li> +<li><strong>parenthetical</strong> optional description provided by caller to ease debugging</li> +</ul> +<h3 id="forcesympd"> + ForceSymPD + <a class="anchor" href="#forcesympd">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> ForceSymPD( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> X +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>X</strong> mutated matrix</li> +</ul> +<h3 id="forcesymmineig"> + ForceSymMinEig + <a class="anchor" href="#forcesymmineig">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> ForceSymMinEig( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> X, +</span></span><span style="display:flex;"><span> data_t eig_min <span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>X</strong> mutated matrix</li> +<li><strong>eig_min</strong> [optional] minimum eigen value</li> +</ul> +<h3 id="lq"> + lq + <a class="anchor" href="#lq">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">void</span> lq( +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> L, +</span></span><span style="display:flex;"><span> Matrix <span style="color:#f92672">&amp;</span> Qt, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> X +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>L</strong> lower triangle matrix</li> +<li><strong>Qt</strong> orthonormal matrix (transposed cf QR decomp)</li> +<li><strong>X</strong> matrix being decomposed</li> +</ul> +<h3 id="calccov"> + calcCov + <a class="anchor" href="#calccov">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span>Matrix calcCov( +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> A, +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> Matrix <span style="color:#f92672">&amp;</span> B +</span></span><span style="display:flex;"><span>) +</span></span></code></pre></div><p><strong>Parameters</strong>:</p> +<ul> +<li><strong>A</strong> some matrix</li> +<li><strong>B</strong> some other matrix</li> +</ul> +<p><strong>Return</strong>: covariance</p> +<h2 id="attribute-details"> + Attribute Details + <a class="anchor" href="#attribute-details">#</a> +</h2> +<h3 id="kinf"> + kInf + <a class="anchor" href="#kinf">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kInf <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>numeric_limits<span style="color:#f92672">&lt;</span>data_t<span style="color:#f92672">&gt;::</span>infinity(); +</span></span></code></pre></div><h3 id="kpi"> + kPi + <a class="anchor" href="#kpi">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> data_t kPi <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>datum<span style="color:#f92672">::</span>pi; +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#lds">lds</a> + <ul> + <li><a href="#namespaces">Namespaces</a></li> + <li><a href="#classes">Classes</a></li> + <li><a href="#types">Types</a></li> + <li><a href="#functions">Functions</a></li> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#type-details">Type Details</a> + <ul> + <li><a href="#ssidwt">SSIDWt</a></li> + <li><a href="#matrixlistfreedim">MatrixListFreeDim</a></li> + <li><a href="#data_t">data_t</a></li> + <li><a href="#vector">Vector</a></li> + <li><a href="#matrix">Matrix</a></li> + <li><a href="#cube">Cube</a></li> + <li><a href="#view">View</a></li> + </ul> + </li> + <li><a href="#function-details">Function Details</a> + <ul> + <li><a href="#limit">Limit</a></li> + <li><a href="#limit-1">Limit</a></li> + <li><a href="#limit-2">Limit</a></li> + <li><a href="#reassign">Reassign</a></li> + <li><a href="#reassign-1">Reassign</a></li> + <li><a href="#forcesympd">ForceSymPD</a></li> + <li><a href="#forcesymmineig">ForceSymMinEig</a></li> + <li><a href="#lq">lq</a></li> + <li><a href="#calccov">calcCov</a></li> + </ul> + </li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#kinf">kInf</a></li> + <li><a href="#kpi">kPi</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html b/docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html new file mode 100644 index 00000000..254a47cf --- /dev/null +++ b/docs/docs/api/namespaces/namespacelds_1_1gaussian/index.html @@ -0,0 +1,289 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Linear Dynamical Systems with Gaussian observations."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::gaussian"> + <meta property="og:description" content="Linear Dynamical Systems with Gaussian observations."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::gaussian | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::gaussian</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussian">lds::gaussian</a> + <ul> + <li><a href="#classes">Classes</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsgaussian"> + lds::gaussian + <a class="anchor" href="#ldsgaussian">#</a> +</h1> +<p>Linear Dynamical Systems with Gaussian observations. <br></p> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/">lds::gaussian::Controller</a></strong> <br>Gaussian-observation <a href="">Controller</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">lds::gaussian::Fit</a></strong> <br>GLDS <a href="">Fit</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/">lds::gaussian::FitEM</a></strong> <br>GLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/">Fit</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/">lds::gaussian::FitSSID</a></strong> <br>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for GLDS.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/">lds::gaussian::SwitchedController</a></strong> <br>Gaussian-observation <a href="">SwitchedController</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/">lds::gaussian::System</a></strong> <br>Gaussian LDS Type.</td> + </tr> + </tbody> +</table> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsgaussian">lds::gaussian</a> + <ul> + <li><a href="#classes">Classes</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/namespaces/namespacelds_1_1poisson/index.html b/docs/docs/api/namespaces/namespacelds_1_1poisson/index.html new file mode 100644 index 00000000..980dc643 --- /dev/null +++ b/docs/docs/api/namespaces/namespacelds_1_1poisson/index.html @@ -0,0 +1,340 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content="Linear Dynamical Systems with Poisson observations."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="lds::poisson"> + <meta property="og:description" content="Linear Dynamical Systems with Poisson observations."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>lds::poisson | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>lds::poisson</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoisson">lds::poisson</a> + <ul> + <li><a href="#classes">Classes</a></li> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#rd">rd</a></li> + <li><a href="#rng">rng</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldspoisson"> + lds::poisson + <a class="anchor" href="#ldspoisson">#</a> +</h1> +<p>Linear Dynamical Systems with Poisson observations. <br></p> +<h2 id="classes"> + Classes + <a class="anchor" href="#classes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/">lds::poisson::Controller</a></strong> <br>PLDS <a href="">Controller</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">lds::poisson::Fit</a></strong> <br>PLDS <a href="">Fit</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/">lds::poisson::FitEM</a></strong> <br>PLDS E-M <a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/">Fit</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/">lds::poisson::FitSSID</a></strong> <br>Subspace Identification (<a href="/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/">SSID</a>) for PLDS.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/">lds::poisson::SwitchedController</a></strong> <br>Poisson-observation <a href="">SwitchedController</a> Type.</td> + </tr> + <tr> + <td>class</td> + <td><strong><a href="/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/">lds::poisson::System</a></strong> <br>Poisson <a href="">System</a> type.</td> + </tr> + </tbody> +</table> +<h2 id="attributes"> + Attributes + <a class="anchor" href="#attributes">#</a> +</h2> +<table> + <thead> + <tr> + <th></th> + <th>Name</th> + </tr> + </thead> + <tbody> + <tr> + <td>std::random_device</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/#variable-rd">rd</a></strong> <br>random device for simulating poisson data</td> + </tr> + <tr> + <td>std::mt19937</td> + <td><strong><a href="/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/#variable-rng">rng</a></strong> <br>random number generator for simulating poisson data</td> + </tr> + </tbody> +</table> +<h2 id="attribute-details"> + Attribute Details + <a class="anchor" href="#attribute-details">#</a> +</h2> +<h3 id="rd"> + rd + <a class="anchor" href="#rd">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> std<span style="color:#f92672">::</span>random_device rd; +</span></span></code></pre></div><h3 id="rng"> + rng + <a class="anchor" href="#rng">#</a> +</h3> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#66d9ef">static</span> std<span style="color:#f92672">::</span>mt19937 rng <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>mt19937( +</span></span><span style="display:flex;"><span> rd()); +</span></span></code></pre></div><hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldspoisson">lds::poisson</a> + <ul> + <li><a href="#classes">Classes</a></li> + <li><a href="#attributes">Attributes</a></li> + <li><a href="#attribute-details">Attribute Details</a> + <ul> + <li><a href="#rd">rd</a></li> + <li><a href="#rng">rng</a></li> + </ul> + </li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/namespaces/namespacestd/index.html b/docs/docs/api/namespaces/namespacestd/index.html new file mode 100644 index 00000000..5989be1d --- /dev/null +++ b/docs/docs/api/namespaces/namespacestd/index.html @@ -0,0 +1,249 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + std + # + + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacestd/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="std"> + <meta property="og:description" content="std # Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>std | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>std</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#std">std</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="std"> + std + <a class="anchor" href="#std">#</a> +</h1> +<br> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#std">std</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/pages/index.html b/docs/docs/api/pages/index.html new file mode 100644 index 00000000..0b6bb617 --- /dev/null +++ b/docs/docs/api/pages/index.html @@ -0,0 +1,247 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Pages + # + + +Updated on 31 March 2025 at 16:04:30 EDT"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/pages/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Pages"> + <meta property="og:description" content="Pages # Updated on 31 March 2025 at 16:04:30 EDT"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Pages | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/pages/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Pages</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#pages">Pages</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="pages"> + Pages + <a class="anchor" href="#pages">#</a> +</h1> +<hr> +<p>Updated on 31 March 2025 at 16:04:30 EDT</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#pages">Pages</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/api/pages/index.xml b/docs/docs/api/pages/index.xml new file mode 100644 index 00000000..4ef68faa --- /dev/null +++ b/docs/docs/api/pages/index.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Pages on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/pages/</link> + <description>Recent content in Pages on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/api/pages/index.xml" rel="self" type="application/rss+xml" /> + </channel> +</rss> diff --git a/docs/docs/getting-started/getting-started/index.html b/docs/docs/getting-started/getting-started/index.html new file mode 100644 index 00000000..11e5c6ee --- /dev/null +++ b/docs/docs/getting-started/getting-started/index.html @@ -0,0 +1,405 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Getting Started + # + +This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows. +ldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C&#43;&#43; package manager which allows us to easily install and use the dependencies in isolation. + + Tested Configurations + # + +Building C&#43;&#43; libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="LDS C&E"> + <meta property="og:description" content="Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows. +ldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C&#43;&#43; package manager which allows us to easily install and use the dependencies in isolation. +Tested Configurations # Building C&#43;&#43; libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>Getting Started | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"class=active><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Getting Started</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#getting-started">Getting Started</a> + <ul> + <li><a href="#tested-configurations">Tested Configurations</a></li> + <li><a href="#mac-pre-requisities">Mac Pre-requisities</a></li> + <li><a href="#linux-pre-requisites">Linux Pre-requisites</a></li> + <li><a href="#windows-installation">Windows Installation</a></li> + <li><a href="#downloading-the-library">Downloading the Library</a></li> + <li><a href="#compilation--installation">Compilation + Installation</a></li> + <li><a href="#options">Options</a></li> + <li><a href="#python-bindings-package-ldsctrlest">Python bindings package <code>ldsctrlest</code></a></li> + <li><a href="#common-issues">Common issues</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="getting-started"> + Getting Started + <a class="anchor" href="#getting-started">#</a> +</h1> +<p>This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.</p> +<p><code>ldsCtrlEst</code> requires <a href="http://arma.sourceforge.net/">Armadillo</a> for linear algebra as well as <a href="https://www.hdfgroup.org/downloads/hdf5/">HDF5</a> for saving output. <a href="https://vcpkg.io/"><code>vcpkg</code></a> is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.</p> +<h2 id="tested-configurations"> + Tested Configurations + <a class="anchor" href="#tested-configurations">#</a> +</h2> +<p>Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the <code>RelWithDebInfo</code> build type in the CMake configure command (<code>-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo</code>):</p> +<ul> +<li>Ubuntu 18.04 with GCC 7.5 compiler</li> +<li>macOS 11 (Big Sur) with Apple Clang 12 compiler</li> +<li>Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler</li> +</ul> +<p>That being said, if you want to debug a build for a single platform, here are some things you can try:</p> +<ul> +<li>Use different compilers (or even different versions of a single compiler)</li> +<li>Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule)</li> +</ul> +<h2 id="mac-pre-requisities"> + Mac Pre-requisities + <a class="anchor" href="#mac-pre-requisities">#</a> +</h2> +<p>Xcode Command Line Tools will get you clang, gcc, make, and git:</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>xcode-select --install +</span></span></code></pre></div><p>Homebrew is &ldquo;The Missing Package Manager for macOS&rdquo; which will make installing lots of things easy. Install like this:</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>/bin/bash -c <span style="color:#e6db74">&#34;</span><span style="color:#66d9ef">$(</span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span style="color:#66d9ef">)</span><span style="color:#e6db74">&#34;</span> +</span></span></code></pre></div><p>You can then use it to install CMake, gfortran, and pkg-config:</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>brew install cmake gfortran pkg-config +</span></span></code></pre></div><h2 id="linux-pre-requisites"> + Linux Pre-requisites + <a class="anchor" href="#linux-pre-requisites">#</a> +</h2> +<p>You&rsquo;ll need Git, CMake, GCC, gfortran, etc.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build +</span></span></code></pre></div><h2 id="windows-installation"> + Windows Installation + <a class="anchor" href="#windows-installation">#</a> +</h2> +<p>Look <a href="/lds-ctrl-est/docs/getting-started/windows/">here</a> for Windows-specific instructions.</p> +<h2 id="downloading-the-library"> + Downloading the Library + <a class="anchor" href="#downloading-the-library">#</a> +</h2> +<p>First, clone the repository along with submodules:</p> +<pre tabindex="0"><code>git clone https://github.com/cloctools/lds-ctrl-est.git +cd lds-ctrl-est +git submodule update --init +</code></pre><h2 id="compilation--installation"> + Compilation + Installation + <a class="anchor" href="#compilation--installation">#</a> +</h2> +<p>Now generate the cache and build using your IDE or from the command line as follows.</p> +<pre tabindex="0"><code class="language-source" data-lang="source">mkdir build &amp;&amp; cd build +cmake .. +cmake --build . +</code></pre><p>The first time, <code>vcpkg</code> will automatically install dependencies into <code>[build directory]/vcpkg_installed/</code>, which will likely take about 10-20 minutes.</p> +<p>If you want to use <code>vcpkg</code> set up somewhere besides this repo&rsquo;s submodule, add <code>-DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake</code> to the <code>cmake</code> command directly or <a href="https://github.com/microsoft/vcpkg#using-vcpkg-with-cmake">through your IDE&rsquo;s settings</a>.</p> +<p>You can verify the build is working by running <code>ctest</code> from the build folder, which runs all the example scripts.</p> +<h2 id="options"> + Options + <a class="anchor" href="#options">#</a> +</h2> +<p>This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.</p> +<ol> +<li><code>LDSCTRLEST_BUILD_EXAMPLES</code> : [default=ON] whether to build example programs located under <code>examples/</code> in the source tree</li> +<li><code>LDSCTRLEST_BUILD_FIT</code> : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation</li> +<li><code>LDSCTRLEST_BUILD_STATIC</code> : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use</li> +</ol> +<p><em>n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.</em></p> +<p>Below are example usages of <code>cmake</code> to configure/build the library.</p> +<ul> +<li> +<p>For basic project build &amp; install</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cd /path/to/repository +</span></span><span style="display:flex;"><span>mkdir build <span style="color:#f92672">&amp;&amp;</span> cd build +</span></span><span style="display:flex;"><span>cmake .. <span style="color:#75715e">#configure build</span> +</span></span><span style="display:flex;"><span>cmake --build <span style="color:#75715e">#build the project</span> +</span></span><span style="display:flex;"><span>sudo make install <span style="color:#75715e">#[optional] installs to default location (OS-specific)</span> +</span></span></code></pre></div></li> +<li> +<p>To set the install prefix</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cd /path/to/repository +</span></span><span style="display:flex;"><span>mkdir build <span style="color:#f92672">&amp;&amp;</span> cd build +</span></span><span style="display:flex;"><span>cmake -DCMAKE_INSTALL_PREFIX<span style="color:#f92672">=</span>/your/install/prefix .. <span style="color:#75715e">#configure build with chosen install location</span> +</span></span><span style="display:flex;"><span>cmake --build <span style="color:#75715e">#build the project</span> +</span></span><span style="display:flex;"><span>make install <span style="color:#75715e">#install to /your/install/prefix</span> +</span></span></code></pre></div></li> +<li> +<p>To build the <em>bare bones</em> project, excluding fit code and Matlab mex code.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cd /path/to/repository +</span></span><span style="display:flex;"><span>mkdir build <span style="color:#f92672">&amp;&amp;</span> cd build +</span></span><span style="display:flex;"><span>cmake -DLDSCTRLEST_BUILD_FIT<span style="color:#f92672">=</span><span style="color:#ae81ff">0</span> .. <span style="color:#75715e">#configure not to build the fitting portion of library</span> +</span></span><span style="display:flex;"><span>make <span style="color:#75715e">#build the project</span> +</span></span></code></pre></div><p><em>n.b.</em>, If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.</p> +<ol> +<li><code>LD_LIBRARY_PATH</code>: search path for dynamically loaded libraries</li> +<li><code>PKG_CONFIG_PATH</code>: search path for <code>pkg-config</code> tool</li> +</ol> +</li> +</ul> +<p>On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.</p> +<h2 id="python-bindings-package-ldsctrlest"> + Python bindings package <code>ldsctrlest</code> + <a class="anchor" href="#python-bindings-package-ldsctrlest">#</a> +</h2> +<p>With the <code>LDSCTRLEST_BUILD_PYTHON</code> setting (off by default) and the <code>pybind11</code> submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a <code>-DPython3_ROOT_DIR=[path/to/install/dir]</code> argument to the CMake cache generation command (the first one) so CMake doesn&rsquo;t use an undesired version. That environment needs to have NumPy installed.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>cmake --build . --target python_modules +</span></span></code></pre></div><p>The bindings need to be generated just once per Python version. Once the build is complete, navigate to the <code>[build location]/python</code> folder and run <code>pip install .</code> to make it importable anywhere for your current environment. <em>The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though.</em> You can verify the installation was successful by running <code>pytest</code> from the <code>build/python</code> directory (<code>pip install pytest matplotlib</code> first if you need to).</p> +<p>See <code>python/ldsctrlest/README.md</code> for usage details.</p> +<p>Also, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with <code>-DLDSCTRLEST_BUILD_PYTHON=ON</code>, install the package, then again with <code>-DLDSCTRLEST_BUILD_PYTHON=OFF</code> for the pure C++ build to work correctly.</p> +<h2 id="common-issues"> + Common issues + <a class="anchor" href="#common-issues">#</a> +</h2> +<ol> +<li>&ldquo;I have built the library and installed it in a non-default location. In building my own project linking against <code>ldsCtrlEst</code>, <code>cmake</code> or <code>pkg-config</code> cannot find the library or its configuration information.&rdquo;</li> +</ol> +<p>If <code>cmake</code> and/or <code>pkg-config</code> cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For <code>cmake</code> this means adding your chosen install prefix to the environment variable <code>CMAKE_PREFIX_PATH</code>. Similarly, for <code>pkg-config</code> you need to add <code>your/install/prefix/lib/pkgconfig</code> to its search path, <code>PKG_CONFIG_PATH</code>. Assuming a Unix shell whose login startup file is <code>~/.profile</code> and ldsCtrlEst was installed using prefix <code>your/install/prefix</code>, add the following to <code>.profile</code>.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>export CMAKE_PREFIX_PATH<span style="color:#f92672">=</span>$CMAKE_PREFIX_PATH:/your/install/prefix +</span></span><span style="display:flex;"><span>export PKG_CONFIG_PATH<span style="color:#f92672">=</span>$PKG_CONFIG_PATH:/your/install/prefix +</span></span></code></pre></div><ol start="2"> +<li>vcpkg fails on configuration</li> +</ol> +<p>Try running <code>./bootstrap-vcpkg</code> from the <code>vcpkg</code> folder and try again. If that doesn&rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running <code>boostsrap-vcpkg</code> again. You can also try upgrading your system (e.g., <code>apt update</code>, <code>apt upgrade</code>).</p> +<ol start="3"> +<li> +<p><code>Could not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)</code></p> +<p>Make sure NumPy is installed in the Python environment you specified. If CMake still can&rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: <code>-DPython3_NumPy_INCLUDE_DIR=...</code>. You can find that location like this: <code>python -c 'import numpy; print(numpy.get_include())'</code></p> +</li> +</ol> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#getting-started">Getting Started</a> + <ul> + <li><a href="#tested-configurations">Tested Configurations</a></li> + <li><a href="#mac-pre-requisities">Mac Pre-requisities</a></li> + <li><a href="#linux-pre-requisites">Linux Pre-requisites</a></li> + <li><a href="#windows-installation">Windows Installation</a></li> + <li><a href="#downloading-the-library">Downloading the Library</a></li> + <li><a href="#compilation--installation">Compilation + Installation</a></li> + <li><a href="#options">Options</a></li> + <li><a href="#python-bindings-package-ldsctrlest">Python bindings package <code>ldsctrlest</code></a></li> + <li><a href="#common-issues">Common issues</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/getting-started/windows/index.html b/docs/docs/getting-started/windows/index.html new file mode 100644 index 00000000..ad5f272e --- /dev/null +++ b/docs/docs/getting-started/windows/index.html @@ -0,0 +1,320 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Windows Installation + # + + + Windows Pre-requisites + # + +Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this: +Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time +iwr get.scoop.sh | Invoke-Expression +Install Git and CMake if you don&rsquo;t already have them: +scoop install git cmake +If that didn&rsquo;t work, follow more detailed instructions here. +The easiest way to compile C&#43;&#43; project on Windows is with Visual Studio&rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on &ldquo;Desktop development with C&#43;&#43;.&rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the &ldquo;Installation details&rdquo; sidebar under optional features."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="LDS C&E"> + <meta property="og:description" content="Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this: +Set-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don’t already have them: +scoop install git cmake If that didn’t work, follow more detailed instructions here. +The easiest way to compile C&#43;&#43; project on Windows is with Visual Studio’s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on “Desktop development with C&#43;&#43;.” If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the “Installation details” sidebar under optional features."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>Windows | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"class=active><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Windows</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#windows-installation">Windows Installation</a> + <ul> + <li><a href="#windows-pre-requisites">Windows Pre-requisites</a></li> + <li><a href="#downloading-the-library">Downloading the Library</a></li> + <li><a href="#installation">Installation</a></li> + <li><a href="#considerations">Considerations</a></li> + <li><a href="#troubleshooting">Troubleshooting</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="windows-installation"> + Windows Installation + <a class="anchor" href="#windows-installation">#</a> +</h1> +<h2 id="windows-pre-requisites"> + Windows Pre-requisites + <a class="anchor" href="#windows-pre-requisites">#</a> +</h2> +<p>Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>Set-ExecutionPolicy RemoteSigned -Scope CurrentUser <span style="color:#75715e"># Optional: Needed to run a remote script the first time</span> +</span></span><span style="display:flex;"><span>iwr get.scoop.sh | Invoke-Expression +</span></span></code></pre></div><p>Install Git and CMake if you don&rsquo;t already have them:</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>scoop install git cmake +</span></span></code></pre></div><p>If that didn&rsquo;t work, follow more detailed instructions <a href="https://github.com/ScoopInstaller/Install#readme">here</a>.</p> +<p>The easiest way to compile C++ project on Windows is with Visual Studio&rsquo;s build tools, which you can download <a href="https://visualstudio.microsoft.com/downloads/">here</a> (or <a href="https://visualstudio.microsoft.com/vs/older-downloads/">here</a> for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on &ldquo;Desktop development with C++.&rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the &ldquo;Installation details&rdquo; sidebar under optional features.</p> +<p>And the easiest way to use Visual Studio&rsquo;s build tools is with <a href="https://code.visualstudio.com/">VS Code</a>, along with the <a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools">CMake Tools extension</a>. Install them and you should be ready to go.</p> +<h2 id="downloading-the-library"> + Downloading the Library + <a class="anchor" href="#downloading-the-library">#</a> +</h2> +<p>First, clone the repository, either from VS Code or the command line:</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git clone https://github.com/cloctools/lds-ctrl-est.git +</span></span><span style="display:flex;"><span>cd lds-ctrl-est +</span></span></code></pre></div><p>You&rsquo;ll need to initialize the submodules from the command line after the repo is cloned:</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git submodule update --init +</span></span></code></pre></div><h2 id="installation"> + Installation + <a class="anchor" href="#installation">#</a> +</h2> +<p>When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you&rsquo;ll be prompted when you configure&ndash;else there&rsquo;s an icon in the bar on the bottom of the window or type <code>Ctrl+Shift+P</code>, then &ldquo;cmake select kit&rdquo;). Choose <code>Clang [latest version] with GNU CLI ... amd64</code> assuming you are running a 64-bit OS. (MSVC may work okay too if you don&rsquo;t need to build Python bindings.)</p> +<p>Follow along with the &ldquo;Getting Started&rdquo; instructions, but where you see config options specified as <code>-DLDSCTREST_BUILD_STATIC=OFF</code> or <code>-DPython3_ROOT_DIR=...</code>, you will enter those in settings: open with <code>Ctrl+,</code>, click &ldquo;workspace&rdquo;, then search for &ldquo;CMake: Configure Args&rdquo; and enter each of your desired arguments as a separate item.</p> +<p>To configure, use <code>Ctrl+Shift+P</code> and search for the &ldquo;CMake: Configure&rdquo; command. To build, click the &ldquo;Build&rdquo; button on the bottom bar. Then click the &ldquo;CTest&rdquo; button to run the example scripts.</p> +<h2 id="considerations"> + Considerations + <a class="anchor" href="#considerations">#</a> +</h2> +<p>Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—<a href="https://docs.microsoft.com/en-us/windows/wsl/install">WSL (Windows Subsystem for Linux)</a> is a good option for Windows users who don&rsquo;t want to work on a different machine.</p> +<p>Compilation has been successfully tested in VS Code using the following kit, <strong>using the &ldquo;RelWithDebInfo&rdquo; config</strong>:</p> +<pre tabindex="0"><code>Clang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) +</code></pre><h2 id="troubleshooting"> + Troubleshooting + <a class="anchor" href="#troubleshooting">#</a> +</h2> +<ol> +<li>The build appears to work, but tests fail with code <code>0xc0000135</code> OR &ldquo;I have built the library and installed it in a non-default location. In building my own project linking against <code>ldsCtrlEst</code>, <code>cmake</code> or <code>pkg-config</code> cannot find the library or its configuration information.&rdquo;</li> +</ol> +<p>Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case <code>INSTALL</code>. If that doesn&rsquo;t solve your problem, you will likely need to add the build or install folder to your <code>PATH</code> environment variable, which you can do using the settings GUI (search for &ldquo;Edit the system environment variables&rdquo;).</p> +<ol start="2"> +<li>On Windows, &ldquo;Generate CMake Cache&rdquo; step errs because creating symbolic links is not permitted.</li> +</ol> +<p>Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to <em>Control Panel -&gt; Administrative Tools -&gt; Local Policies -&gt; User Rights Assignment -&gt; Create Symbolic Links</em>.</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#windows-installation">Windows Installation</a> + <ul> + <li><a href="#windows-pre-requisites">Windows Pre-requisites</a></li> + <li><a href="#downloading-the-library">Downloading the Library</a></li> + <li><a href="#installation">Installation</a></li> + <li><a href="#considerations">Considerations</a></li> + <li><a href="#troubleshooting">Troubleshooting</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/index.html b/docs/docs/index.html new file mode 100644 index 00000000..dd2543c4 --- /dev/null +++ b/docs/docs/index.html @@ -0,0 +1,243 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + LDS Control &amp; Estimation Documentation + # +"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="LDS C&#43;E Documentation"> + <meta property="og:description" content="LDS Control &amp; Estimation Documentation #"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>LDS C&#43;E Documentation | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>LDS C&#43;E Documentation</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#lds-control--estimation-documentation">LDS Control &amp; Estimation Documentation</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="lds-control--estimation-documentation"> + LDS Control &amp; Estimation Documentation + <a class="anchor" href="#lds-control--estimation-documentation">#</a> +</h1> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#lds-control--estimation-documentation">LDS Control &amp; Estimation Documentation</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/index.xml b/docs/docs/index.xml new file mode 100644 index 00000000..c8443874 --- /dev/null +++ b/docs/docs/index.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>LDS C&#43;E Documentation on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/</link> + <description>Recent content in LDS C&#43;E Documentation on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title></title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</guid> + <description>&lt;h1 id=&#34;getting-started&#34;&gt;&#xA; Getting Started&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#getting-started&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;ldsCtrlEst&lt;/code&gt; requires &lt;a href=&#34;http://arma.sourceforge.net/&#34;&gt;Armadillo&lt;/a&gt; for linear algebra as well as &lt;a href=&#34;https://www.hdfgroup.org/downloads/hdf5/&#34;&gt;HDF5&lt;/a&gt; for saving output. &lt;a href=&#34;https://vcpkg.io/&#34;&gt;&lt;code&gt;vcpkg&lt;/code&gt;&lt;/a&gt; is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tested-configurations&#34;&gt;&#xA; Tested Configurations&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#tested-configurations&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the &lt;code&gt;RelWithDebInfo&lt;/code&gt; build type in the CMake configure command (&lt;code&gt;-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo&lt;/code&gt;):&lt;/p&gt;</description> + </item> + <item> + <title></title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</guid> + <description>&lt;h1 id=&#34;windows-installation&#34;&gt;&#xA; Windows Installation&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#windows-installation&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;windows-pre-requisites&#34;&gt;&#xA; Windows Pre-requisites&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#windows-pre-requisites&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Set-ExecutionPolicy RemoteSigned -Scope CurrentUser &lt;span style=&#34;color:#75715e&#34;&gt;# Optional: Needed to run a remote script the first time&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;iwr get.scoop.sh | Invoke-Expression&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install Git and CMake if you don&amp;rsquo;t already have them:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scoop install git cmake&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If that didn&amp;rsquo;t work, follow more detailed instructions &lt;a href=&#34;https://github.com/ScoopInstaller/Install#readme&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The easiest way to compile C++ project on Windows is with Visual Studio&amp;rsquo;s build tools, which you can download &lt;a href=&#34;https://visualstudio.microsoft.com/downloads/&#34;&gt;here&lt;/a&gt; (or &lt;a href=&#34;https://visualstudio.microsoft.com/vs/older-downloads/&#34;&gt;here&lt;/a&gt; for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on &amp;ldquo;Desktop development with C++.&amp;rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the &amp;ldquo;Installation details&amp;rdquo; sidebar under optional features.&lt;/p&gt;</description> + </item> + <item> + <title>C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</guid> + <description>&lt;h1 id=&#34;control--estimation&#34;&gt;&#xA; Control &amp;amp; Estimation&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#control--estimation&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., &lt;em&gt;filtering&lt;/em&gt;). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.&lt;/p&gt;</description> + </item> + <item> + <title>Models</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</guid> + <description>&lt;h1 id=&#34;model-definitions&#34;&gt;&#xA; Model Definitions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#model-definitions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form:&#xA;&#xA;&lt;link rel=&#34;stylesheet&#34; href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/katex/katex.min.css&#34; /&gt;&#xA;&lt;script defer src=&#34;https://stanley-rozell.github.io/lds-ctrl-est/katex/katex.min.js&#34;&gt;&lt;/script&gt;&#xA;&lt;script defer src=&#34;https://stanley-rozell.github.io/lds-ctrl-est/katex/auto-render.min.js&#34; onload=&#34;renderMathInElement(document.body);&#34;&gt;&lt;/script&gt;&lt;span&gt;&#xA; \[\mathbf{x}_{t&amp;#43;1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} &amp;#43; \mathbf{B} \mathbf{v}_{t} &amp;#43; \mathbf{m}_{t} &amp;#43; \mathbf{w}_{t}\]&#xA;&lt;/span&gt;&#xA;&lt;/p&gt;&#xA;&lt;span&gt;&#xA; \[\mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right)\]&#xA;&lt;/span&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;t : time index&#xA;x : system state&#xA;v = g%u : input (e.g., in physical units used for model fit)&#xA;u : control signal sent to actuator (e.g., in Volts)&#xA;y : system output&#xA;m : process disturbance&#xA;w ~ N(0, Q) : process noise/disturbance&#xA;&#xA;A : state matrix&#xA;B : input coupling matrix&#xA;g : input gain (e.g., for converting to control signal actuator voltage)&#xA; n.b., assumes this conversion is linear&#xA;Q : process noise covariance&#xA;&#xA;% : element-wise multiplication&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;lds-with-gaussian-observations&#34;&gt;&#xA; LDS with Gaussian Observations&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#lds-with-gaussian-observations&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.&lt;/p&gt;</description> + </item> + </channel> +</rss> diff --git a/docs/docs/terminology/control-estimation/index.html b/docs/docs/terminology/control-estimation/index.html new file mode 100644 index 00000000..41d5deee --- /dev/null +++ b/docs/docs/terminology/control-estimation/index.html @@ -0,0 +1,368 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Control &amp; Estimation + # + +The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="C&E"> + <meta property="og:description" content="Control &amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>C&amp;E | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"class=active><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>C&amp;E</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#control--estimation">Control &amp; Estimation</a> + <ul> + <li><a href="#state-estimation">State estimation</a></li> + <li><a href="#adaptive-estimation-of-process-disturbance">Adaptive estimation of process disturbance</a></li> + <li><a href="#control">Control</a></li> + <li><a href="#calculating-reference-state-control-from-output">Calculating reference state-control from output</a></li> + <li><a href="#model-predictive-control">Model Predictive Control</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="control--estimation"> + Control &amp; Estimation + <a class="anchor" href="#control--estimation">#</a> +</h1> +<p>The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., <em>filtering</em>). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.</p> +<h2 id="state-estimation"> + State estimation + <a class="anchor" href="#state-estimation">#</a> +</h2> +<p>In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by + +<link rel="stylesheet" href="/lds-ctrl-est/katex/katex.min.css" /> +<script defer src="/lds-ctrl-est/katex/katex.min.js"></script> +<script defer src="/lds-ctrl-est/katex/auto-render.min.js" onload="renderMathInElement(document.body);"></script><span> + \[\widehat{\mathbf{x}}_{t|t-1} = \mathbf{A}\widehat{\mathbf{x}}_{t-1|t-1} &#43; \mathbf{B} u_{t-1} &#43; \mathbf{m}_{t-1} \;,\] +</span> +</p> +<span> + \[\widehat{\mathbf{x}}_{t|t} = \widehat{\mathbf{x}}_{t|t-1} &#43; \mathbf{K}^{\rm e}_t \left(\mathbf{z}_t - \widehat{\mathbf{y}}_{t|t-1}\right)\;,\] +</span> + +<p>where <span> + \( \hat{\left(\cdot\right)}_{t|j} \) +</span> + indicates an estimate at time <span> + \( t \) +</span> + given data up to time <span> + \( j \) +</span> + inclusive, <span> + \( \mathbf{K}^{\rm e} \) +</span> + is the estimator gain, and</p> +<span> + \[ \widehat{\mathbf{y}}_{t|t-1} = h\left( \widehat{\mathbf{x}}_{t|t-1} \right) \; .\] +</span> + +<p>In the case of GLDS models, the estimator gain (called <code>Ke</code> in library) is calculated recursively by <a href="https://en.wikipedia.org/wiki/Kalman_filter">Kalman filtering</a>, which requires knowledge of the process noise and measurement noise covariances (<code>Q</code>, <code>R</code>) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the <code>lds::gaussian::System::set_Ke</code> mutator.</p> +<p>In the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (<a href="http://www.stat.columbia.edu/~liam/teaching/neurostat-spr11/papers/brown-et-al/eden2004.pdf">Eden et al. 2004</a>). This nonlinear filter recursively updates <code>Ke</code> at each time step and requires an estimate of the process noise covariance (<code>Q</code>) as well.</p> +<h2 id="adaptive-estimation-of-process-disturbance"> + Adaptive estimation of process disturbance + <a class="anchor" href="#adaptive-estimation-of-process-disturbance">#</a> +</h2> +<p>Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (<code>m</code>) is adaptively re-estimated when the <code>lds::System::do_adapt_m</code> property is set to <code>true</code>. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.</p> +<p>When parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk +<span> + \[\mathbf{m}_{t} = \mathbf{m}_{t-1} &#43; \mathbf{w}^m_{t-1} \;,\] +</span> + +where <span> + \( \mathbf{w}^m \sim \mathcal{N}\left(0, \mathbf{Q}_m\right)\) +</span> +. Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.</p> +<h2 id="control"> + Control + <a class="anchor" href="#control">#</a> +</h2> +<p>Given the estimated state, the controller updates the inputs to the system according to the following law: +<span> + \[\mathbf{u}_{t} = \mathbf{u}^{\rm ref}_t - \mathbf{K}^c_x \left( \widehat{\mathbf{x}}_t - \mathbf{x}^{\rm ref}_t\right)\;,\] +</span> +</p> +<p>where <span> + \( \left( \cdot \right)^{\rm ref} \) +</span> + correspond to reference/target signals and <span> + \( \mathbf{K}^c_x \) +</span> + is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.</p> +<p>If users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as</p> +<span> + \[\mathbf{u}_{t} = \mathbf{u}^{\rm ref}_t - \mathbf{K}^c_x \left( \widehat{\mathbf{x}}_t - \mathbf{x}^{\rm ref}_t\right) - \mathbf{K}^c_{\rm inty} \sum_{j=1}^{t}\left( \widehat{\mathbf{y}}_j - \mathbf{y}^{\rm ref}_j \right) \;.\] +</span> + +<p>An additional option available to users is a control law that updates the <em>change</em> in <code>u</code>,</p> +<span> + \[\Delta\mathbf{u}_{t} = -\mathbf{K}^c_u \left(\mathbf{u}_{t-1} - \mathbf{u}^{\rm ref}_{t-1} \right) - \mathbf{K}^c_x \left( \widehat{\mathbf{x}}_t - \mathbf{x}^{\rm ref}_t\right)\;,\] +</span> + +<span> + \[\mathbf{u}_{t} = \mathbf{u}_{t-1} &#43; \Delta\mathbf{u}_{t} \; .\] +</span> + +<p>Notice that this takes the form of a first-order difference equation for updating control (<em>i.e.</em>, <span> + \( \Delta\mathbf{u}_{t} = -\mathbf{K}^c_u \mathbf{u}_{t-1} &#43; \epsilon_{t-1} \) +</span> +), effectively low-pass filtering the input depending on the characteristics of <span> + \( \mathbf{K}^c_u \) +</span> +. This can be useful in cases where users have designed the controller gains by LQR to minimize <em>not</em> the amplitude of the input, but the <em>change</em> in input, by augmenting the state vector with the input during LQR design.</p> +<p>Integral action and the <span> + \( \Delta \mathbf{u} \) +</span> + control law can be combined. The library keeps track of the controller type by way of <a href="/lds-ctrl-est/docs/api/modules/group__control__masks/">bit masks</a> which can be bit-wise OR&rsquo;d to use in combination.</p> +<h2 id="calculating-reference-state-control-from-output"> + Calculating reference state-control from output + <a class="anchor" href="#calculating-reference-state-control-from-output">#</a> +</h2> +<p>In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce <span> + \( \mathbf{x}^{\rm ref} \) +</span> + and <span> + \( \mathbf{u}^{\rm ref} \) +</span> +. Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (<code>lds::Controller&lt;System&gt;::ControlOutputReference</code>). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.</p> +<h2 id="model-predictive-control"> + Model Predictive Control + <a class="anchor" href="#model-predictive-control">#</a> +</h2> +<p>Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.</p> +<p>In the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#control--estimation">Control &amp; Estimation</a> + <ul> + <li><a href="#state-estimation">State estimation</a></li> + <li><a href="#adaptive-estimation-of-process-disturbance">Adaptive estimation of process disturbance</a></li> + <li><a href="#control">Control</a></li> + <li><a href="#calculating-reference-state-control-from-output">Calculating reference state-control from output</a></li> + <li><a href="#model-predictive-control">Model Predictive Control</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/terminology/model/index.html b/docs/docs/terminology/model/index.html new file mode 100644 index 00000000..22aada64 --- /dev/null +++ b/docs/docs/terminology/model/index.html @@ -0,0 +1,361 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Model Definitions + # + +This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: + + + + + \[\mathbf{x}_{t&#43;1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} &#43; \mathbf{B} \mathbf{v}_{t} &#43; \mathbf{m}_{t} &#43; \mathbf{w}_{t}\] + + + + \[\mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right)\] + + +t : time index +x : system state +v = g%u : input (e.g., in physical units used for model fit) +u : control signal sent to actuator (e.g., in Volts) +y : system output +m : process disturbance +w ~ N(0, Q) : process noise/disturbance + +A : state matrix +B : input coupling matrix +g : input gain (e.g., for converting to control signal actuator voltage) + n.b., assumes this conversion is linear +Q : process noise covariance + +% : element-wise multiplication + + + LDS with Gaussian Observations + # + +For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Models"> + <meta property="og:description" content="Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \[\mathbf{x}_{t&#43;1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} &#43; \mathbf{B} \mathbf{v}_{t} &#43; \mathbf{m}_{t} &#43; \mathbf{w}_{t}\] \[\mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right)\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>Models | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"class=active><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Models</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#model-definitions">Model Definitions</a> + <ul> + <li><a href="#lds-with-gaussian-observations">LDS with Gaussian Observations</a></li> + <li><a href="#lds-with-poisson-observations">LDS with Poisson Observations</a></li> + <li><a href="#model-predictive-control-mpc">Model Predictive Control (MPC)</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="model-definitions"> + Model Definitions + <a class="anchor" href="#model-definitions">#</a> +</h1> +<p>This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: + +<link rel="stylesheet" href="/lds-ctrl-est/katex/katex.min.css" /> +<script defer src="/lds-ctrl-est/katex/katex.min.js"></script> +<script defer src="/lds-ctrl-est/katex/auto-render.min.js" onload="renderMathInElement(document.body);"></script><span> + \[\mathbf{x}_{t&#43;1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} &#43; \mathbf{B} \mathbf{v}_{t} &#43; \mathbf{m}_{t} &#43; \mathbf{w}_{t}\] +</span> +</p> +<span> + \[\mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right)\] +</span> + +<pre><code>t : time index +x : system state +v = g%u : input (e.g., in physical units used for model fit) +u : control signal sent to actuator (e.g., in Volts) +y : system output +m : process disturbance +w ~ N(0, Q) : process noise/disturbance + +A : state matrix +B : input coupling matrix +g : input gain (e.g., for converting to control signal actuator voltage) + n.b., assumes this conversion is linear +Q : process noise covariance + +% : element-wise multiplication +</code></pre> +<h2 id="lds-with-gaussian-observations"> + LDS with Gaussian Observations + <a class="anchor" href="#lds-with-gaussian-observations">#</a> +</h2> +<p>For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.</p> +<span> + \[\mathbf{y}_{t} = \mathbf{C} \mathbf{x}_{t} &#43; \mathbf{d}\] +</span> + +<span> + \[\mathbf{z}_{t} \sim \mathcal{N}\left(\mathbf{y}_{t} , \mathbf{R} \right)\] +</span> + +<pre><code>z : measurement + +C : output matrix +d : output bias +R : measurement noise covariance +</code></pre> +<h2 id="lds-with-poisson-observations"> + LDS with Poisson Observations + <a class="anchor" href="#lds-with-poisson-observations">#</a> +</h2> +<p>For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.</p> +<span> + \[y_{t}^{i} = \exp \left(\mathbf{c}^i \mathbf{x}_{t} &#43; d^i\right)\] +</span> + +<span> + \[z_{t}^i \sim \rm{Poisson} \left(y_{t}^i \right)\] +</span> + +<pre><code>i : output index + +z : measurement (count data) + +c : i^th row of output matrix (C) +d : output bias +</code></pre> +<h2 id="model-predictive-control-mpc"> + Model Predictive Control (MPC) + <a class="anchor" href="#model-predictive-control-mpc">#</a> +</h2> +<p>Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.</p> +<p>In the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#model-definitions">Model Definitions</a> + <ul> + <li><a href="#lds-with-gaussian-observations">LDS with Gaussian Observations</a></li> + <li><a href="#lds-with-poisson-observations">LDS with Poisson Observations</a></li> + <li><a href="#model-predictive-control-mpc">Model Predictive Control (MPC)</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/tutorials/eg_glds_control/index.html b/docs/docs/tutorials/eg_glds_control/index.html new file mode 100644 index 00000000..89c73f49 --- /dev/null +++ b/docs/docs/tutorials/eg_glds_control/index.html @@ -0,0 +1,439 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + GLDS Control Tutorial + # + +This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="GLDS Control"> + <meta property="og:description" content="GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>GLDS Control | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/"class=active>GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>GLDS Control</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#glds-control-tutorial">GLDS Control Tutorial</a> + <ul> + <li><a href="#preamble">Preamble</a></li> + <li><a href="#creating-a-simulated-system">Creating a simulated system</a></li> + <li><a href="#creating-the-controller">Creating the controller</a></li> + <li><a href="#simulating-control">Simulating control</a></li> + <li><a href="#example-simulation-result">Example simulation result</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="glds-control-tutorial"> + GLDS Control Tutorial + <a class="anchor" href="#glds-control-tutorial">#</a> +</h1> +<p>This tutorial shows how to use this library to control a system with a Gaussian LDS controller (<code>lds::gaussian::Controller</code>). In place of a physical system, a GLDS model (<code>lds::gaussian::System</code>) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.</p> +<p>The full code for this can be found <a href="/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/">here</a>.</p> +<h2 id="preamble"> + Preamble + <a class="anchor" href="#preamble">#</a> +</h2> +<p>In addition to including the main <code>ldsCtrlEst</code> header, this tutorial will use some shorthand.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span></code></pre></div><p>Note that <code>lds::Matrix</code> and <code>lds::Vector</code> are typedefs for <code>arma::Mat&lt;data_t&gt;</code> and <code>arma::Col&lt;data_t&gt;</code>, where the data type is <code>double</code> by default. May be changed to <code>float</code> in <code>include/ldsCtrlEst_h/lds.h</code> if there are memory constraints (e.g., large-scale MIMO control problems).</p> +<h2 id="creating-a-simulated-system"> + Creating a simulated system + <a class="anchor" href="#creating-a-simulated-system">#</a> +</h2> +<p>A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Make 1st-order SISO system, sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// no time steps for simulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">5.0</span> <span style="color:#f92672">/</span> dt); +</span></span></code></pre></div><p>When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// construct ground truth system to be controlled... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// initializes to random walk model with top-most n_y state observed +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System controlled_system(n_u, n_x, n_y, dt); +</span></span></code></pre></div><p>This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top <code>min(n_x, n_y)</code> states are observed at the output. i.e., for this example, + +<link rel="stylesheet" href="/lds-ctrl-est/katex/katex.min.css" /> +<script defer src="/lds-ctrl-est/katex/katex.min.js"></script> +<script defer src="/lds-ctrl-est/katex/auto-render.min.js" onload="renderMathInElement(document.body);"></script><span> + \[ +x_{t&#43;1} = x_t &#43; w_t \] +</span> +</p> +<span> + \[ +y_{t} = x_t \] +</span> + +<p>where <span> + \( w_{t} \sim \mathcal{N}\left( 0, Q \right) \) +</span> +.</p> +<p>Now, create non-default parameters for this model.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Ground-truth parameters for the controlled system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (stand-in for physical system to be controlled) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">a_true</span>(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a_true[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> exp(<span style="color:#f92672">-</span>dt <span style="color:#f92672">/</span> <span style="color:#ae81ff">0.01</span>); +</span></span><span style="display:flex;"><span> Matrix b_true <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(<span style="color:#ae81ff">2e-4</span>); +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// control signal to model input unit conversion e.g., V -&gt; mW/mm2: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector g_true <span style="color:#f92672">=</span> Vector(n_y).fill(<span style="color:#ae81ff">10.0</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// output noise covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix r_true <span style="color:#f92672">=</span> Matrix(n_y, n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-4</span>; +</span></span></code></pre></div><p>As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">/// Going to simulate a switching disturbance (m) acting on system +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t which_m <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#75715e">// whether low or high disturbance (0, 1) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t m_low <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> dt <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_lo2hi <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; <span style="color:#75715e">// probability of going from low to high disturb. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t m_high <span style="color:#f92672">=</span> <span style="color:#ae81ff">20</span> <span style="color:#f92672">*</span> dt <span style="color:#f92672">*</span> (<span style="color:#ae81ff">1</span> <span style="color:#f92672">-</span> a_true[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> data_t pr_hi2lo <span style="color:#f92672">=</span> pr_lo2hi; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// initially let m be low +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m0_true <span style="color:#f92672">=</span> Vector(n_y).fill(m_low); +</span></span></code></pre></div><p>Finally, assign the parameters using corresponding set-methods.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Assign params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controlled_system.set_A(a_true); +</span></span><span style="display:flex;"><span> controlled_system.set_B(b_true); +</span></span><span style="display:flex;"><span> controlled_system.set_m(m0_true); +</span></span><span style="display:flex;"><span> controlled_system.set_g(g_true); +</span></span><span style="display:flex;"><span> controlled_system.set_R(r_true); +</span></span></code></pre></div><h2 id="creating-the-controller"> + Creating the controller + <a class="anchor" href="#creating-the-controller">#</a> +</h2> +<p>Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this <code>lds::gaussian::System</code> object and upper/lower bounds on the control signal (<code>u_lb</code>, <code>u_ub</code> below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does <strong>not</strong> saturate somehow, simply set the lower and upper bounds to <code>-lds::kInf</code> and <code>lds::kInf</code>, respectively. Simple saturation is currently the only actuator model in this library.</p> +<p>For the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// make a controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>Controller controller; +</span></span><span style="display:flex;"><span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Create **incorrect** model used for control. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (e.g., imperfect model fitting) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix b_controller <span style="color:#f92672">=</span> b_true <span style="color:#f92672">/</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// let&#39;s assume zero process disturbance initially +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// (will be re-estimating) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m_controller <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// for this demo, just use arbitrary default R +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix r_controller <span style="color:#f92672">=</span> Matrix(n_y, n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> lds<span style="color:#f92672">::</span>kDefaultR0; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>System controller_system(controlled_system); +</span></span><span style="display:flex;"><span> controller_system.set_B(b_controller); +</span></span><span style="display:flex;"><span> controller_system.set_m(m_controller); +</span></span><span style="display:flex;"><span> controller_system.set_R(r_controller); +</span></span><span style="display:flex;"><span> controller_system.Reset(); <span style="color:#75715e">// reset to new m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// going to adaptively re-estimate the disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller_system.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set adaptation rate by changing covariance of assumed process noise +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// acting on random-walk evolution of m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_m <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-6</span>; +</span></span><span style="display:flex;"><span> controller_system.set_Q_m(q_m); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// lower and upper bounds on control signal (e.g., in Volts) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t u_lb <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; <span style="color:#75715e">// [=] V +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t u_ub <span style="color:#f92672">=</span> <span style="color:#ae81ff">5.0</span>; <span style="color:#75715e">// [=] V +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move( +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>gaussian<span style="color:#f92672">::</span>Controller(std<span style="color:#f92672">::</span>move(controller_system), u_lb, u_ub)); +</span></span><span style="display:flex;"><span> } +</span></span></code></pre></div><p>Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.</p> +<p>With the controller constructed, control variables may be set.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Control variables: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// if following enabled, adapts set point with re-estimated process +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// disturbance n.b., should not need integral action if this is enabled as the +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// adaptive estimator minimizes DC error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> do_adaptive_set_point <span style="color:#f92672">=</span> false; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Reference/target output, controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector y_ref0 <span style="color:#f92672">=</span> Vector(n_y).fill(<span style="color:#ae81ff">20.0</span> <span style="color:#f92672">*</span> dt); +</span></span><span style="display:flex;"><span> Matrix k_x <span style="color:#f92672">=</span> Matrix(n_u, n_x).fill(<span style="color:#ae81ff">100</span>); <span style="color:#75715e">// gains on state error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix k_inty <span style="color:#f92672">=</span> Matrix(n_u, n_y).fill(<span style="color:#ae81ff">1e3</span>); <span style="color:#75715e">// gains on integrated err +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// setting initial state to target to avoid error at onset: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0 <span style="color:#f92672">=</span> Vector(n_x).fill(y_ref0[<span style="color:#ae81ff">0</span>]); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set up controller type bit mask so controller knows how to proceed +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_adaptive_set_point) { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// adapt set point with estimated disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> control_type <span style="color:#f92672">=</span> control_type <span style="color:#f92672">|</span> lds<span style="color:#f92672">::</span>kControlTypeAdaptM; +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// use integral action to minimize DC error +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> control_type <span style="color:#f92672">=</span> control_type <span style="color:#f92672">|</span> lds<span style="color:#f92672">::</span>kControlTypeIntY; +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set controller type +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_control_type(control_type); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Let&#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector g_design <span style="color:#f92672">=</span> Vector(n_u).fill(<span style="color:#ae81ff">9</span>); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Set params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// **n.b. using arbitrary defaults for Q, R in this example. Really, these +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// should be set by users, as they tune characteristics of Kalman filter. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Users can also choose not to recursively calculate the estimator gain and +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// supply it (setKe) instead of covariances.** +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> controller.set_y_ref(y_ref0); +</span></span><span style="display:flex;"><span> controller.set_Kc(k_x); +</span></span><span style="display:flex;"><span> controller.set_Kc_inty(k_inty); +</span></span><span style="display:flex;"><span> controller.set_g_design(g_design); +</span></span></code></pre></div><h2 id="simulating-control"> + Simulating control + <a class="anchor" href="#simulating-control">#</a> +</h2> +<p>In this demonstration, we will use the <code>ControlOutputReference</code> method which allows users to simply set the reference output and supply the current measurement <code>z</code>. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.</p> +<p>The control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Simulate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t) <span style="color:#f92672">=</span> controlled_system.Simulate(u_tm1); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// This method uses a steady-state solution to control problem to calculate +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// x_ref, u_ref from reference output y_ref. Therefore, it is only +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// applicable to regulation problems or cases where reference trajectory +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// changes slowly compared to system dynamics. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u.col(t) <span style="color:#f92672">=</span> controller.ControlOutputReference(z.col(t)); +</span></span></code></pre></div><h2 id="example-simulation-result"> + Example simulation result + <a class="anchor" href="#example-simulation-result">#</a> +</h2> +<p>Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller&rsquo;s online estimates of the output, state, and disturbance are given in purple.</p> +<p><img src="/lds-ctrl-est/eg_glds_ctrl_output.png" alt="example control output" /></p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#glds-control-tutorial">GLDS Control Tutorial</a> + <ul> + <li><a href="#preamble">Preamble</a></li> + <li><a href="#creating-a-simulated-system">Creating a simulated system</a></li> + <li><a href="#creating-the-controller">Creating the controller</a></li> + <li><a href="#simulating-control">Simulating control</a></li> + <li><a href="#example-simulation-result">Example simulation result</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/tutorials/eg_plds_state_estimation/index.html b/docs/docs/tutorials/eg_plds_state_estimation/index.html new file mode 100644 index 00000000..154cfcdc --- /dev/null +++ b/docs/docs/tutorials/eg_plds_state_estimation/index.html @@ -0,0 +1,368 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + PLDS State Estimation Tutorial + # + +This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated. +The full code for this can be found here."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="PLDS State Estimation"> + <meta property="og:description" content="PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated. +The full code for this can be found here."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>PLDS State Estimation | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/"class=active>PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>PLDS State Estimation</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#plds-state-estimation-tutorial">PLDS State Estimation Tutorial</a> + <ul> + <li><a href="#preamble">Preamble</a></li> + <li><a href="#creating-a-simulated-system">Creating a simulated system</a></li> + <li><a href="#creating-the-estimator">Creating the estimator</a></li> + <li><a href="#simulating-estimation">Simulating estimation</a></li> + <li><a href="#example-simulation-result">Example simulation result</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="plds-state-estimation-tutorial"> + PLDS State Estimation Tutorial + <a class="anchor" href="#plds-state-estimation-tutorial">#</a> +</h1> +<p>This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (<code>lds::poisson::System</code>) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.</p> +<p>The full code for this can be found <a href="/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/">here</a>.</p> +<h2 id="preamble"> + Preamble + <a class="anchor" href="#preamble">#</a> +</h2> +<p>In addition to including the main <code>ldsCtrlEst</code> header, this tutorial will use some shorthand.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span></code></pre></div><p>Note that <code>lds::Matrix</code> and <code>lds::Vector</code> are typedefs for <code>arma::Mat&lt;data_t&gt;</code> and <code>arma::Col&lt;data_t&gt;</code>, where the data type is <code>double</code> by default. May be changed to <code>float</code> in <code>include/ldsCtrlEst_h/lds.h</code> if there are memory constraints (e.g., large-scale MIMO control problems).</p> +<h2 id="creating-a-simulated-system"> + Creating a simulated system + <a class="anchor" href="#creating-a-simulated-system">#</a> +</h2> +<p>A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Make SISO system sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// no. inputs +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// no. states +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; <span style="color:#75715e">// no. outputs +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">30</span> <span style="color:#f92672">/</span> dt); <span style="color:#75715e">// no time steps for simulation. +</span></span></span></code></pre></div><p>When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// construct ground truth system... +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System system_true(n_u, n_x, n_y, dt); +</span></span></code></pre></div><p>This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top <code>min(n_x, n_y)</code> states are observed at the output. i.e., for this example, + +<link rel="stylesheet" href="/lds-ctrl-est/katex/katex.min.css" /> +<script defer src="/lds-ctrl-est/katex/katex.min.js"></script> +<script defer src="/lds-ctrl-est/katex/auto-render.min.js" onload="renderMathInElement(document.body);"></script><span> + \[x_{t&#43;1} = x_t &#43; w_t\] +</span> +</p> +<span> + \[y_{t} = \exp\left(x_t\right)\] +</span> + +<p>where <span> + \( w_{t} \sim \mathcal{N}\left( 0, Q \right) \) +</span> +.</p> +<p>Now, create non-default parameters for this model.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Model parameters +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">a_true</span>(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a_true[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> exp(<span style="color:#f92672">-</span>dt <span style="color:#f92672">/</span> <span style="color:#ae81ff">0.075</span>); +</span></span><span style="display:flex;"><span> Matrix b_true <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(<span style="color:#ae81ff">1e-2</span>); +</span></span><span style="display:flex;"><span> Vector m0_true <span style="color:#f92672">=</span> Vector(n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros).fill(<span style="color:#f92672">-</span><span style="color:#ae81ff">7e-2</span>); <span style="color:#75715e">// disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_true <span style="color:#f92672">=</span> m0_true <span style="color:#f92672">*</span> arma<span style="color:#f92672">::</span>inv(Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">-</span> +</span></span><span style="display:flex;"><span> a_true); <span style="color:#75715e">// initial state +</span></span></span></code></pre></div><p>Finally, assign the parameters using corresponding set-methods.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Assign params. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_true.set_A(a_true); +</span></span><span style="display:flex;"><span> system_true.set_B(b_true); +</span></span><span style="display:flex;"><span> system_true.set_x0(x0_true); +</span></span><span style="display:flex;"><span> system_true.set_m(m0_true); +</span></span><span style="display:flex;"><span> system_true.Reset(); +</span></span></code></pre></div><h2 id="creating-the-estimator"> + Creating the estimator + <a class="anchor" href="#creating-the-estimator">#</a> +</h2> +<p>Now, create the estimator. The system type includes filtering functionality for state estimation, so create another <code>lds::poisson::System</code>. As noted above, the only parameter mismatch in this simulation will be the process disturbance.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Construct system for estimation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// e.g., will create a model with incorrect disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System system_estimator(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Can copy parameters from another system object +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator <span style="color:#f92672">=</span> system_true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// wrong disturbance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector m0_est <span style="color:#f92672">=</span> m0_true <span style="color:#f92672">*</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> system_estimator.set_m(m0_est); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set new initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_est <span style="color:#f92672">=</span> m0_est <span style="color:#f92672">*</span> arma<span style="color:#f92672">::</span>inv(Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">-</span> +</span></span><span style="display:flex;"><span> a_true); <span style="color:#75715e">// initial state +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator.set_x0(x0_est); +</span></span><span style="display:flex;"><span> system_estimator.Reset(); <span style="color:#75715e">// reset to initial condition. +</span></span></span></code></pre></div><p>To ensure robust estimates, adaptively re-estimate the process disturbance.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// turn on adaptive disturbance estimation +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set adaptation rate by changing covariance of assumed process noise acting +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// on random-walk evolution of m +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_m <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">1e-6</span>; +</span></span><span style="display:flex;"><span> system_estimator.set_Q_m(q_m); +</span></span></code></pre></div><h2 id="simulating-estimation"> + Simulating estimation + <a class="anchor" href="#simulating-estimation">#</a> +</h2> +<p>In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Simlate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t) <span style="color:#f92672">=</span> system_true.Simulate(u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Filter (predict -&gt; update) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> system_estimator.Filter(u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>), z.col(t)); +</span></span></code></pre></div><h2 id="example-simulation-result"> + Example simulation result + <a class="anchor" href="#example-simulation-result">#</a> +</h2> +<p>Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.</p> +<p><img src="/lds-ctrl-est/eg_plds_est_output.png" alt="example estimator output" /></p> +<p>With this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.</p> +<p><img src="/lds-ctrl-est/eg_plds_est_output_hist.png" alt="example estimator output histogram" /></p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#plds-state-estimation-tutorial">PLDS State Estimation Tutorial</a> + <ul> + <li><a href="#preamble">Preamble</a></li> + <li><a href="#creating-a-simulated-system">Creating a simulated system</a></li> + <li><a href="#creating-the-estimator">Creating the estimator</a></li> + <li><a href="#simulating-estimation">Simulating estimation</a></li> + <li><a href="#example-simulation-result">Example simulation result</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/tutorials/eg_switched_plds_control/index.html b/docs/docs/tutorials/eg_switched_plds_control/index.html new file mode 100644 index 00000000..ec4911b7 --- /dev/null +++ b/docs/docs/tutorials/eg_switched_plds_control/index.html @@ -0,0 +1,412 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + PLDS Switched Control Tutorial + # + +This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear. +In the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="PLDS Switched Control"> + <meta property="og:description" content="PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear. +In the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> + <meta property="article:section" content="docs"> +<title>PLDS Switched Control | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/"class=active>Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>PLDS Switched Control</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#plds-switched-control-tutorial">PLDS Switched Control Tutorial</a> + <ul> + <li><a href="#preamble">Preamble</a></li> + <li><a href="#creating-the-simulated-system">Creating the simulated system</a></li> + <li><a href="#creating-the-controller">Creating the controller</a></li> + <li><a href="#simulating-control">Simulating control</a></li> + <li><a href="#example-simulation-result">Example simulation result</a></li> + </ul> + </li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="plds-switched-control-tutorial"> + PLDS Switched Control Tutorial + <a class="anchor" href="#plds-switched-control-tutorial">#</a> +</h1> +<p>This tutorial shows how to use this library to control a system with a switched PLDS controller (<code>lds::poisson::SwitchedController</code>). This type of controller is applicable in scenarios where a physical system is not accurately captured by a <strong>single</strong> LDS but has <strong>multiple</strong> discrete operating modes where the dynamics can be well-approximated as linear.</p> +<p>In the example that follows, another PLDS model (<code>lds::poisson::System</code>) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.</p> +<p>The full code for this can be found <a href="/lds-ctrl-est/docs/api/examples/eg_plds_switched_log_lin_ctrl_8cpp-example/">here</a>.</p> +<h2 id="preamble"> + Preamble + <a class="anchor" href="#preamble">#</a> +</h2> +<p>In addition to including the main <code>ldsCtrlEst</code> header, this tutorial will use some shorthand.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;ldsCtrlEst&gt;</span><span style="color:#75715e"> +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Matrix; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>Vector; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> lds<span style="color:#f92672">::</span>data_t; +</span></span><span style="display:flex;"><span><span style="color:#66d9ef">using</span> std<span style="color:#f92672">::</span>cout; +</span></span></code></pre></div><p>Note that <code>lds::Matrix</code> and <code>lds::Vector</code> are typedefs for <code>arma::Mat&lt;data_t&gt;</code> and <code>arma::Col&lt;data_t&gt;</code>, where the data type is <code>double</code> by default. May be changed to <code>float</code> in <code>include/ldsCtrlEst_h/lds.h</code> if there are memory constraints (e.g., large-scale MIMO control problems).</p> +<h2 id="creating-the-simulated-system"> + Creating the simulated system + <a class="anchor" href="#creating-the-simulated-system">#</a> +</h2> +<p>A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// whether to do switched control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">bool</span> do_switch_ctrl <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Make SISO system sampled at 1kHz +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t dt <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; +</span></span><span style="display:flex;"><span> size_t n_u <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_x <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> size_t n_y <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// no time steps for simulation. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">auto</span> n_t <span style="color:#f92672">=</span> <span style="color:#66d9ef">static_cast</span><span style="color:#f92672">&lt;</span>size_t<span style="color:#f92672">&gt;</span>(<span style="color:#ae81ff">30.0</span> <span style="color:#f92672">/</span> dt); +</span></span></code></pre></div><p>The system&rsquo;s input matrix (<code>B</code>) will be switched stochastically from one value (<code>b1</code>) to a less sensitive value (<code>b2</code>) according to the following probabilities.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// for simulating switching +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t which_mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> data_t pr_21 <span style="color:#f92672">=</span> <span style="color:#ae81ff">1e-3</span>; <span style="color:#75715e">// prob mode 1 -&gt; 2 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t pr_12 <span style="color:#f92672">=</span> pr_21; <span style="color:#75715e">// prob mode 2 -&gt; 1 +</span></span></span></code></pre></div><p>Initially, the system will be in &ldquo;mode&rdquo; 1, where <code>B = b1</code>.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// simulated system being controlled +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System controlled_system(n_u, n_x, n_y, dt); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// **Assume the system is not well characterized by one LDS, but is well +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// characterized by two LDS models with different input matrices.** +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> data_t scale_sys_b <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> Matrix <span style="color:#a6e22e">a</span>(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye); +</span></span><span style="display:flex;"><span> a[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.985</span>; +</span></span><span style="display:flex;"><span> Matrix b1 <span style="color:#f92672">=</span> Matrix(n_x, n_u).fill(<span style="color:#ae81ff">0.05</span>); +</span></span><span style="display:flex;"><span> Vector d <span style="color:#f92672">=</span> Vector(n_y, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>zeros).fill(log(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> dt)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> controlled_system.set_A(a); +</span></span><span style="display:flex;"><span> controlled_system.set_B(b1); +</span></span><span style="display:flex;"><span> controlled_system.set_d(d); +</span></span><span style="display:flex;"><span> controlled_system.Reset(); <span style="color:#75715e">// reset to initial conditions +</span></span></span></code></pre></div><p>See the <a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a> and <a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS State Estimation</a> tutorials for more detail about creating <code>System</code> objects.</p> +<h2 id="creating-the-controller"> + Creating the controller + <a class="anchor" href="#creating-the-controller">#</a> +</h2> +<p>Now, create the controller. A switched-system controller (<code>SwitchedController</code>) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (<code>sys1</code>, <code>sys2</code>), which are the same save for their input gains.</p> +<p>Similar to a non-switched controller, constructing a <code>SwitchedController</code> requires these system models and upper/lower bounds on control. See the <a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a> tutorial for more details. In the case of a <code>SwitchedController</code>, it needs a <strong>list</strong> of systems, using the <code>std::vector</code> container.</p> +<p>Moreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides <code>UniformMatrixList</code> and <code>UniformVectorList</code> containers that should be used when setting <code>Kc</code>, <code>Kc_inty</code>, <code>g_design</code>. These containers are <code>std::vector</code>s whose contents are uniformly sized.</p> +<p>Putting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// create switched controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>SwitchedController switched_controller; +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;&gt;</span> k_x; <span style="color:#75715e">// feedback controller gains +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// create switched controller sub-systems +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// system 1 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System sys1(controlled_system); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set process noise covariance +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix q_controller <span style="color:#f92672">=</span> Matrix(n_x, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>eye) <span style="color:#f92672">*</span> <span style="color:#ae81ff">5e-3</span>; +</span></span><span style="display:flex;"><span> sys1.set_Q(q_controller); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// adaptively estimate process disturbance (m) +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// n.b. using arbitrary default value for process noise if enabled. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sys1.do_adapt_m <span style="color:#f92672">=</span> true; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// setting initial mode to target to avoid large error at onset: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector x0_controller <span style="color:#f92672">=</span> arma<span style="color:#f92672">::</span>log(y_ref0) <span style="color:#f92672">-</span> d; +</span></span><span style="display:flex;"><span> sys1.set_x0(x0_controller); +</span></span><span style="display:flex;"><span> sys1.Reset(); <span style="color:#75715e">// reset to initial conditions +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// system 2 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System sys2 <span style="color:#f92672">=</span> sys1; +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// set parameters +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> sys2.set_B(b2); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>UniformSystemList<span style="color:#f92672">&lt;</span>lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>System<span style="color:#f92672">&gt;</span> systems({sys1, sys2}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// controller gains for underlying systems: +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Matrix <span style="color:#a6e22e">k_x1</span>(n_u, n_x, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>ones); +</span></span><span style="display:flex;"><span> Matrix k_x2 <span style="color:#f92672">=</span> scale_sys_b <span style="color:#f92672">*</span> k_x1; <span style="color:#75715e">// system2 is x-times less sensitive. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> k_x <span style="color:#f92672">=</span> lds<span style="color:#f92672">::</span>UniformMatrixList<span style="color:#f92672">&lt;&gt;</span>({k_x1, k_x2}); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> data_t u_lb <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>; +</span></span><span style="display:flex;"><span> data_t u_ub <span style="color:#f92672">=</span> <span style="color:#ae81ff">5.0</span>; +</span></span><span style="display:flex;"><span> switched_controller <span style="color:#f92672">=</span> std<span style="color:#f92672">::</span>move( +</span></span><span style="display:flex;"><span> lds<span style="color:#f92672">::</span>poisson<span style="color:#f92672">::</span>SwitchedController(std<span style="color:#f92672">::</span>move(systems), u_lb, u_ub)); +</span></span><span style="display:flex;"><span> } +</span></span></code></pre></div><p>Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.</p> +<p>Now that the <code>SwitchedController</code> is instantiated, assign its parameters.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Control variables +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> size_t control_type <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>; <span style="color:#75715e">// no integral action, etc +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> switched_controller.set_control_type(control_type); +</span></span><span style="display:flex;"><span> switched_controller.set_Kc(std<span style="color:#f92672">::</span>move(k_x)); +</span></span><span style="display:flex;"><span> switched_controller.set_y_ref(y_ref0); +</span></span></code></pre></div><h2 id="simulating-control"> + Simulating control + <a class="anchor" href="#simulating-control">#</a> +</h2> +<p>In this demonstration, we will use the <code>ControlOutputReference</code> method which allows users to simply set the reference output event rate (<code>y_ref</code>) and supply the current measurement <code>z</code>. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).</p> +<p>The control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.</p> +<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cpp" data-lang="cpp"><span style="display:flex;"><span> <span style="color:#75715e">// Let the controlled system stochastically change gain +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// Assume another algorithm decodes this mode change and signals the +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#75715e">// switched_controller +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> Vector <span style="color:#a6e22e">chance</span>(<span style="color:#ae81ff">1</span>, arma<span style="color:#f92672">::</span>fill<span style="color:#f92672">::</span>randu); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (which_mode <span style="color:#f92672">==</span> <span style="color:#ae81ff">1</span>) <span style="color:#75715e">// mode1 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> { +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_21) { +</span></span><span style="display:flex;"><span> which_mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">2</span>; +</span></span><span style="display:flex;"><span> controlled_system.set_B(b2); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_switch_ctrl) { +</span></span><span style="display:flex;"><span> switched_controller.Switch(<span style="color:#ae81ff">1</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } <span style="color:#66d9ef">else</span> { <span style="color:#75715e">// mode2 +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> <span style="color:#66d9ef">if</span> (chance[<span style="color:#ae81ff">0</span>] <span style="color:#f92672">&lt;</span> pr_12) { +</span></span><span style="display:flex;"><span> which_mode <span style="color:#f92672">=</span> <span style="color:#ae81ff">1</span>; +</span></span><span style="display:flex;"><span> controlled_system.set_B(b1); +</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> (do_switch_ctrl) { +</span></span><span style="display:flex;"><span> switched_controller.Switch(<span style="color:#ae81ff">0</span>); +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> } +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Simulate the true system. +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> z.col(t) <span style="color:#f92672">=</span> controlled_system.Simulate(u.col(t <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>)); +</span></span><span style="display:flex;"><span> +</span></span><span style="display:flex;"><span> <span style="color:#75715e">// perform control +</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span> u.col(t) <span style="color:#f92672">=</span> switched_controller.ControlOutputReference(z.col(t)); +</span></span></code></pre></div><p>Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system&rsquo;s operating mode and call the <code>Switch</code> method accordingly. Such a decoder is not currently included in this library.</p> +<h2 id="example-simulation-result"> + Example simulation result + <a class="anchor" href="#example-simulation-result">#</a> +</h2> +<p>Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller&rsquo;s online estimates of the output and state are shown in purple.</p> +<p><img src="/lds-ctrl-est/eg_plds_switched_ctrl_output.png" alt="example control output" /></p> +<p>Note that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#plds-switched-control-tutorial">PLDS Switched Control Tutorial</a> + <ul> + <li><a href="#preamble">Preamble</a></li> + <li><a href="#creating-the-simulated-system">Creating the simulated system</a></li> + <li><a href="#creating-the-controller">Creating the controller</a></li> + <li><a href="#simulating-control">Simulating control</a></li> + <li><a href="#example-simulation-result">Example simulation result</a></li> + </ul> + </li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/tutorials/index.html b/docs/docs/tutorials/index.html new file mode 100644 index 00000000..f9a281cc --- /dev/null +++ b/docs/docs/tutorials/index.html @@ -0,0 +1,243 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Examples + # +"> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="LDS C&#43;E Examples"> + <meta property="og:description" content="Examples #"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>LDS C&#43;E Examples | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>LDS C&#43;E Examples</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#examples">Examples</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="examples"> + Examples + <a class="anchor" href="#examples">#</a> +</h1> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#examples">Examples</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/docs/tutorials/index.xml b/docs/docs/tutorials/index.xml new file mode 100644 index 00000000..232f8ad3 --- /dev/null +++ b/docs/docs/tutorials/index.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>LDS C&#43;E Examples on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/</link> + <description>Recent content in LDS C&#43;E Examples on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title>GLDS Control</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</guid> + <description>&lt;h1 id=&#34;glds-control-tutorial&#34;&gt;&#xA; GLDS Control Tutorial&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#glds-control-tutorial&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This tutorial shows how to use this library to control a system with a Gaussian LDS controller (&lt;code&gt;lds::gaussian::Controller&lt;/code&gt;). In place of a physical system, a GLDS model (&lt;code&gt;lds::gaussian::System&lt;/code&gt;) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.&lt;/p&gt;</description> + </item> + <item> + <title>PLDS State Estimation</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</guid> + <description>&lt;h1 id=&#34;plds-state-estimation-tutorial&#34;&gt;&#xA; PLDS State Estimation Tutorial&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#plds-state-estimation-tutorial&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (&lt;code&gt;lds::poisson::System&lt;/code&gt;) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.&lt;/p&gt;&#xA;&lt;p&gt;The full code for this can be found &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</description> + </item> + <item> + <title>PLDS Switched Control</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</guid> + <description>&lt;h1 id=&#34;plds-switched-control-tutorial&#34;&gt;&#xA; PLDS Switched Control Tutorial&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#plds-switched-control-tutorial&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This tutorial shows how to use this library to control a system with a switched PLDS controller (&lt;code&gt;lds::poisson::SwitchedController&lt;/code&gt;). This type of controller is applicable in scenarios where a physical system is not accurately captured by a &lt;strong&gt;single&lt;/strong&gt; LDS but has &lt;strong&gt;multiple&lt;/strong&gt; discrete operating modes where the dynamics can be well-approximated as linear.&lt;/p&gt;&#xA;&lt;p&gt;In the example that follows, another PLDS model (&lt;code&gt;lds::poisson::System&lt;/code&gt;) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.&lt;/p&gt;</description> + </item> + </channel> +</rss> diff --git a/docs/eg_glds_ctrl_output.png b/docs/eg_glds_ctrl_output.png new file mode 100644 index 0000000000000000000000000000000000000000..9e7d9b202f4f59aaf05ddcaa8c57ffbecb774455 GIT binary patch literal 169071 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 Dff{T@ literal 0 HcmV?d00001 diff --git a/docs/eg_plds_ctrl_output.png b/docs/eg_plds_ctrl_output.png new file mode 100644 index 0000000000000000000000000000000000000000..c3f7346baee2c2d6a7d23061c68205bf79a0ada3 GIT binary patch literal 217595 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%0D1WbS8e-o!u7dF9y@ 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 Yd*GY3hX+T%YvSn~($v$)Qn$SEA2o%2F#rGn literal 0 HcmV?d00001 diff --git a/docs/eg_plds_est_output.png b/docs/eg_plds_est_output.png new file mode 100644 index 0000000000000000000000000000000000000000..6008362171c47023d581ec3defb02b3ac0d45569 GIT binary patch literal 148988 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 ZGsML|l*2$VS&=Aw8R?np7E>M0{y%7YwGsdT literal 0 HcmV?d00001 diff --git a/docs/eg_plds_est_output_hist.png b/docs/eg_plds_est_output_hist.png new file mode 100644 index 0000000000000000000000000000000000000000..992aecaf8d2cfd2c260474348b540256e2b90d27 GIT binary patch literal 29070 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 J9KFNg{{clw1DOB- literal 0 HcmV?d00001 diff --git a/docs/eg_plds_switched_ctrl_output.png b/docs/eg_plds_switched_ctrl_output.png new file mode 100644 index 0000000000000000000000000000000000000000..6fd77957e1ef90b23a3b03aa477c5d3d259742c1 GIT binary patch literal 156078 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 literal 0 HcmV?d00001 diff --git a/docs/en.search-data.min.1bc81b8f6c065ee207cd6c9ffacb10cc8cbb02c0e2b14346c8eca1b0148e9277.json b/docs/en.search-data.min.1bc81b8f6c065ee207cd6c9ffacb10cc8cbb02c0e2b14346c8eca1b0148e9277.json new file mode 100644 index 00000000..d678cdc6 --- /dev/null +++ b/docs/en.search-data.min.1bc81b8f6c065ee207cd6c9ffacb10cc8cbb02c0e2b14346c8eca1b0148e9277.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:32:33 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.30eff4c2c98b0267a65ac83cdd908a3d059084312c7d6ca2681cf60eebe832c2.json b/docs/en.search-data.min.30eff4c2c98b0267a65ac83cdd908a3d059084312c7d6ca2681cf60eebe832c2.json new file mode 100644 index 00000000..492e4d4c --- /dev/null +++ b/docs/en.search-data.min.30eff4c2c98b0267a65ac83cdd908a3d059084312c7d6ca2681cf60eebe832c2.json @@ -0,0 +1 @@ +[{"id":0,"href":"/docs/","title":"LDS C+E Documentation","section":"LDS Control and Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/docs/api/","title":"API Reference","section":"LDS C+E Documentation","content":" API Reference # The API documentation is organized into the following sections:\nClasses - Documentation for all classes Namespaces - Documentation for all namespaces Files - Documentation for all files Modules - Documentation for all modules Examples - Documentation for all examples "},{"id":3,"href":"/acknowledgements/","title":"Acknowledgements","section":"LDS Control and Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":4,"href":"/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":5,"href":"/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":6,"href":"/issues-contributing/","title":"Issues Contributing","section":"LDS Control and Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":7,"href":"/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":8,"href":"/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":9,"href":"/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":10,"href":"/docs/api/classes/","title":"Classes","section":"API Reference","content":" Classes # "},{"id":11,"href":"/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":12,"href":"/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":13,"href":"/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":14,"href":"/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":15,"href":"/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":16,"href":"/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":17,"href":"/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":18,"href":"/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":19,"href":"/docs/api/examples/","title":"Examples","section":"API Reference","content":" Examples # "},{"id":20,"href":"/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":21,"href":"/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":22,"href":"/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":23,"href":"/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":24,"href":"/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":25,"href":"/docs/api/files/","title":"Files","section":"API Reference","content":" Files # "},{"id":26,"href":"/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":27,"href":"/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":28,"href":"/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":29,"href":"/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":30,"href":"/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":31,"href":"/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":32,"href":"/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":33,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":34,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":35,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":36,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":37,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":38,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":39,"href":"/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":40,"href":"/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":41,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":42,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":43,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":44,"href":"/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":45,"href":"/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":46,"href":"/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":47,"href":"/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":48,"href":"/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":49,"href":"/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":50,"href":"/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":51,"href":"/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":52,"href":"/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":53,"href":"/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":54,"href":"/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":55,"href":"/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":56,"href":"/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":57,"href":"/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":58,"href":"/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":59,"href":"/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":60,"href":"/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":61,"href":"/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":62,"href":"/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":63,"href":"/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":64,"href":"/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":65,"href":"/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":66,"href":"/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":67,"href":"/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":68,"href":"/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":69,"href":"/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":70,"href":"/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":71,"href":"/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":72,"href":"/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":73,"href":"/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":74,"href":"/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":75,"href":"/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":76,"href":"/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":77,"href":"/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":78,"href":"/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":79,"href":"/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":80,"href":"/docs/api/modules/","title":"Modules","section":"API Reference","content":" Modules # "},{"id":81,"href":"/docs/api/namespaces/","title":"Namespaces","section":"API Reference","content":" Namespaces # "},{"id":82,"href":"/docs/api/pages/","title":"Pages","section":"API Reference","content":" Pages # Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":83,"href":"/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":84,"href":"/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":85,"href":"/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":86,"href":"/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":87,"href":"/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":88,"href":"/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":89,"href":"/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":90,"href":"/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":91,"href":"/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:35:01 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.3daf9b62119cf749584e6093a5ab5ecb6b0c3b3fcee13be147e897573018facc.json b/docs/en.search-data.min.3daf9b62119cf749584e6093a5ab5ecb6b0c3b3fcee13be147e897573018facc.json new file mode 100644 index 00000000..dfdc44d8 --- /dev/null +++ b/docs/en.search-data.min.3daf9b62119cf749584e6093a5ab5ecb6b0c3b3fcee13be147e897573018facc.json @@ -0,0 +1 @@ +[{"id":0,"href":"/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":7,"href":"/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":8,"href":"/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":10,"href":"/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":11,"href":"/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":12,"href":"/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":13,"href":"/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":14,"href":"/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":15,"href":"/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":16,"href":"/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:32:33 EST "},{"id":17,"href":"/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":18,"href":"/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":19,"href":"/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":20,"href":"/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":21,"href":"/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":22,"href":"/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":23,"href":"/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":24,"href":"/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":25,"href":"/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":27,"href":"/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":28,"href":"/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":29,"href":"/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":30,"href":"/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":31,"href":"/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":32,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":33,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":34,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":35,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":36,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":37,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":38,"href":"/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":39,"href":"/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":40,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":41,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":42,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":43,"href":"/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":44,"href":"/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":45,"href":"/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":46,"href":"/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":47,"href":"/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":48,"href":"/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":49,"href":"/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":50,"href":"/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":51,"href":"/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":52,"href":"/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":53,"href":"/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":54,"href":"/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":55,"href":"/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":56,"href":"/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":57,"href":"/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":58,"href":"/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":59,"href":"/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":60,"href":"/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":61,"href":"/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":62,"href":"/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":63,"href":"/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":64,"href":"/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":65,"href":"/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":66,"href":"/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":67,"href":"/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":68,"href":"/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":69,"href":"/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":70,"href":"/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":71,"href":"/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":72,"href":"/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":73,"href":"/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":74,"href":"/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":75,"href":"/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":76,"href":"/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":77,"href":"/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":78,"href":"/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":80,"href":"/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 16:32:33 EST\n"},{"id":81,"href":"/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":82,"href":"/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":85,"href":"/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":86,"href":"/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":87,"href":"/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":88,"href":"/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":89,"href":"/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:32:33 EST\n"},{"id":90,"href":"/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:32:33 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.4c637a056ddfb2c8bcb41aabcefd2837a2544211e1d1e02e860af9cc2cf113f6.json b/docs/en.search-data.min.4c637a056ddfb2c8bcb41aabcefd2837a2544211e1d1e02e860af9cc2cf113f6.json new file mode 100644 index 00000000..c10f1b8b --- /dev/null +++ b/docs/en.search-data.min.4c637a056ddfb2c8bcb41aabcefd2837a2544211e1d1e02e860af9cc2cf113f6.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:20:07 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:20:07 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:20:07 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:20:07 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:20:07 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 16:20:07 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:20:07 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:20:07 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.51330e61c7b1fd13d76bf8145d7df78bb633baa004df0164d36937dcba61b94f.json b/docs/en.search-data.min.51330e61c7b1fd13d76bf8145d7df78bb633baa004df0164d36937dcba61b94f.json new file mode 100644 index 00000000..6e640db6 --- /dev/null +++ b/docs/en.search-data.min.51330e61c7b1fd13d76bf8145d7df78bb633baa004df0164d36937dcba61b94f.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:11:28 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:11:28 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:11:28 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 21:11:28 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:11:28 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 21:11:27 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 21:11:27 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 21:11:28 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 21:11:28 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 21:11:28 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.65c34f625f9950a602a0226433da4c37549866f3f72e93c93939e71f342009dc.json b/docs/en.search-data.min.65c34f625f9950a602a0226433da4c37549866f3f72e93c93939e71f342009dc.json new file mode 100644 index 00000000..0124b02d --- /dev/null +++ b/docs/en.search-data.min.65c34f625f9950a602a0226433da4c37549866f3f72e93c93939e71f342009dc.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # "},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # "},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # "},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # "},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:35:01 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.88021e71daa56310a06a39db6e72993621d34c5a76c9ed5ecd29b1bf4ad2f8db.json b/docs/en.search-data.min.88021e71daa56310a06a39db6e72993621d34c5a76c9ed5ecd29b1bf4ad2f8db.json new file mode 100644 index 00000000..6f9a9a40 --- /dev/null +++ b/docs/en.search-data.min.88021e71daa56310a06a39db6e72993621d34c5a76c9ed5ecd29b1bf4ad2f8db.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:42:06 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:42:06 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:42:06 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:42:06 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:42:06 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 16:42:06 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:42:06 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:42:06 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.916c0a771f703a05af3ec0233f9223552556f3ef4854450f4c4b69af5e5495e2.json b/docs/en.search-data.min.916c0a771f703a05af3ec0233f9223552556f3ef4854450f4c4b69af5e5495e2.json new file mode 100644 index 00000000..8ff6233b --- /dev/null +++ b/docs/en.search-data.min.916c0a771f703a05af3ec0233f9223552556f3ef4854450f4c4b69af5e5495e2.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\nWhen editing any documentation/guides, please use the markdown docs in misc/docs-hugo instead of directly editing the HTML docs. This may require having hugo, graphviz and doxygen installed through brew, as well as doxybook2 installed through a git clone.\nClone the doxybook2 repository online and place the executable in your usr/local/bin folder or another bin folder. Run sudo chmod +x /usr/local/bin/doxybook2 to give doxybook2 permissions. Run doxybook2 --help to ensure that this works properly. If permission is still denied, navigate to System Preferences \u0026gt; Security \u0026amp; Privacy \u0026gt; General. You should see a message at the bottom that says: \u0026ldquo;doxybook2 was blocked from use because it is not from an identified developer.\u0026rdquo; Click Allow Anyway.\nUpdated docs can be built by running cd scripts and ./update-docs.sh. After a successful build, navigate to /misc/docs-hugo/ and run hugo server to create a local host of the website. Access the website using local host to ensure the correct results.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 21:41:26 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 21:41:26 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\nModel Predictive Control # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:41:27 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:41:27 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:41:27 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 21:41:27 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:41:27 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 21:41:26 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 21:41:27 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 21:41:27 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 21:41:27 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.9cfd320ab0b81a7a116ce6b7963a474b4ef48b8bab6a8c0001d3c629222792ea.json b/docs/en.search-data.min.9cfd320ab0b81a7a116ce6b7963a474b4ef48b8bab6a8c0001d3c629222792ea.json new file mode 100644 index 00000000..8bcc5fec --- /dev/null +++ b/docs/en.search-data.min.9cfd320ab0b81a7a116ce6b7963a474b4ef48b8bab6a8c0001d3c629222792ea.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\nWhen editing any documentation/guides, please use the markdown docs in misc/docs-hugo instead of directly editing the HTML docs. This may require having hugo, graphviz and doxygen installed through brew, as well as doxybook2 installed through a git clone.\nClone the doxybook2 repository online and place the executable in your usr/local/bin folder or another bin folder. Run sudo chmod +x /usr/local/bin/doxybook2 to give doxybook2 permissions. Run doxybook2 --help to ensure that this works properly. If permission is still denied, navigate to System Preferences \u0026gt; Security \u0026amp; Privacy \u0026gt; General. You should see a message at the bottom that says: \u0026ldquo;doxybook2 was blocked from use because it is not from an identified developer.\u0026rdquo; Click Allow Anyway.\nUpdated docs can be built by running cd scripts and ./update-docs.sh. After a successful build, navigate to /misc/docs-hugo/ and run hugo server to create a local host of the website. Access the website using local host to ensure the correct results.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\nModel Predictive Control # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:04:30 EDT "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:04:30 EDT "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:04:30 EDT "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 31 March 2025 at 16:04:30 EDT "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:04:30 EDT "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 31 March 2025 at 16:04:30 EDT\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 31 March 2025 at 16:04:30 EDT\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 31 March 2025 at 16:04:30 EDT\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.c719c889cec9be6cd5a2ff58d2119ba439cb5d872f0ffca15bdfacaa9767331b.json b/docs/en.search-data.min.c719c889cec9be6cd5a2ff58d2119ba439cb5d872f0ffca15bdfacaa9767331b.json new file mode 100644 index 00000000..81686767 --- /dev/null +++ b/docs/en.search-data.min.c719c889cec9be6cd5a2ff58d2119ba439cb5d872f0ffca15bdfacaa9767331b.json @@ -0,0 +1 @@ +[{"id":0,"href":"/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/docs/api/","title":"API Reference","section":"LDS C+E Documentation","content":" API Reference # The API documentation is organized into the following sections:\nClasses - Documentation for all classes Namespaces - Documentation for all namespaces Files - Documentation for all files Modules - Documentation for all modules Examples - Documentation for all examples "},{"id":3,"href":"/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":4,"href":"/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":5,"href":"/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":6,"href":"/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":7,"href":"/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":8,"href":"/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":9,"href":"/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":10,"href":"/docs/api/classes/","title":"Classes","section":"API Reference","content":" Classes # "},{"id":11,"href":"/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":12,"href":"/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":13,"href":"/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":14,"href":"/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":15,"href":"/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":16,"href":"/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":17,"href":"/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":18,"href":"/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":19,"href":"/docs/api/examples/","title":"Examples","section":"API Reference","content":" Examples # "},{"id":20,"href":"/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":21,"href":"/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":22,"href":"/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":23,"href":"/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":24,"href":"/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":25,"href":"/docs/api/files/","title":"Files","section":"API Reference","content":" Files # "},{"id":26,"href":"/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":27,"href":"/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":28,"href":"/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":29,"href":"/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":30,"href":"/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":31,"href":"/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":32,"href":"/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":33,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":34,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":35,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":36,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":37,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":38,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":39,"href":"/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":40,"href":"/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":41,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":42,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":43,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":44,"href":"/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":45,"href":"/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":46,"href":"/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":47,"href":"/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":48,"href":"/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":49,"href":"/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":50,"href":"/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":51,"href":"/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":52,"href":"/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":53,"href":"/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":54,"href":"/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":55,"href":"/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":56,"href":"/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":57,"href":"/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":58,"href":"/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":59,"href":"/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":60,"href":"/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":61,"href":"/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":62,"href":"/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":63,"href":"/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":64,"href":"/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":65,"href":"/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":66,"href":"/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":67,"href":"/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":68,"href":"/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":69,"href":"/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":70,"href":"/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":71,"href":"/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":72,"href":"/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":73,"href":"/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":74,"href":"/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":75,"href":"/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":76,"href":"/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":77,"href":"/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":78,"href":"/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":79,"href":"/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":80,"href":"/docs/api/modules/","title":"Modules","section":"API Reference","content":" Modules # "},{"id":81,"href":"/docs/api/namespaces/","title":"Namespaces","section":"API Reference","content":" Namespaces # "},{"id":82,"href":"/docs/api/pages/","title":"Pages","section":"API Reference","content":" Pages # Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":83,"href":"/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":84,"href":"/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":85,"href":"/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":86,"href":"/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":87,"href":"/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":88,"href":"/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":89,"href":"/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":90,"href":"/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":91,"href":"/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:35:01 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.d2e859c10d1d47f88862dc00415f6fddfe22120c22886c64132a9b3ce602e147.json b/docs/en.search-data.min.d2e859c10d1d47f88862dc00415f6fddfe22120c22886c64132a9b3ce602e147.json new file mode 100644 index 00000000..440b716d --- /dev/null +++ b/docs/en.search-data.min.d2e859c10d1d47f88862dc00415f6fddfe22120c22886c64132a9b3ce602e147.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:00:41 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:00:41 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:00:41 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 21:00:41 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:00:41 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 21:00:41 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 21:00:41 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 21:00:41 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.d8a5f567aaddfd14291ea701eca6ccd7c962a2433ee501ee3ca9187ef3d617fd.json b/docs/en.search-data.min.d8a5f567aaddfd14291ea701eca6ccd7c962a2433ee501ee3ca9187ef3d617fd.json new file mode 100644 index 00000000..4f054bc8 --- /dev/null +++ b/docs/en.search-data.min.d8a5f567aaddfd14291ea701eca6ccd7c962a2433ee501ee3ca9187ef3d617fd.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:18:09 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:18:09 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:18:10 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:18:10 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:18:10 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:18:10 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:18:10 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:18:09 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:18:09 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:18:09 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:18:09 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:18:09 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:18:09 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 16:18:10 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:18:10 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:18:10 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.d8f6979e36e91aaee0955534c9a6ee87be5533f8cf09b0d7dfd21dc847b28bc5.json b/docs/en.search-data.min.d8f6979e36e91aaee0955534c9a6ee87be5533f8cf09b0d7dfd21dc847b28bc5.json new file mode 100644 index 00000000..fbe57f32 --- /dev/null +++ b/docs/en.search-data.min.d8f6979e36e91aaee0955534c9a6ee87be5533f8cf09b0d7dfd21dc847b28bc5.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\nWhen editing any documentation/guides, please use the markdown docs in misc/docs-hugo instead of directly editing the HTML docs. This may require having hugo, graphviz and doxygen installed through brew, as well as doxybook2 installed through a git clone.\nClone the doxybook2 repository online and place the executable in your usr/local/bin folder or another bin folder. Run sudo chmod +x /usr/local/bin/doxybook2 to give doxybook2 permissions. Run doxybook2 --help to ensure that this works properly. If permission is still denied, navigate to System Preferences \u0026gt; Security \u0026amp; Privacy \u0026gt; General. You should see a message at the bottom that says: \u0026ldquo;doxybook2 was blocked from use because it is not from an identified developer.\u0026rdquo; Click Allow Anyway.\nUpdated docs can be built by running cd scripts and ./update-docs.sh. After a successful build, navigate to /misc/docs-hugo/ and run hugo server to create a local host of the website. Access the website using local host to ensure the correct results.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\nModel Predictive Control # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:03:56 EDT "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:03:56 EDT "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:03:56 EDT "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 31 March 2025 at 16:03:56 EDT "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 31 March 2025 at 16:03:56 EDT "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 31 March 2025 at 16:03:55 EDT\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 31 March 2025 at 16:03:55 EDT\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 31 March 2025 at 16:03:56 EDT\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 31 March 2025 at 16:03:56 EDT\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 31 March 2025 at 16:03:56 EDT\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.e015cd687a9e9fd3fc4e08fb324842e12bc2202a08e95f5d633e3dddc2361d47.json b/docs/en.search-data.min.e015cd687a9e9fd3fc4e08fb324842e12bc2202a08e95f5d633e3dddc2361d47.json new file mode 100644 index 00000000..8add89f9 --- /dev/null +++ b/docs/en.search-data.min.e015cd687a9e9fd3fc4e08fb324842e12bc2202a08e95f5d633e3dddc2361d47.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\nWhen editing any documentation/guides, please use the markdown docs in misc/docs-hugo instead of directly editing the HTML docs. Updated docs can be built by running cd scripts and ./update-docs.sh. This may require having hugo, graphviz and doxygen installed through brew, as well as doxybook2 installed through a git clone.\nClone the doxybook2 repository online and place the executable in your usr/local/bin folder or another bin folder. Run sudo chmod +x /usr/local/bin/doxybook2 to give doxybook2 permissions. Run doxybook2 --help to ensure that this works properly. If permission is still denied, navigate to System Preferences \u0026gt; Security \u0026amp; Privacy \u0026gt; General. You should see a message at the bottom that says: \u0026ldquo;doxybook2 was blocked from use because it is not from an identified developer.\u0026rdquo; Click Allow Anyway.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\nModel Predictive Control # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:33:49 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:33:49 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:33:49 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 21:33:49 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:33:49 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 21:33:49 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 21:33:49 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 21:33:49 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.f0cd534ef163f541ebcbce10f51eba1a283a32b46261720b6440d5fe719c1f2b.json b/docs/en.search-data.min.f0cd534ef163f541ebcbce10f51eba1a283a32b46261720b6440d5fe719c1f2b.json new file mode 100644 index 00000000..8d107065 --- /dev/null +++ b/docs/en.search-data.min.f0cd534ef163f541ebcbce10f51eba1a283a32b46261720b6440d5fe719c1f2b.json @@ -0,0 +1 @@ +[{"id":0,"href":"/lds-ctrl-est/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/lds-ctrl-est/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/lds-ctrl-est/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/lds-ctrl-est/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/lds-ctrl-est/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/lds-ctrl-est/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":7,"href":"/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":8,"href":"/lds-ctrl-est/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/lds-ctrl-est/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":10,"href":"/lds-ctrl-est/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":11,"href":"/lds-ctrl-est/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":12,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:01:32 EST "},{"id":13,"href":"/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:01:32 EST "},{"id":14,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:01:32 EST "},{"id":15,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 21:01:32 EST "},{"id":16,"href":"/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 21:01:32 EST "},{"id":17,"href":"/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":18,"href":"/lds-ctrl-est/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":19,"href":"/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":20,"href":"/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":21,"href":"/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":22,"href":"/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":23,"href":"/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":24,"href":"/lds-ctrl-est/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":25,"href":"/lds-ctrl-est/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":27,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":28,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":29,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":30,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":31,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":32,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":33,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":34,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":35,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":36,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":37,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":38,"href":"/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":39,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":40,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":41,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":42,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":43,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":44,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":45,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":46,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":47,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":48,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":49,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":50,"href":"/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":51,"href":"/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":52,"href":"/lds-ctrl-est/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":53,"href":"/lds-ctrl-est/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":54,"href":"/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":55,"href":"/lds-ctrl-est/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":56,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":57,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":58,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":59,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":60,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":61,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":62,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":63,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":64,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":65,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":66,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":67,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":68,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":69,"href":"/lds-ctrl-est/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":70,"href":"/lds-ctrl-est/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":71,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/lds-ctrl-est/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":72,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":73,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":74,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":75,"href":"/lds-ctrl-est/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":76,"href":"/lds-ctrl-est/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":77,"href":"/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":78,"href":"/lds-ctrl-est/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/lds-ctrl-est/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":80,"href":"/lds-ctrl-est/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 21:01:32 EST\n"},{"id":81,"href":"/lds-ctrl-est/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":82,"href":"/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":85,"href":"/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":86,"href":"/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":87,"href":"/lds-ctrl-est/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":88,"href":"/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":89,"href":"/lds-ctrl-est/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 21:01:32 EST\n"},{"id":90,"href":"/lds-ctrl-est/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 21:01:32 EST\n"}] \ No newline at end of file diff --git a/docs/en.search-data.min.f9279ffce4d2a4ee361f219216511c0e77ad58c4ba1b2be8f44059d8123bc4be.json b/docs/en.search-data.min.f9279ffce4d2a4ee361f219216511c0e77ad58c4ba1b2be8f44059d8123bc4be.json new file mode 100644 index 00000000..fd87e86e --- /dev/null +++ b/docs/en.search-data.min.f9279ffce4d2a4ee361f219216511c0e77ad58c4ba1b2be8f44059d8123bc4be.json @@ -0,0 +1 @@ +[{"id":0,"href":"/docs/","title":"LDS C+E Documentation","section":"LDS Control \u0026 Estimation","content":" LDS Control \u0026amp; Estimation Documentation # "},{"id":1,"href":"/docs/tutorials/","title":"LDS C+E Examples","section":"LDS C+E Documentation","content":" Examples # "},{"id":2,"href":"/acknowledgements/","title":"Acknowledgements","section":"LDS Control \u0026 Estimation","content":" Acknowledgements # Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.\n"},{"id":3,"href":"/docs/getting-started/getting-started/","title":"Getting Started","section":"LDS C+E Documentation","content":" Getting Started # This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.\nldsCtrlEst requires Armadillo for linear algebra as well as HDF5 for saving output. vcpkg is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.\nTested Configurations # Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the RelWithDebInfo build type in the CMake configure command (-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo):\nUbuntu 18.04 with GCC 7.5 compiler macOS 11 (Big Sur) with Apple Clang 12 compiler Windows 10 with Visual Studio 16.11 (2019 release) and Clang 12 compiler That being said, if you want to debug a build for a single platform, here are some things you can try:\nUse different compilers (or even different versions of a single compiler) Use different versions of vcpkg (which you can control by checking out a different commit in the vcpkg submodule) Mac Pre-requisities # Xcode Command Line Tools will get you clang, gcc, make, and git:\nxcode-select --install Homebrew is \u0026ldquo;The Missing Package Manager for macOS\u0026rdquo; which will make installing lots of things easy. Install like this:\n/bin/bash -c \u0026#34;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\u0026#34; You can then use it to install CMake, gfortran, and pkg-config:\nbrew install cmake gfortran pkg-config Linux Pre-requisites # You\u0026rsquo;ll need Git, CMake, GCC, gfortran, etc.\nsudo apt install git cmake pkg-config gfortran curl zip unzip tar build-essential ninja-build Windows Installation # Look here for Windows-specific instructions.\nDownloading the Library # First, clone the repository along with submodules:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est\rgit submodule update --init Compilation + Installation # Now generate the cache and build using your IDE or from the command line as follows.\nmkdir build \u0026amp;\u0026amp; cd build\rcmake ..\rcmake --build . The first time, vcpkg will automatically install dependencies into [build directory]/vcpkg_installed/, which will likely take about 10-20 minutes.\nIf you want to use vcpkg set up somewhere besides this repo\u0026rsquo;s submodule, add -DCMAKE_TOOLCHAIN_FILE=[path to vcpkg]/scripts/buildsystems/vcpkg.cmake to the cmake command directly or through your IDE\u0026rsquo;s settings.\nYou can verify the build is working by running ctest from the build folder, which runs all the example scripts.\nOptions # This project is configured/compiled/installed by way of CMake and (on Unix-based operating systems) GNU Make. For configuration with CMake, there are three available options.\nLDSCTRLEST_BUILD_EXAMPLES : [default=ON] whether to build example programs located under examples/ in the source tree LDSCTRLEST_BUILD_FIT : [default=ON] whether to build the auxiliary fitting portion of the source code that is not pertinent to control implementation LDSCTRLEST_BUILD_STATIC : [default=ON] whether to statically link against OpenBLAS and create a static ldsCtrlEst library for future use n.b., If both options 2 and 3 are enabled, Matlab/Octave mex functions will be compiled for exposing some of the fitting functionality to Matlab/Octave, assuming these programs are installed.\nBelow are example usages of cmake to configure/build the library.\nFor basic project build \u0026amp; install\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake .. #configure build cmake --build #build the project sudo make install #[optional] installs to default location (OS-specific) To set the install prefix\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DCMAKE_INSTALL_PREFIX=/your/install/prefix .. #configure build with chosen install location cmake --build #build the project make install #install to /your/install/prefix To build the bare bones project, excluding fit code and Matlab mex code.\ncd /path/to/repository mkdir build \u0026amp;\u0026amp; cd build cmake -DLDSCTRLEST_BUILD_FIT=0 .. #configure not to build the fitting portion of library make #build the project n.b., If you choose not to install the library or install it to the non-default location, ensure you have updated the following environment variables on Unix-based operating systems.\nLD_LIBRARY_PATH: search path for dynamically loaded libraries PKG_CONFIG_PATH: search path for pkg-config tool On Windows, you may need to add the build location to the PATH environment variable for the library to be used elsewhere.\nPython bindings package ldsctrlest # With the LDSCTRLEST_BUILD_PYTHON setting (off by default) and the pybind11 submodule initialized, you can build Python bindings. You will probably want to specify the installation of Python to use by adding a -DPython3_ROOT_DIR=[path/to/install/dir] argument to the CMake cache generation command (the first one) so CMake doesn\u0026rsquo;t use an undesired version. That environment needs to have NumPy installed.\ncmake --build . --target python_modules The bindings need to be generated just once per Python version. Once the build is complete, navigate to the [build location]/python folder and run pip install . to make it importable anywhere for your current environment. The file structure only works correctly for this if you use a single-config generator like Ninja or Make, though. You can verify the installation was successful by running pytest from the build/python directory (pip install pytest matplotlib first if you need to).\nSee python/ldsctrlest/README.md for usage details.\nAlso, beware that a single build will probably not work for both the standalone library and the Python package, since the conversion between NumPy and Armadillo alters the way Armadillo allocates memory. In this case you may want to build once with -DLDSCTRLEST_BUILD_PYTHON=ON, install the package, then again with -DLDSCTRLEST_BUILD_PYTHON=OFF for the pure C++ build to work correctly.\nCommon issues # \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; If cmake and/or pkg-config cannot find the required configuration files for your project to link against ldsCtrlEst, make sure that these utilities know to look for them in the non-default location where you installed the library. For cmake this means adding your chosen install prefix to the environment variable CMAKE_PREFIX_PATH. Similarly, for pkg-config you need to add your/install/prefix/lib/pkgconfig to its search path, PKG_CONFIG_PATH. Assuming a Unix shell whose login startup file is ~/.profile and ldsCtrlEst was installed using prefix your/install/prefix, add the following to .profile.\nexport CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/your/install/prefix export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/your/install/prefix vcpkg fails on configuration Try running ./bootstrap-vcpkg from the vcpkg folder and try again. If that doesn\u0026rsquo;t work, try updating vcpkg to a newer version (in the source control tab, click on the commit hash by the vcpkg repo then select from the dropdown) and running boostsrap-vcpkg again. You can also try upgrading your system (e.g., apt update, apt upgrade).\nCould not find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy)\nMake sure NumPy is installed in the Python environment you specified. If CMake still can\u0026rsquo;t find it, you may need to tell CMake exactly where to find it by adding an argument to the configure command: -DPython3_NumPy_INCLUDE_DIR=.... You can find that location like this: python -c 'import numpy; print(numpy.get_include())'\n"},{"id":4,"href":"/docs/getting-started/windows/","title":"Windows","section":"LDS C+E Documentation","content":" Windows Installation # Windows Pre-requisites # Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:\nSet-ExecutionPolicy RemoteSigned -Scope CurrentUser # Optional: Needed to run a remote script the first time iwr get.scoop.sh | Invoke-Expression Install Git and CMake if you don\u0026rsquo;t already have them:\nscoop install git cmake If that didn\u0026rsquo;t work, follow more detailed instructions here.\nThe easiest way to compile C++ project on Windows is with Visual Studio\u0026rsquo;s build tools, which you can download here (or here for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on \u0026ldquo;Desktop development with C++.\u0026rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the \u0026ldquo;Installation details\u0026rdquo; sidebar under optional features.\nAnd the easiest way to use Visual Studio\u0026rsquo;s build tools is with VS Code, along with the CMake Tools extension. Install them and you should be ready to go.\nDownloading the Library # First, clone the repository, either from VS Code or the command line:\ngit clone https://github.com/cloctools/lds-ctrl-est.git cd lds-ctrl-est You\u0026rsquo;ll need to initialize the submodules from the command line after the repo is cloned:\ngit submodule update --init Installation # When you open the folder in VS Code, you will like be prompted by the CMake Tools extension to configure the project. Make sure you select the kit (you\u0026rsquo;ll be prompted when you configure\u0026ndash;else there\u0026rsquo;s an icon in the bar on the bottom of the window or type Ctrl+Shift+P, then \u0026ldquo;cmake select kit\u0026rdquo;). Choose Clang [latest version] with GNU CLI ... amd64 assuming you are running a 64-bit OS. (MSVC may work okay too if you don\u0026rsquo;t need to build Python bindings.)\nFollow along with the \u0026ldquo;Getting Started\u0026rdquo; instructions, but where you see config options specified as -DLDSCTREST_BUILD_STATIC=OFF or -DPython3_ROOT_DIR=..., you will enter those in settings: open with Ctrl+,, click \u0026ldquo;workspace\u0026rdquo;, then search for \u0026ldquo;CMake: Configure Args\u0026rdquo; and enter each of your desired arguments as a separate item.\nTo configure, use Ctrl+Shift+P and search for the \u0026ldquo;CMake: Configure\u0026rdquo; command. To build, click the \u0026ldquo;Build\u0026rdquo; button on the bottom bar. Then click the \u0026ldquo;CTest\u0026rdquo; button to run the example scripts.\nConsiderations # Development on Windows has been more prone to bugs than on Unix systems, so if you encounter many problems, consider switching—WSL (Windows Subsystem for Linux) is a good option for Windows users who don\u0026rsquo;t want to work on a different machine.\nCompilation has been successfully tested in VS Code using the following kit, using the \u0026ldquo;RelWithDebInfo\u0026rdquo; config:\nClang 12.0.0 (GNU CLI) for MSVC 16.11.31702.278 (Visual Studio Community 2019 Release - amd64) Troubleshooting # The build appears to work, but tests fail with code 0xc0000135 OR \u0026ldquo;I have built the library and installed it in a non-default location. In building my own project linking against ldsCtrlEst, cmake or pkg-config cannot find the library or its configuration information.\u0026rdquo; Have you installed the library? In VS Code, use Shift+F7 to build a specific target, in this case INSTALL. If that doesn\u0026rsquo;t solve your problem, you will likely need to add the build or install folder to your PATH environment variable, which you can do using the settings GUI (search for \u0026ldquo;Edit the system environment variables\u0026rdquo;).\nOn Windows, \u0026ldquo;Generate CMake Cache\u0026rdquo; step errs because creating symbolic links is not permitted. Certain source files are sym-linked to the build/install directories during configuration with cmake. As such, your user in Windows must be permitted to do so. Make sure that your user is listed next to Control Panel -\u0026gt; Administrative Tools -\u0026gt; Local Policies -\u0026gt; User Rights Assignment -\u0026gt; Create Symbolic Links.\n"},{"id":5,"href":"/issues-contributing/","title":"Issues Contributing","section":"LDS Control \u0026 Estimation","content":" Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.\nContributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository.\n"},{"id":6,"href":"/docs/api/namespaces/namespacearmamexc/","title":"armamexc","section":"Namespaces","content":" armamexc # arma/mex interface using Matlab C API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; T m2T_scalar(const mxArray * matlab_scalar)\nConvert Matlab mxArray to scalar of type T. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true)\nConvert matlab matrix to armadillo. template \u0026lt;typename T \u0026gt; mxArray * a2m_mat(arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat)\nConvert armadillo to matlab matrix. template \u0026lt;typename T \u0026gt; mxArray * a2m_vec(arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec)\nConvert armadillo to matlab vector. Detailed Description # Utilities for arma/mex interface using Matlab C API\nFunction Details # m2T_scalar # template \u0026lt;class T \u0026gt; inline T m2T_scalar( const mxArray * matlab_scalar ) Parameters:\nmatlab_scalar matlab scalar Template Parameters:\nT type Return: scalar of type T\nm2a_mat # template \u0026lt;class T \u0026gt; inline arma::Mat\u0026lt; T \u0026gt; m2a_mat( const mxArray * matlab_mat, bool copy_aux_mem =false, bool strict =true ) Parameters:\nmatlab_mat matlab matrix copy_aux_mem [optional] whether to copy auxiliary memory strict [optional] strictly enforce the above Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_mat( arma::Mat\u0026lt; T \u0026gt; const \u0026amp; arma_mat ) Parameters:\narma_mat armadillo matrix Return: matlab matrix\na2m_vec # template \u0026lt;typename T \u0026gt; inline mxArray * a2m_vec( arma::Col\u0026lt; T \u0026gt; const \u0026amp; arma_vec ) Parameters:\narma_vec armadillo vector Return: matlab vector\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":7,"href":"/docs/api/namespaces/namespacearmamexcpp/","title":"armamexcpp","section":"Namespaces","content":" armamexcpp # arma/mex interface using Matlab C++ API More\u0026hellip; Functions # Name template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat(matlab::data::CellArray \u0026amp; matlab_cell)\nConvert matlab cell array to vector of armadillo matrices. template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array)\nConvert matlab matrix to a vector of scalars. template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo vector. template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array)\nConvert matlab to armadillo matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat(const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab matrix. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec(const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert armadillo to matlab vector. template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec(const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory)\nConvert vector of scalar T to matlab matrix. Detailed Description # utilities for arma/mex interface using Matlab C++ API\nFunction Details # m2a_cellmat # template \u0026lt;class T \u0026gt; std::vector\u0026lt; arma::Mat\u0026lt; T \u0026gt; \u0026gt; m2a_cellmat( matlab::data::CellArray \u0026amp; matlab_cell ) Parameters:\nmatlab_cell matlab cell Template Parameters:\nT type Return: vector of armadillo matrices of type T\nm2s_vec # template \u0026lt;class T \u0026gt; std::vector\u0026lt; T \u0026gt; m2s_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; \u0026amp; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: vector of type T\nm2a_vec # template \u0026lt;class T \u0026gt; arma::Col\u0026lt; T \u0026gt; m2a_vec( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab array Template Parameters:\nT type Return: armadillo vector of type T\nm2a_mat # template \u0026lt;class T \u0026gt; arma::Mat\u0026lt; T \u0026gt; m2a_mat( matlab::data::TypedArray\u0026lt; T \u0026gt; matlab_array ) Parameters:\nmatlab_array matlab matrix Template Parameters:\nT type Return: armadillo matrix of type T\na2m_mat # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_mat( const arma::Mat\u0026lt; T \u0026gt; \u0026amp; arma_mat, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_mat arma matrix factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\na2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; a2m_vec( const arma::Col\u0026lt; T \u0026gt; \u0026amp; arma_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\narma_vec armadillo vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\ns2m_vec # template \u0026lt;class T \u0026gt; matlab::data::TypedArray\u0026lt; T \u0026gt; s2m_vec( const std::vector\u0026lt; T \u0026gt; \u0026amp; std_vec, matlab::data::ArrayFactory \u0026amp; factory ) Parameters:\nstd_vec standard vector factory matlab \u0026ldquo;array factory\u0026rdquo; Template Parameters:\nT type Return: matlab matrix\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":8,"href":"/docs/terminology/control-estimation/","title":"C\u0026E","section":"LDS C+E Documentation","content":" Control \u0026amp; Estimation # The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., filtering). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.\nState estimation # In general, the filtering performed to estimate the underlying state proceeds recursively by first using the model dynamics to predict the state change at the next time step, followed by updating this prediction when a new measurement is available. For a LDS, this two-step process can be summarized by \\[\\widehat{\\mathbf{x}}_{t|t-1} = \\mathbf{A}\\widehat{\\mathbf{x}}_{t-1|t-1} \u0026#43; \\mathbf{B} u_{t-1} \u0026#43; \\mathbf{m}_{t-1} \\;,\\] \\[\\widehat{\\mathbf{x}}_{t|t} = \\widehat{\\mathbf{x}}_{t|t-1} \u0026#43; \\mathbf{K}^{\\rm e}_t \\left(\\mathbf{z}_t - \\widehat{\\mathbf{y}}_{t|t-1}\\right)\\;,\\] where \\( \\hat{\\left(\\cdot\\right)}_{t|j} \\) indicates an estimate at time \\( t \\) given data up to time \\( j \\) inclusive, \\( \\mathbf{K}^{\\rm e} \\) is the estimator gain, and\n\\[ \\widehat{\\mathbf{y}}_{t|t-1} = h\\left( \\widehat{\\mathbf{x}}_{t|t-1} \\right) \\; .\\] In the case of GLDS models, the estimator gain (called Ke in library) is calculated recursively by Kalman filtering, which requires knowledge of the process noise and measurement noise covariances (Q, R) in addition to the system matrices. For time-invariant GLDS models, the infinite horizon solution is often used, so this gain need not be time-varying. Users may instead set its pre-determined value with the lds::gaussian::System::set_Ke mutator.\nIn the case of PLDS models, there is an analogue of the Kalman filter developed for dynamical systems with point-process observations (Eden et al. 2004). This nonlinear filter recursively updates Ke at each time step and requires an estimate of the process noise covariance (Q) as well.\nAdaptive estimation of process disturbance # Both the Kalman filter and point-process analogue are model-based; therefore, their performance can be sensitive to model mismatch, whether this be imperfect model fitting or true drifts in system behavior. A practical approach to improving robustness is parameter adaptation. To that end, this library provides dual state-parameter estimation. Specifically, an additive process disturbance (m) is adaptively re-estimated when the lds::System::do_adapt_m property is set to true. This effectively provides integral action on minimizing state estimation error that could either be due to model mismatch or a true disturbance.\nWhen parameter adaptation is enabled, this process disturbance is assumed to vary stochastically on a random walk \\[\\mathbf{m}_{t} = \\mathbf{m}_{t-1} \u0026#43; \\mathbf{w}^m_{t-1} \\;,\\] where \\( \\mathbf{w}^m \\sim \\mathcal{N}\\left(0, \\mathbf{Q}_m\\right)\\) . Kalman filtering or the point-process analogue are then used to estimate this disturbance in parallel with the state.\nControl # Given the estimated state, the controller updates the inputs to the system according to the following law: \\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] where \\( \\left( \\cdot \\right)^{\\rm ref} \\) correspond to reference/target signals and \\( \\mathbf{K}^c_x \\) is the state feedback controller gain. Recall that these controller gains are assumed to have been designed before the experiment using, for example, LQR.\nIf users are employing integral action for more robust tracking at DC and did not use the approach of augmenting the state vector and system matrices accordingly, there is an option to include the integral term as\n\\[\\mathbf{u}_{t} = \\mathbf{u}^{\\rm ref}_t - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right) - \\mathbf{K}^c_{\\rm inty} \\sum_{j=1}^{t}\\left( \\widehat{\\mathbf{y}}_j - \\mathbf{y}^{\\rm ref}_j \\right) \\;.\\] An additional option available to users is a control law that updates the change in u,\n\\[\\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\left(\\mathbf{u}_{t-1} - \\mathbf{u}^{\\rm ref}_{t-1} \\right) - \\mathbf{K}^c_x \\left( \\widehat{\\mathbf{x}}_t - \\mathbf{x}^{\\rm ref}_t\\right)\\;,\\] \\[\\mathbf{u}_{t} = \\mathbf{u}_{t-1} \u0026#43; \\Delta\\mathbf{u}_{t} \\; .\\] Notice that this takes the form of a first-order difference equation for updating control (i.e., \\( \\Delta\\mathbf{u}_{t} = -\\mathbf{K}^c_u \\mathbf{u}_{t-1} \u0026#43; \\epsilon_{t-1} \\) ), effectively low-pass filtering the input depending on the characteristics of \\( \\mathbf{K}^c_u \\) . This can be useful in cases where users have designed the controller gains by LQR to minimize not the amplitude of the input, but the change in input, by augmenting the state vector with the input during LQR design.\nIntegral action and the \\( \\Delta \\mathbf{u} \\) control law can be combined. The library keeps track of the controller type by way of bit masks which can be bit-wise OR\u0026rsquo;d to use in combination.\nCalculating reference state-control from output # In cases where an output reference is supplied and the goal is to track either a static or slowly varying output, users do not have to produce \\( \\mathbf{x}^{\\rm ref} \\) and \\( \\mathbf{u}^{\\rm ref} \\) . Methods are provided for calculating the state and control that would be required to reach the reference output at steady state (lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference). This is achieved by linearly-constrained least squares. For single-output systems, it results in an exact solution; however, for multi-output problems it provides a least squares comprimise across outputs.\n"},{"id":9,"href":"/docs/api/classes/","title":"Classes","section":"LDS C+E Documentation","content":" Classes # lds::Controller\nlds::EM\nlds::Fit LDS Fit Type.\nlds::SSID\nlds::SwitchedController SwitchedController Type.\nlds::System Linear Dynamical System Type.\nlds::UniformMatrixList\nlds::UniformSystemList\nlds::UniformVectorList\nlds::gaussian::Controller Gaussian-observation Controller Type.\nlds::gaussian::Fit GLDS Fit Type.\nlds::gaussian::FitEM GLDS E-M Fit Type.\nlds::gaussian::FitSSID Subspace Identification (SSID) for GLDS.\nlds::gaussian::SwitchedController Gaussian-observation SwitchedController Type.\nlds::gaussian::System Gaussian LDS Type.\nlds::poisson::Controller PLDS Controller Type.\nlds::poisson::Fit PLDS Fit Type.\nlds::poisson::FitEM PLDS E-M Fit Type.\nlds::poisson::FitSSID Subspace Identification (SSID) for PLDS.\nlds::poisson::SwitchedController Poisson-observation SwitchedController Type.\nlds::poisson::System Poisson System type.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":10,"href":"/docs/api/modules/group__control__masks/","title":"Control Mode Bit Masks","section":"Modules","content":" Control Mode Bit Masks # provides fill types for constructing new armadillo vectors, matrices More\u0026hellip; Attributes # Name const std::size_t kControlTypeDeltaU control designed to penalize change in input const std::size_t kControlTypeIntY control using integral action const std::size_t kControlTypeAdaptM adapt control setpoint with re-estimated disturbance m Detailed Description # Control mode bit masks. These can be bit-wise OR\u0026rsquo;d to use in combination.\nAttribute Details # kControlTypeDeltaU # static const std::size_t kControlTypeDeltaU = 0x1; Control was designed to penalize change in input (i.e., the state was augmented with input u)\nkControlTypeIntY # static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; Control using integral action (i.e., the state was augmented with output y during design)\nkControlTypeAdaptM # static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; Adapt control setpoint adapted with re-estimated process disturbance m.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":11,"href":"/docs/api/modules/group__defaults/","title":"Defaults","section":"Modules","content":" Defaults # More\u0026hellip; Attributes # Name const data_t kDefaultP0 default state estimate covar const data_t kDefaultQ0 default process noise covar const data_t kDefaultR0 default output noise covar Detailed Description # Default values for common variables (e.g., default diagonal elements of covariances)\nAttribute Details # kDefaultP0 # static const data_t kDefaultP0 = 1e-6; kDefaultQ0 # static const data_t kDefaultQ0 = 1e-6; kDefaultR0 # static const data_t kDefaultR0 = 1e-2; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":12,"href":"/docs/api/examples/eg_glds_ctrl_8cpp-example/","title":"eg_glds_ctrl.cpp","section":"Examples","content":" eg_glds_ctrl.cpp # Example GLDS Control ```cpp\n//===\u0026ndash; eg_glds_ctrl.cpp - Example GLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS Control ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\n// output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4;\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false;\n// Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err\n// setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]);\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; }\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":13,"href":"/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/","title":"eg_glds_du_plds_ctrl.cpp","section":"Examples","content":" eg_glds_du_plds_ctrl.cpp # Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u). ```cpp\n//===\u0026ndash; eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026quot;;\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt);\n// construct ground truth system to be controlled\u0026hellip; // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0);\nsize_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\n// initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// make a controller lds::gaussian::Controller controller; { // Create incorrect model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50;\n// let's assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); }\n// Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt);\n// to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp\n// set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU;\n// set controller type controller.set_control_type(control_type);\n// Let\u0026rsquo;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10);\n// Set params. // n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances. controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;control system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// set up variables for simulation // create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0];\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y();\nx_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x();\nm_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\ncout \u0026laquo; \u0026ldquo;Saving simulation data to disk.\\n\u0026rdquo;;\n// saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_glds_du_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\ncout \u0026laquo; \u0026ldquo;fin.\\n\u0026rdquo;; return 0; }\n_Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":14,"href":"/docs/api/examples/eg_plds_ctrl_8cpp-example/","title":"eg_plds_ctrl.cpp","section":"Examples","content":" eg_plds_ctrl.cpp # Example PLDS Control ```cpp\n//===\u0026ndash; eg_plds_ctrl.cpp - Example PLDS Control \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Control ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt);\n// Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err\n// Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt);\nsize_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi;\nVector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled\u0026hellip; lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset();\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controlled_system:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controlled_system.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system);\n// for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type);\n// set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty);\n// to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// create Matrix to save outputs in\u0026hellip; Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0;\n// Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros);\n// outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros);\n// *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros);\n// set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y();\nx_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x();\nm_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true);\n// e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":15,"href":"/docs/api/examples/eg_plds_est_8cpp-example/","title":"eg_plds_est.cpp","section":"Examples","content":" eg_plds_est.cpp # Example PLDS Estimation ```cpp\n//===\u0026ndash; eg_plds_est.cpp - Example PLDS Estimation \u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout;\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0);\nint main() { cout \u0026laquo; \u0026quot; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026quot;;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation.\n// construct ground truth system\u0026hellip; lds::poisson::System system_true(n_u, n_x, n_y, dt);\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset();\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt);\n// Can copy parameters from another system object system_estimator = system_true;\n// wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est);\n// set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true;\n// set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m);\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;estimator:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; system_estimator.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros));\n// create matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros);\n// states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros);\nMatrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros);\n// initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simlation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1));\n// Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simlation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); m_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); m_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;m_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_est.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace));\nreturn 0; }\n// for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows;\nif ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026ldquo;Q must be n x n.\u0026rdquo;); }\nMatrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); }\nreturn x; }\n_Filename: eg_plds_est.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":16,"href":"/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/","title":"eg_plds_switched_ctrl.cpp","section":"Examples","content":" eg_plds_switched_ctrl.cpp # Example Switched PLDS Control ```cpp\n//===\u0026ndash; eg_plds_switched_ctrl.cpp - Example Switched PLDS Control \u0026mdash;===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026ldquo;License\u0026rdquo;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026ldquo;AS IS\u0026rdquo; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===// //===\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;\u0026mdash;-===//\n#include using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout;\nauto main() -\u0026gt; int { cout \u0026laquo; \u0026quot; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026quot;;\n// whether to do switched control bool do_switch_ctrl = true;\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1;\n// no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt);\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt);\n// Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices. data_t scale_sys_b = 2;\nMatrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt));\ncontrolled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions\n// reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt);\n// Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b);\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system);\n// set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys1:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;sys2:\\n\u0026quot;; cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026quot;.....................................\\n\u0026quot;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x));\nswitched_controller.set_y_ref(y_ref0);\nstd::vectorlds::poisson::System systems_vec(3, lds::poisson::System()); lds::UniformSystemListlds::poisson::System systems(std::move(systems_vec));\ncout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;switched_controller:\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;; switched_controller.Print(); cout \u0026laquo; \u0026ldquo;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;\u0026hellip;.\\n\u0026rdquo;;\n// Fake measurements Matrix z(n_y, n_t, arma::fill::zeros);\n// Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros);\n// create Matrix to save outputs in\u0026hellip; Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]);\n// modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones);\n// set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x();\ncout \u0026laquo; \u0026ldquo;Starting \u0026quot; \u0026laquo; n_t * dt \u0026laquo; \u0026quot; sec simulation \u0026hellip; \\n\u0026rdquo;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } }\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); }\nauto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026laquo; \u0026ldquo;Finished simulation in \u0026quot; \u0026laquo; sim_time_ms.count() \u0026laquo; \u0026quot; ms.\\n\u0026rdquo;; cout \u0026laquo; \u0026ldquo;(app. \u0026quot; \u0026laquo; (sim_time_ms.count() / n_t) * 1e3 \u0026laquo; \u0026quot; us/time-step)\\n\u0026rdquo;;\n// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace;\nauto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;dt\u0026rdquo;)); y_ref.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_ref\u0026rdquo;, replace)); u.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;u\u0026rdquo;, replace)); z.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;z\u0026rdquo;, replace)); x_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_true\u0026rdquo;, replace)); y_true.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_true\u0026rdquo;, replace)); x_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;x_hat\u0026rdquo;, replace)); y_hat.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;y_hat\u0026rdquo;, replace)); mode.save(arma::hdf5_name(\u0026ldquo;eg_plds_switched_ctrl.h5\u0026rdquo;, \u0026ldquo;mode\u0026rdquo;, replace));\nreturn 0; }\n_Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- Updated on 5 March 2025 at 16:35:01 EST "},{"id":17,"href":"/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/","title":"examples","section":"Files","content":" examples # Files # Name examples/eg_glds_ctrl.cpp examples/eg_glds_du_plds_ctrl.cpp examples/eg_plds_ctrl.cpp examples/eg_plds_est.cpp examples/eg_plds_switched_ctrl.cpp Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":18,"href":"/docs/api/examples/","title":"Examples","section":"LDS C+E Documentation","content":" Examples # eg_glds_ctrl.cpp Example GLDS Control.\neg_glds_du_plds_ctrl.cpp Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).\neg_plds_ctrl.cpp Example PLDS Control.\neg_plds_est.cpp Example PLDS Estimation.\neg_plds_switched_ctrl.cpp Example Switched PLDS Control.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":19,"href":"/docs/api/files/eg__glds__ctrl_8cpp/","title":"examples/eg_glds_ctrl.cpp","section":"Files","content":" examples/eg_glds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_ctrl.cpp - Example GLDS Control ---------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS Control ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // set initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":20,"href":"/docs/api/files/eg__glds__du__plds__ctrl_8cpp/","title":"examples/eg_glds_du_plds_ctrl.cpp","section":"Files","content":" examples/eg_glds_du_plds_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_glds_du_plds_ctrl.cpp - Example GLDS Delta u Control of PLDS ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Gaussian LDS du Control of PLDS ********** \\n\\n\u0026#34;; // Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); // construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2.5e-2); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 0; // 1e-3; // probability of going from low to high disturb. data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_x).fill(m_low); Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_x0(x0_true); controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 50; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // process noise covariance Matrix q_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-8; // output noise covariance Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * 1e-2; lds::gaussian::System controller_system(n_u, n_x, n_y, dt); controller_system.set_A(a_true); controller_system.set_B(b_controller); controller_system.set_g(g_true); controller_system.set_m(m_controller); controller_system.set_Q(q_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-8; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } // Control variables: // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); // to design for this example, augmented state with control and made the input // du; cost on output q_y = 1, on integral output = 1e2, on u = 0, on du = // 1e-2. Matrix k_x = Matrix(n_u, n_x).fill(2.44); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(97.4); // gains on integrated err Matrix k_u = Matrix(n_u, n_u).fill(5.23e-2); // gains on input amp // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; // update change in control (LP filters control) control_type = control_type | lds::kControlTypeDeltaU; // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(10); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_Kc_u(k_u); controller.set_g_design(g_design); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;control system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // set up variables for simulation // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::ones) * y_ref0[0]; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // get initial val y_hat.submat(0, 0, n_y - 1, 0) = controller.sys().y(); y_true.submat(0, 0, n_y - 1, 0) = controlled_system.y(); x_hat.submat(0, 0, n_x - 1, 0) = controller.sys().x(); x_true.submat(0, 0, n_x - 1, 0) = controlled_system.x(); m_hat.submat(0, 0, n_x - 1, 0) = controller.sys().m(); m_true.submat(0, 0, n_x - 1, 0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // input Vector u_tm1(u.colptr(t - 1), u.n_rows, false, true); // Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); // save the signals y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;Saving simulation data to disk.\\n\u0026#34;; // saved variables: dt, lambdaHat, xHat, mHat, z, u, lambdaRef, lambdaTrue, // xTrue, mTrue saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_glds_du_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); cout \u0026lt;\u0026lt; \u0026#34;fin.\\n\u0026#34;; return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":21,"href":"/docs/api/files/eg__plds__ctrl_8cpp/","title":"examples/eg_plds_ctrl.cpp","section":"Files","content":" examples/eg_plds_ctrl.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # main # int main() Going to simulate a switching disturbance (m) acting on system\nSource code # //===-- eg_plds_ctrl.cpp - Example PLDS Control ---------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Control ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(10.0 / dt); // Control variables: _reference/target output, controller gains // n.b., Can either use Vector (arma::Col) or std::vector Vector y_ref0 = Vector(n_y, arma::fill::ones) * 30.0 * dt; Matrix k_x = Matrix(n_u, n_x, arma::fill::zeros) + 1; // gains on state error Matrix k_inty = Matrix(n_u, n_y, arma::fill::zeros) + 10; // gains on integrated output err // Set control type bit mask, so controller knows what to do size_t control_type = lds::kControlTypeIntY; // integral action // Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = 0.986; Matrix b_true(n_x, n_u, arma::fill::zeros); b_true[0] = 0.054; Vector x0_true = Vector(n_x, arma::fill::ones) * log(1 * dt); size_t which_m = 0; data_t m_low = log(1 * dt) * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; data_t m_high = log(20 * dt) * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; Vector m0_true = Vector(n_x, arma::fill::ones) * m_low; // construct ground truth system to be controlled... lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_x0(x0_true); // reset to initial conditions controlled_system.Reset(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controlled_system:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controlled_system.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Create the controller lds::poisson::Controller controller; { // Create model used for control. lds::poisson::System controller_system(controlled_system); // for this example, assume model correct, except disturbance Vector m0_controller = Vector(n_x, arma::fill::ones) * m_low; Vector x0_controller = arma::log(y_ref0); controller_system.set_m(m0_controller); controller_system.set_x0(x0_controller); controller_system.Reset(); //reset to new init condition // adaptively re-estimate process disturbance (m) controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-5; controller_system.set_Q_m(q_m); data_t u_lb = 0.0; data_t u_ub = 5.0; controller = std::move( lds::poisson::Controller(std::move(controller_system), u_lb, u_ub)); } // set controller type controller.set_control_type(control_type); // set controller gains controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); // to protect against integral windup when output is consistently above // target: data_t tau_awu(0.1); controller.set_tau_awu(tau_awu); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // create Matrix to save outputs in... Matrix y_ref = Matrix(n_y, n_t, arma::fill::zeros); y_ref.each_col() += y_ref0; // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // simulated control signal ([=] V) Matrix u(n_u, n_t, arma::fill::zeros); // outputs, states and gain/disturbance params // *_hat indicates online estimates Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_y, n_t, arma::fill::zeros); // *_true indicates ground truth (system being controlled) Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_y, n_t, arma::fill::zeros); // set initial val y_hat.col(0) = controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = controller.sys().x(); x_true.col(0) = controlled_system.x(); m_hat.col(0) = controller.sys().m(); m_true.col(0) = controlled_system.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // simulate a stochastically switched disturbance Vector chance = arma::randu\u0026lt;Vector\u0026gt;(1); if (which_m == 0) // low disturbance { if (chance[0] \u0026lt; pr_lo2hi) { // switches low -\u0026gt; high disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_high); which_m = 1; } } else { // high disturbance if (chance[0] \u0026lt; pr_hi2lo) { // swithces high -\u0026gt; low disturbance m0_true = std::vector\u0026lt;data_t\u0026gt;(n_x, m_low); which_m = 0; } } controlled_system.set_m(m0_true); // e.g., use sinusoidal reference data_t f = 0.5; // freq [=] Hz Vector t_vec = Vector(n_y, arma::fill::ones) * t; y_ref.col(t) += y_ref0 % arma::sin(f * 2 * lds::kPi * dt * t_vec - lds::kPi / 4); // Simulate the true system. z.col(t)=controlled_system.Simulate(u.col(t-1)); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Notably, it does this in the // log-linear space (i.e., log(y)). // // Therefore, it is only applicable to regulation problems or cases where // reference trajectory changes slowly compared to system dynamics. controller.set_y_ref(y_ref.col(t)); u.col(t)=controller.ControlOutputReference(z.col(t)); y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); m_true.col(t) = controlled_system.m(); y_hat.col(t) = controller.sys().y(); x_hat.col(t) = controller.sys().x(); m_hat.col(t) = controller.sys().m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":22,"href":"/docs/api/files/eg__plds__est_8cpp/","title":"examples/eg_plds_est.cpp","section":"Files","content":" examples/eg_plds_est.cpp # Types # Name using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector using double data_t Functions # Name Matrix random_walk(size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0) int main() Type Details # Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nFunction Details # random_walk # Matrix random_walk( size_t n_t, const Matrix \u0026amp; Q, const Vector \u0026amp; x0 ) main # int main() Source code # //===-- eg_plds_est.cpp - Example PLDS Estimation -------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0); int main() { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Poisson LDS Estimation ********** \\n\\n\u0026#34;; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. // construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); // Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state // Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); // Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. // turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;estimator:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; system_estimator.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Set up simulation : // Simulated measurements Matrix z(n_y, n_t, arma::fill::zeros); // Stimulus (generate random stimulus) Matrix q_u = Matrix(n_u, n_u, arma::fill::eye) * 1e-3; // cov of random walk Matrix u = random_walk(n_t, q_u, Vector(n_u, arma::fill::zeros)); // create matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); // states and disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix m_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix m_true(n_x, n_t, arma::fill::zeros); // initial conditions y_hat.col(0) = system_estimator.y(); y_true.col(0) = system_true.y(); x_hat.col(0) = system_estimator.x(); x_true.col(0) = system_true.x(); m_hat.col(0) = system_estimator.m(); m_true.col(0) = system_true.m(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simlation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); // save signals y_hat.col(t) = system_estimator.y(); y_true.col(t) = system_true.y(); x_true.col(t) = system_true.x(); m_true.col(t) = system_true.m(); x_hat.col(t) = system_estimator.x(); m_hat.col(t) = system_estimator.m(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simlation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;dt\u0026#34;)); u.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); m_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); m_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;m_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_est.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); return 0; } // for generating random input Matrix random_walk(size_t n_t, const Matrix\u0026amp; Q, const Vector\u0026amp; x0) { size_t n = Q.n_rows; if ((n != Q.n_cols) || (Q.n_cols != Q.n_rows)) { throw std::logic_error(\u0026#34;Q must be `n` x `n`.\u0026#34;); } Matrix x(n, n_t, arma::fill::zeros); x.col(0) = x0; for (size_t t = 1; t \u0026lt; n_t; t++) { x.col(t) = x.col(t - 1) + arma::mvnrnd(Vector(n, arma::fill::zeros), Q); } return x; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":23,"href":"/docs/api/files/eg__plds__switched__ctrl_8cpp/","title":"examples/eg_plds_switched_ctrl.cpp","section":"Files","content":" examples/eg_plds_switched_ctrl.cpp # Types # Name using double data_t using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Col\u0026lt; data_t \u0026gt; Vector Functions # Name int main() Type Details # data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nMatrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Vector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Function Details # main # int main() Source code # //===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst\u0026gt; using lds::data_t; using lds::Matrix; using lds::Vector; using std::cout; auto main() -\u0026gt; int { cout \u0026lt;\u0026lt; \u0026#34; ********** Example Switched Poisson LDS Control ********** \\n\\n\u0026#34;; // whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); // for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 // simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions // reference Vector y_ref0 = Vector(n_y, arma::fill::zeros).fill(25.0 * dt); // Let underlying system 1 be more sensitive than system 2 Matrix b2 = Matrix(n_x, n_u).fill(b1[0] / scale_sys_b); // create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys1:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys1.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;sys2:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; sys2.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying system s: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } // Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); std::vector\u0026lt;lds::poisson::System\u0026gt; systems_vec(3, lds::poisson::System()); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems(std::move(systems_vec)); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;switched_controller:\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; switched_controller.Print(); cout \u0026lt;\u0026lt; \u0026#34;.....................................\\n\u0026#34;; // Fake measurements Matrix z(n_y, n_t, arma::fill::zeros); // Will later contain control. Matrix u(n_u, n_t, arma::fill::zeros); // create Matrix to save outputs in... Matrix y_hat(n_y, n_t, arma::fill::zeros); Matrix y_true(n_y, n_t, arma::fill::zeros); Matrix y_ref = Matrix(n_y, n_t).fill(y_ref0[0]); // modes and gain/disturbance params Matrix x_hat(n_x, n_t, arma::fill::zeros); Matrix x_true(n_x, n_t, arma::fill::zeros); Matrix mode(1, n_t, arma::fill::ones); // set initial val y_hat.col(0) = switched_controller.sys().y(); y_true.col(0) = controlled_system.y(); x_hat.col(0) = switched_controller.sys().x(); x_true.col(0) = controlled_system.x(); cout \u0026lt;\u0026lt; \u0026#34;Starting \u0026#34; \u0026lt;\u0026lt; n_t * dt \u0026lt;\u0026lt; \u0026#34; sec simulation ... \\n\u0026#34;; auto start = std::chrono::high_resolution_clock::now(); for (size_t t = 1; t \u0026lt; n_t; t++) { // Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); mode.col(t) = which_mode; y_ref.col(t) = y_ref0; y_true.col(t) = controlled_system.y(); x_true.col(t) = controlled_system.x(); y_hat.col(t) = switched_controller.sys().y(); x_hat.col(t) = switched_controller.sys().x(); } auto finish = std::chrono::high_resolution_clock::now(); std::chrono::duration\u0026lt;data_t, std::milli\u0026gt; sim_time_ms = finish - start; cout \u0026lt;\u0026lt; \u0026#34;Finished simulation in \u0026#34; \u0026lt;\u0026lt; sim_time_ms.count() \u0026lt;\u0026lt; \u0026#34; ms.\\n\u0026#34;; cout \u0026lt;\u0026lt; \u0026#34;(app. \u0026#34; \u0026lt;\u0026lt; (sim_time_ms.count() / n_t) * 1e3 \u0026lt;\u0026lt; \u0026#34; us/time-step)\\n\u0026#34;; // saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true, // x_true, m_true saving with hdf5 via armadillo arma::hdf5_opts::opts replace = arma::hdf5_opts::replace; auto dt_vec = Vector(1).fill(dt); dt_vec.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;dt\u0026#34;)); y_ref.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_ref\u0026#34;, replace)); u.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;u\u0026#34;, replace)); z.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;z\u0026#34;, replace)); x_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_true\u0026#34;, replace)); y_true.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_true\u0026#34;, replace)); x_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;x_hat\u0026#34;, replace)); y_hat.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;y_hat\u0026#34;, replace)); mode.save(arma::hdf5_name(\u0026#34;eg_plds_switched_ctrl.h5\u0026#34;, \u0026#34;mode\u0026#34;, replace)); return 0; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":24,"href":"/docs/api/files/","title":"Files","section":"LDS C+E Documentation","content":" Files # examples/eg_glds_ctrl.cpp\nexamples/eg_glds_du_plds_ctrl.cpp\nexamples/eg_plds_ctrl.cpp\nexamples/eg_plds_est.cpp\nexamples/eg_plds_switched_ctrl.cpp\nldsCtrlEst_h/lds.h lds namespace\nldsCtrlEst_h/lds_ctrl.h Controller.\nldsCtrlEst_h/lds_fit.h LDS base fit type.\nldsCtrlEst_h/lds_fit_em.h subspace identification\nldsCtrlEst_h/lds_fit_ssid.h subspace identification\nldsCtrlEst_h/lds_gaussian.h glds namespace\nldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller.\nldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type.\nldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type.\nldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type.\nldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type.\nldsCtrlEst_h/lds_gaussian_sys.h GLDS base type.\nldsCtrlEst_h/lds_poisson.h plds namespace\nldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type.\nldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type.\nldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type.\nldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type.\nldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type.\nldsCtrlEst_h/lds_poisson_sys.h PLDS base type.\nldsCtrlEst_h/lds_sctrl.h SwitchedController type.\nldsCtrlEst_h/lds_sys.h LDS base type.\nldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices.\nldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems.\nldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors.\nldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API)\nldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API)\nsrc/lds.cpp misc lds namespace functions\nsrc/lds_gaussian_sys.cpp GLDS base type.\nsrc/lds_poisson_sys.cpp PLDS base type.\nsrc/lds_sys.cpp LDS base type.\nsrc/lds_uniform_vecs.cpp Uniformly sized vectors.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":25,"href":"/docs/tutorials/eg_glds_control/","title":"GLDS Control","section":"LDS C+E Examples","content":" GLDS Control Tutorial # This tutorial shows how to use this library to control a system with a Gaussian LDS controller (lds::gaussian::Controller). In place of a physical system, a GLDS model (lds::gaussian::System) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 5 seconds.\n// Make 1st-order SISO system, sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(5.0 / dt); When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system to be controlled... // initializes to random walk model with top-most n_y state observed lds::gaussian::System controlled_system(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[\rx_{t\u0026#43;1} = x_t \u0026#43; w_t\r\\] \\[\ry_{t} = x_t\r\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Ground-truth parameters for the controlled system // (stand-in for physical system to be controlled) Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.01); Matrix b_true = Matrix(n_x, n_u).fill(2e-4); // control signal to model input unit conversion e.g., V -\u0026gt; mW/mm2: Vector g_true = Vector(n_y).fill(10.0); // output noise covariance Matrix r_true = Matrix(n_y, n_y, arma::fill::eye) * 1e-4; As mentioned above, this example will feature a stochastic disturbance. More specifically, a process disturbance will randomly change between two values.\n/// Going to simulate a switching disturbance (m) acting on system size_t which_m = 0; // whether low or high disturbance (0, 1) data_t m_low = 5 * dt * (1 - a_true[0]); data_t pr_lo2hi = 1e-3; // probability of going from low to high disturb. data_t m_high = 20 * dt * (1 - a_true[0]); data_t pr_hi2lo = pr_lo2hi; // initially let m be low Vector m0_true = Vector(n_y).fill(m_low); Finally, assign the parameters using corresponding set-methods.\n// Assign params. controlled_system.set_A(a_true); controlled_system.set_B(b_true); controlled_system.set_m(m0_true); controlled_system.set_g(g_true); controlled_system.set_R(r_true); Creating the controller # Now, create the controller. This requires first constructing the system model that the control uses for estimating state feedback and updating the control signal. A controller is then constructed from this lds::gaussian::System object and upper/lower bounds on the control signal (u_lb, u_ub below), past which the control saturates. Here, the control signal is command voltage sent to an analog driver (e.g., for an LED). Its limits are 0 to 5 V. If your actuator does not saturate somehow, simply set the lower and upper bounds to -lds::kInf and lds::kInf, respectively. Simple saturation is currently the only actuator model in this library.\nFor the sake of this simulation, the system model input matrix is set to an incorrect value. We also assume that the controller feedback gains were designed with an actuator whose conversion factor from volts to physical units (e.g., mW/mm2 optical intensity) differed from the actuator being used in the current experiment.\n// make a controller lds::gaussian::Controller controller; { // Create **incorrect** model used for control. // (e.g., imperfect model fitting) Matrix b_controller = b_true / 2; // let\u0026#39;s assume zero process disturbance initially // (will be re-estimating) Vector m_controller = Vector(n_x, arma::fill::zeros); // for this demo, just use arbitrary default R Matrix r_controller = Matrix(n_y, n_y, arma::fill::eye) * lds::kDefaultR0; lds::gaussian::System controller_system(controlled_system); controller_system.set_B(b_controller); controller_system.set_m(m_controller); controller_system.set_R(r_controller); controller_system.Reset(); // reset to new m // going to adaptively re-estimate the disturbance controller_system.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise // acting on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; controller_system.set_Q_m(q_m); // create controller // lower and upper bounds on control signal (e.g., in Volts) data_t u_lb = 0.0; // [=] V data_t u_ub = 5.0; // [=] V controller = std::move( lds::gaussian::Controller(std::move(controller_system), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nWith the controller constructed, control variables may be set.\n// Control variables: // if following enabled, adapts set point with re-estimated process // disturbance n.b., should not need integral action if this is enabled as the // adaptive estimator minimizes DC error bool do_adaptive_set_point = false; // Reference/target output, controller gains Vector y_ref0 = Vector(n_y).fill(20.0 * dt); Matrix k_x = Matrix(n_u, n_x).fill(100); // gains on state error Matrix k_inty = Matrix(n_u, n_y).fill(1e3); // gains on integrated err // setting initial state to target to avoid error at onset: Vector x0 = Vector(n_x).fill(y_ref0[0]); // set up controller type bit mask so controller knows how to proceed size_t control_type = 0; if (do_adaptive_set_point) { // adapt set point with estimated disturbance control_type = control_type | lds::kControlTypeAdaptM; } else { // use integral action to minimize DC error control_type = control_type | lds::kControlTypeIntY; } // set controller type controller.set_control_type(control_type); // Let\u0026#39;s say these controller gains were designed assuming g was 9 V/(mW/mm2): Vector g_design = Vector(n_u).fill(9); // Set params. // **n.b. using arbitrary defaults for Q, R in this example. Really, these // should be set by users, as they tune characteristics of Kalman filter. // Users can also choose not to recursively calculate the estimator gain and // supply it (setKe) instead of covariances.** controller.set_y_ref(y_ref0); controller.set_Kc(k_x); controller.set_Kc_inty(k_inty); controller.set_g_design(g_design); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output and supply the current measurement z. It then calculates the solution for the state/input required to track the reference output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system.\nThe control loop is carried out here in a simple for-loop, where a the controlled system is simulated, a measurement taken, and the control signal updated.\n// Simulate the true system. z.col(t) = controlled_system.Simulate(u_tm1); // This method uses a steady-state solution to control problem to calculate // x_ref, u_ref from reference output y_ref. Therefore, it is only // applicable to regulation problems or cases where reference trajectory // changes slowly compared to system dynamics. u.col(t) = controller.ControlOutputReference(z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the control signal. The controller\u0026rsquo;s online estimates of the output, state, and disturbance are given in purple.\n"},{"id":26,"href":"/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/","title":"include","section":"Files","content":" include # Directories # Name ldsCtrlEst_h Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":27,"href":"/docs/api/namespaces/namespacelds/","title":"lds","section":"Namespaces","content":" lds # Linear Dynamical Systems (LDS) namespace. Namespaces # Name lds::gaussian Linear Dynamical Systems with Gaussian observations. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::Controller class lds::EM class lds::Fit LDS Fit Type. class lds::SSID class lds::SwitchedController SwitchedController Type. class lds::System Linear Dynamical System Type. class lds::UniformMatrixList class lds::UniformSystemList class lds::UniformVectorList Types # Name enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA}\nweighting options for SSID enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2} using double data_t using arma::Col\u0026lt; data_t \u0026gt; Vector using arma::Mat\u0026lt; data_t \u0026gt; Matrix using arma::Cube\u0026lt; data_t \u0026gt; Cube using arma::subview\u0026lt; data_t \u0026gt; View Functions # Name void Limit(std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub) void Limit(Vector \u0026amp; x, data_t lb, data_t ub) void Limit(Matrix \u0026amp; x, data_t lb, data_t ub) void Reassign(Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Vector in place void Reassign(Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026ldquo;Reassign\u0026rdquo;)\nreassigns contents of some Matrix in place void ForceSymPD(Matrix \u0026amp; X)\nforces matrix to be symmetric positive-definite void ForceSymMinEig(Matrix \u0026amp; X, data_t eig_min =0)\nforces matrix to be symmetric and have a minimum eigenvalue void lq(Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X)\nLQ decomposition. Matrix calcCov(const Matrix \u0026amp; A, const Matrix \u0026amp; B)\nCalculate covariance matrix. Attributes # Name const data_t kInf Some useful numbers. const data_t kPi Type Details # SSIDWt # Enumerator Value Description kSSIDNone None. kSSIDMOESP MOESP (AKA \u0026ldquo;robust method\u0026rdquo; in van Overschee 1996) kSSIDCVA CVA \u0026ldquo;Canonical Variate Analysis\u0026rdquo;. Weighting options for singular value decomposition performed during subspace identification (SSID)\nReference:\nvan Overschee, de Moor. 1996. Subspace Identification for Linear Systems.\nMatrixListFreeDim # Enumerator Value Description kMatFreeDimNone neither dim free to be hetero in mat list kMatFreeDim1 allow 1st dim of mats in list to be hetero kMatFreeDim2 allow 2nd dim of mats in list to be hetero data_t # using lds::data_t = double; Type of all data in library. If need 32b, change double to float. This could be potentially useful for large scale problems where there are memory constraints.\nVector # using lds::Vector = arma::Col\u0026lt;data_t\u0026gt;; Matrix # using lds::Matrix = arma::Mat\u0026lt;data_t\u0026gt;; Cube # using lds::Cube = arma::Cube\u0026lt;data_t\u0026gt;; View # using lds::View = arma::subview\u0026lt;data_t\u0026gt;; Function Details # Limit # inline void Limit( std::vector\u0026lt; data_t \u0026gt; \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Vector \u0026amp; x, data_t lb, data_t ub ) Limit # inline void Limit( Matrix \u0026amp; x, data_t lb, data_t ub ) Reassign # inline void Reassign( Vector \u0026amp; some, const Vector \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Vector other other Vector parenthetical optional description provided by caller to ease debugging Reassign # inline void Reassign( Matrix \u0026amp; some, const Matrix \u0026amp; other, const std::string \u0026amp; parenthetical =\u0026#34;Reassign\u0026#34; ) Parameters:\nsome some Matrix other other Matrix parenthetical optional description provided by caller to ease debugging ForceSymPD # void ForceSymPD( Matrix \u0026amp; X ) Parameters:\nX mutated matrix ForceSymMinEig # void ForceSymMinEig( Matrix \u0026amp; X, data_t eig_min =0 ) Parameters:\nX mutated matrix eig_min [optional] minimum eigen value lq # void lq( Matrix \u0026amp; L, Matrix \u0026amp; Qt, const Matrix \u0026amp; X ) Parameters:\nL lower triangle matrix Qt orthonormal matrix (transposed cf QR decomp) X matrix being decomposed calcCov # Matrix calcCov( const Matrix \u0026amp; A, const Matrix \u0026amp; B ) Parameters:\nA some matrix B some other matrix Return: covariance\nAttribute Details # kInf # static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); kPi # static const data_t kPi = arma::datum::pi; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":28,"href":"/docs/api/classes/classlds_1_1_controller/","title":"lds::Controller","section":"Classes","content":" lds::Controller # More\u0026hellip;\nInherited by lds::SwitchedController\u0026lt; System \u0026gt;, lds::gaussian::Controller, lds::poisson::Controller\nPublic Functions # Name Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes # Name System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::Controller; Public Function Details # Controller # Controller() =default Controller # inline Controller( const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Controller # inline Controller( System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsys System (derived from lds::System) u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Template Parameters:\nSystem type derived from lds::System Control # inline const Vector \u0026amp; Control( const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step). This is the most flexible option, but requires user to have set the controller\u0026rsquo;s y_ref, x_ref, and u_ref variables.\nControlOutputReference # inline const Vector \u0026amp; ControlOutputReference( const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true ) Parameters:\nz measurement do_control [optional] whether to update control (true) or simply feed through u_ref (false) do_estimation [optional] whether to update state estimate (if false, effectively open-loop control) do_lock_control [optional] whether to lock control at its current value sigma_soft_start [optional] standard deviation (sigma) of a Gaussian soft-start to control (do_control from false to true) sigma_u_noise [optional] standard deviation (sigma) of Gaussian noise added on top of control signal do_reset_at_control_onset [optional] whether to reset controller at control epoch onset (i.e., do_control from false to true) Return: updated control signal\nUpdates the control signal (single-step), given previously-set y_ref. This method calculates the rest of the set point (u_ref, x_ref) that is required to for the system to be at y_ref at steady state. This is accomplished by linearly-constrained least-squares. For a single-output system, the solution should be exact within control saturation limits. For a multi-output system, it provides the least-squares comprimise across the outputs.\nsys # inline const System \u0026amp; sys() const Kc # inline const Matrix \u0026amp; Kc() const Kc_inty # inline const Matrix \u0026amp; Kc_inty() const Kc_u # inline const Matrix \u0026amp; Kc_u() const g_design # inline const Vector \u0026amp; g_design() const u_ref # inline const Vector \u0026amp; u_ref() const x_ref # inline const Vector \u0026amp; x_ref() const y_ref # inline const Vector \u0026amp; y_ref() const control_type # inline size_t control_type() const tau_awu # inline data_t tau_awu() const u_lb # inline data_t u_lb() const u_ub # inline data_t u_ub() const set_sys # inline void set_sys( const System \u0026amp; sys ) set_g_design # inline void set_g_design( const Vector \u0026amp; g_design ) set_u_ref # inline void set_u_ref( const Vector \u0026amp; u_ref ) set_x_ref # inline void set_x_ref( const Vector \u0026amp; x_ref ) set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) Reimplemented by: lds::gaussian::Controller::set_y_ref, lds::gaussian::SwitchedController::set_y_ref, lds::poisson::Controller::set_y_ref, lds::poisson::SwitchedController::set_y_ref\nset_Kc # inline void set_Kc( const Matrix \u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const Matrix \u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const Matrix \u0026amp; Kc_u ) set_tau_awu # inline void set_tau_awu( data_t tau ) set_control_type # inline void set_control_type( size_t control_type ) Parameters:\ncontrol_type control type bit mask Template Parameters:\nSystem type derived from lds::System set_u_lb # inline void set_u_lb( data_t u_lb ) Parameters:\nu_lb control lower bound set_u_ub # inline void set_u_ub( data_t u_ub ) Parameters:\nu_ub control upper bound Reset # inline void Reset() Print # inline void Print() Protected Attribute Details # sys_ # System sys_; u_ # Vector u_; u_return_ # Vector u_return_; g_design_ # Vector g_design_; u_ref_ # Vector u_ref_; u_ref_prev_ # Vector u_ref_prev_; x_ref_ # Vector x_ref_; y_ref_ # Vector y_ref_; cx_ref_ # Vector cx_ref_; Kc_ # Matrix Kc_; Kc_u_ # Matrix Kc_u_; Kc_inty_ # Matrix Kc_inty_; du_ref_ # Vector du_ref_; dv_ref_ # Vector dv_ref_; v_ref_ # Vector v_ref_; dv_ # Vector dv_; v_ # Vector v_; int_e_ # Vector int_e_; int_e_awu_adjust_ # Vector int_e_awu_adjust_; u_sat_ # Vector u_sat_; do_control_prev_ # bool do_control_prev_ = false; do_lock_control_prev_ # bool do_lock_control_prev_ = false; u_saturated_ # bool u_saturated_ = false; u_lb_ # data_t u_lb_ {}; u_ub_ # data_t u_ub_ {}; tau_awu_ # data_t tau_awu_ {}; k_awu_ # data_t k_awu_ = 0; t_since_control_onset_ # data_t t_since_control_onset_ = 0; control_type_ # size_t control_type_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":29,"href":"/docs/api/classes/classlds_1_1_e_m/","title":"lds::EM","section":"Classes","content":" lds::EM # More\u0026hellip;\nInherited by lds::gaussian::FitEM, lds::poisson::FitEM\nPublic Functions # Name EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions # Name void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() virtual void MaximizeOutput() =0 virtual void MaximizeMeasurement() =0 void Smooth(bool force_common_initial)\nget smoothed estimates virtual void RecurseKe(Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t) =0\nrecursively update estimator gain Ke void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::EM; Public Function Details # EM # EM() =default EM # EM( size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nn_x number of states dt sample period u_train input training data z_train measurement training data EM # EM( const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train ) Parameters:\nfit0 initial fit u_train input training data z_train measurement training data ~EM # virtual ~EM() =default Run # const Fit \u0026amp; Run( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2 ) Parameters:\ncalc_dynamics [optional] whether to calculate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law max_iter max number of iterations tol convergence tolerance (max fractional abs change) Return: Fit\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nx # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const y # inline const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const sum_E_x_t_x_t # inline const Matrix \u0026amp; sum_E_x_t_x_t() const sum_E_xu_tm1_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const sum_E_xu_t_xu_tm1 # inline const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const n_t_tot # inline size_t n_t_tot() theta # inline const Vector \u0026amp; theta() const Protected Function Details # Expectation # void Expectation( bool force_common_initial =false ) Parameters:\nforce_common_initial whether to force common initial condition for all trials Maximization # void Maximization( bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false ) Parameters:\ncalc_dynamics [optional] whether to caclulate dynamics (A, B) calc_Q [optional] whether to calculate process noise covariance calc_init [optional] whether to calculate initial conditions calc_output [optional] whether to calculate output function calc_measurement [optional] whether to calculate parameters for measurement/observation law MaximizeDynamics # void MaximizeDynamics() MaximizeQ # void MaximizeQ() MaximizeInitial # void MaximizeInitial() MaximizeOutput # virtual void MaximizeOutput() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeOutput, lds::poisson::FitEM::MaximizeOutput\nMaximizeMeasurement # virtual void MaximizeMeasurement() =0 Reimplemented by: lds::gaussian::FitEM::MaximizeMeasurement, lds::poisson::FitEM::MaximizeMeasurement\nSmooth # void Smooth( bool force_common_initial ) Parameters:\nforce_common_initial whether to force common initial conditions RecurseKe # virtual void RecurseKe( Matrix \u0026amp; Ke, Cube \u0026amp; P_pre, Cube \u0026amp; P_post, size_t t ) =0 Parameters:\nKe estimator gain P_pre cov of predicted state est. P_post cov of postior sate est. t time Reimplemented by: lds::gaussian::FitEM::RecurseKe, lds::poisson::FitEM::RecurseKe\nReset # void Reset() InitVars # void InitVars() UpdateTheta # Vector UpdateTheta() Return: parameter list\nProtected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; x_ # std::vector\u0026lt; Matrix \u0026gt; x_; P_ # std::vector\u0026lt; Cube \u0026gt; P_; P_t_tm1_ # std::vector\u0026lt; Cube \u0026gt; P_t_tm1_; y_ # std::vector\u0026lt; Matrix \u0026gt; y_; diag_y_ # Matrix diag_y_; sum_E_x_t_x_t_ # Matrix sum_E_x_t_x_t_; sum_E_xu_tm1_xu_tm1_ # Matrix sum_E_xu_tm1_xu_tm1_; sum_E_xu_t_xu_tm1_ # Matrix sum_E_xu_t_xu_tm1_; fit_ # Fit fit_; theta_ # Vector theta_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":30,"href":"/docs/api/classes/classlds_1_1_fit/","title":"lds::Fit","section":"Classes","content":" lds::Fit # LDS Fit Type. #include \u0026lt;lds_fit.h\u0026gt;\nInherited by lds::gaussian::Fit, lds::poisson::Fit\nPublic Functions # Name Fit() =default\nConstructs a new Fit. Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias virtual const Matrix \u0026amp; R() const =0 void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance virtual void set_R(const Matrix \u0026amp; R) =0\nsets output noise covariance (if any) void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) =0\noutput function Protected Attributes # Name data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period ~Fit # virtual ~Fit() =default n_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const m # inline const Vector \u0026amp; m() const Q # inline const Matrix \u0026amp; Q() const x0 # inline const Vector \u0026amp; x0() const P0 # inline const Matrix \u0026amp; P0() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const R # virtual const Matrix \u0026amp; R() const =0 Reimplemented by: lds::gaussian::Fit::R, lds::poisson::Fit::R\nset_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_g # inline void set_g( const Vector \u0026amp; g ) set_m # inline void set_m( const Vector \u0026amp; m ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # virtual void set_R( const Matrix \u0026amp; R ) =0 Reimplemented by: lds::gaussian::Fit::set_R, lds::poisson::Fit::set_R\nset_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) f # inline View f( Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t ) Parameters:\nx state estimate (over time) u input (over time) t time index Return: view of updated state\nf # inline View f( Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t ) Parameters:\nx_pre predicted state est. x_post posterior state est. u input (over time) t time index Return: view of predicted state\nh # virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) =0 Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplemented by: lds::gaussian::Fit::h, lds::poisson::Fit::h\nProtected Attribute Details # dt_ # data_t dt_ {}; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; m_ # Vector m_; Q_ # Matrix Q_; C_ # Matrix C_; d_ # Vector d_; R_ # Matrix R_; x0_ # Vector x0_; P0_ # Matrix P0_; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":31,"href":"/docs/api/namespaces/namespacelds_1_1gaussian/","title":"lds::gaussian","section":"Namespaces","content":" lds::gaussian # Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. class lds::gaussian::Fit GLDS Fit Type. class lds::gaussian::FitEM GLDS E-M Fit Type. class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. class lds::gaussian::System Gaussian LDS Type. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":32,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_controller/","title":"lds::gaussian::Controller","section":"Classes","content":" lds::gaussian::Controller # Gaussian-observation Controller Type. #include \u0026lt;lds_gaussian_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":33,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit/","title":"lds::gaussian::Fit","section":"Classes","content":" lds::gaussian::Fit # GLDS Fit Type. #include \u0026lt;lds_gaussian_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual const Matrix \u0026amp; R() const override\ngets measurement noise covariance virtual void set_R(const Matrix \u0026amp; R) override\nsets measurement noise covariance virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period R # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nh # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":34,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/","title":"lds::gaussian::FitEM","section":"Classes","content":" lds::gaussian::FitEM # GLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_gaussian_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::gaussian::FitEM; This type is used in the process of fitting GLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":35,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/","title":"lds::gaussian::FitSSID","section":"Classes","content":" lds::gaussian::FitSSID # Subspace Identification (SSID) for GLDS. #include \u0026lt;lds_gaussian_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":36,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/","title":"lds::gaussian::SwitchedController","section":"Classes","content":" lds::gaussian::SwitchedController # Gaussian-observation SwitchedController Type. #include \u0026lt;lds_gaussian_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nsets reference output Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":37,"href":"/docs/api/classes/classlds_1_1gaussian_1_1_system/","title":"lds::gaussian::System","section":"Classes","content":" lds::gaussian::System # Gaussian LDS Type. #include \u0026lt;lds_gaussian_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0)\nConstructs a new Gaussian System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. const Matrix \u0026amp; R() const\nGet output noise covariance. void set_Q(const Matrix \u0026amp; Q) void set_R(const Matrix \u0026amp; R)\nSet output noise covariance. void set_Ke(const Matrix \u0026amp; Ke)\nSet estimator gain. void set_Ke_m(const Matrix \u0026amp; Ke_m)\nSet disturbance estimator gain. void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively update estimator gain. Protected Attributes # Name Matrix R_ covariance of output noise bool do_recurse_Ke_ whether to recursively calculate estimator gain Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0, data_t r0 =kDefaultR0 ) Parameters:\nn_u number of inputs (u) n_x number of states (x) n_y number of outputs (y) dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) r0 [optional] initial diagonal elements of output noise covariance (R) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nR # inline const Matrix \u0026amp; R() const set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_R # inline void set_R( const Matrix \u0026amp; R ) set_Ke # inline void set_Ke( const Matrix \u0026amp; Ke ) set_Ke_m # inline void set_Ke_m( const Matrix \u0026amp; Ke_m ) Print # void Print() Protected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nProtected Attribute Details # R_ # Matrix R_; do_recurse_Ke_ # bool do_recurse_Ke_ {}; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":38,"href":"/docs/api/namespaces/namespacelds_1_1poisson/","title":"lds::poisson","section":"Namespaces","content":" lds::poisson # Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. class lds::poisson::Fit PLDS Fit Type. class lds::poisson::FitEM PLDS E-M Fit Type. class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. class lds::poisson::System Poisson System type. Attributes # Name std::random_device rd random device for simulating poisson data std::mt19937 rng random number generator for simulating poisson data Attribute Details # rd # static std::random_device rd; rng # static std::mt19937 rng = std::mt19937( rd()); Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":39,"href":"/docs/api/classes/classlds_1_1poisson_1_1_controller/","title":"lds::poisson::Controller","section":"Classes","content":" lds::poisson::Controller # PLDS Controller Type. #include \u0026lt;lds_poisson_ctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":40,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit/","title":"lds::poisson::Fit","section":"Classes","content":" lds::poisson::Fit # PLDS Fit Type. #include \u0026lt;lds_poisson_fit.h\u0026gt;\nInherits from lds::Fit\nPublic Functions # Name Fit() =default Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt)\nConstructs a new Fit. virtual View h(Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t) override\noutput function virtual void set_R(const Matrix \u0026amp; R) override\nsets output noise covariance (if any) virtual const Matrix \u0026amp; R() const override Additional inherited members # Public Functions inherited from lds::Fit\nName virtual ~Fit() =default size_t n_u() const\ngets number of inputs size_t n_x() const\ngets number of states size_t n_y() const\ngets number of outputs data_t dt() const\ngets sample period const Matrix \u0026amp; A() const\ngets state matrix const Matrix \u0026amp; B() const\ngets input matrix const Vector \u0026amp; g() const\ngets input gain const Vector \u0026amp; m() const\ngets process disturbance const Matrix \u0026amp; Q() const\ngets process noise covariance const Vector \u0026amp; x0() const\ngets initial state estimate const Matrix \u0026amp; P0() const\ngets covariance of initial state estimate const Matrix \u0026amp; C() const\ngets output matrix const Vector \u0026amp; d() const\ngets output bias void set_A(const Matrix \u0026amp; A)\nsets state matrix void set_B(const Matrix \u0026amp; B)\nsets input matrix void set_g(const Vector \u0026amp; g)\nsets input gain/conversion factor void set_m(const Vector \u0026amp; m)\nsets process disturbance void set_Q(const Matrix \u0026amp; Q)\nsets process noise covariance void set_x0(const Vector \u0026amp; x0)\nsets initial state estimate void set_P0(const Matrix \u0026amp; P0)\nsets initial state estimate covariance void set_C(const Matrix \u0026amp; C)\nsets output matrix void set_d(const Vector \u0026amp; d)\nsets output bias View f(Matrix \u0026amp; x, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function View f(Matrix \u0026amp; x_pre, const Matrix \u0026amp; x_post, const Matrix \u0026amp; u, size_t t)\nsystem dynamics function Protected Attributes inherited from lds::Fit\nName data_t dt_ sample period Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Vector m_ process noise mean Matrix Q_ process noise cov Matrix C_ output matrix Vector d_ output bias Matrix R_ measurement noise Vector x0_ initial state Matrix P0_ initial covar size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs Public Function Details # Fit # Fit() =default Fit # inline Fit( size_t n_u, size_t n_x, size_t n_y, data_t dt ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period h # inline virtual View h( Matrix \u0026amp; y, const Matrix \u0026amp; x, size_t t ) override Parameters:\ny output estimate (over time) x state estimate (over time) t time index Return: output\nReimplements: lds::Fit::h\nset_R # inline virtual void set_R( const Matrix \u0026amp; R ) override Reimplements: lds::Fit::set_R\nR # inline virtual const Matrix \u0026amp; R() const override Reimplements: lds::Fit::R\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":41,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/","title":"lds::poisson::FitEM","section":"Classes","content":" lds::poisson::FitEM # PLDS E-M Fit Type. More\u0026hellip;\n#include \u0026lt;lds_poisson_fit_em.h\u0026gt;\nInherits from lds::EM\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName EM() =default\nConstructs a new EMFit type. EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. EM(const Fit \u0026amp; fit0, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train)\nConstructs a new EMFit type. virtual ~EM() =default const Fit \u0026amp; Run(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, data_t tol =1e-2)\nRuns fitting by Expectation(E)-Maximization(M) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the input/output data to caller. const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; x() const\ngets estimated state (over time) const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; y() const\ngets estimated output (over time) const Matrix \u0026amp; sum_E_x_t_x_t() const\ngets state-input covariance const Matrix \u0026amp; sum_E_xu_tm1_xu_tm1() const\ngets state-input covariance (t-minus-1) const Matrix \u0026amp; sum_E_xu_t_xu_tm1() const\ngets single lag state-input covariance size_t n_t_tot()\ntotal number of time samples const Vector \u0026amp; theta() const\ngets parameters updated in M step Protected Functions inherited from lds::EM\u0026lt; Fit \u0026gt;\nName void Expectation(bool force_common_initial =false)\nExpectation step. void Maximization(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)\nMaximization step. void MaximizeDynamics() void MaximizeQ() void MaximizeInitial() void Smooth(bool force_common_initial)\nget smoothed estimates void Reset()\nreset to initial conditions void InitVars()\nInitializes the variables. Vector UpdateTheta()\nupdates parameter list, theta Protected Attributes inherited from lds::EM\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data std::vector\u0026lt; Matrix \u0026gt; x_ state estimate std::vector\u0026lt; Cube \u0026gt; P_ state estimate cov std::vector\u0026lt; Cube \u0026gt; P_t_tm1_ single-lag state covariance std::vector\u0026lt; Matrix \u0026gt; y_ output estimate Matrix diag_y_ Matrix sum_E_x_t_x_t_ state covariance (current time) Matrix sum_E_xu_tm1_xu_tm1_ state-input covariance (t-minus-1) Matrix sum_E_xu_t_xu_tm1_ single lag state-input covariance Fit fit_ Vector theta_ data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Detailed Description # class lds::poisson::FitEM; This type is used in the process of fitting PLDS models by expectation-maximization (EM). Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":42,"href":"/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/","title":"lds::poisson::FitSSID","section":"Classes","content":" lds::poisson::FitSSID # Subspace Identification (SSID) for PLDS. #include \u0026lt;lds_poisson_fit_ssid.h\u0026gt;\nInherits from lds::SSID\u0026lt; Fit \u0026gt;\nAdditional inherited members # Public Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes inherited from lds::SSID\u0026lt; Fit \u0026gt;\nName UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":43,"href":"/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/","title":"lds::poisson::SwitchedController","section":"Classes","content":" lds::poisson::SwitchedController # Poisson-observation SwitchedController Type. #include \u0026lt;lds_poisson_sctrl.h\u0026gt;\nInherits from lds::SwitchedController\u0026lt; System \u0026gt;, lds::Controller\u0026lt; System \u0026gt;\nPublic Functions # Name virtual void set_y_ref(const Vector \u0026amp; y_ref) override\nSet reference output. Additional inherited members # Public Functions inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes inherited from lds::SwitchedController\u0026lt; System \u0026gt;\nName std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_g_design(const Vector \u0026amp; g_design)\nSet input gain used in controller design (g_design) void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) void set_Kc(const Matrix \u0026amp; Kc)\nSet state controller gain. void set_Kc_inty(const Matrix \u0026amp; Kc_inty)\nSet integral controller gain. void set_Kc_u(const Matrix \u0026amp; Kc_u)\nSet input controller gain. void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Public Function Details # set_y_ref # inline virtual void set_y_ref( const Vector \u0026amp; y_ref ) override Reimplements: lds::Controller::set_y_ref\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":44,"href":"/docs/api/classes/classlds_1_1poisson_1_1_system/","title":"lds::poisson::System","section":"Classes","content":" lds::poisson::System # Poisson System type. #include \u0026lt;lds_poisson_sys.h\u0026gt;\nInherits from lds::System\nPublic Functions # Name System() =default\nConstructs a new System. System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nConstructs a new Poisson System. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) override\nSimulate system measurement. Protected Functions # Name virtual void h() override\nSystem output function. virtual Vector h_(Vector x) override\nSystem output function: stateless. virtual void RecurseKe() override\nRecursively recalculate estimator gain (Ke) Additional inherited members # Public Functions inherited from lds::System\nName virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions inherited from lds::System\nName void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes inherited from lds::System\nName bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes inherited from lds::System\nName std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 [optional] initial diagonal elements of state estimate covariance (P) q0 [optional] initial diagonal elements of process noise covariance (Q) Simulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) override Parameters:\nu_tm1 input at t-1 Return: z measurement\nReimplements: lds::System::Simulate\nSimulate system and produce measurement\nProtected Function Details # h # inline virtual void h() override Reimplements: lds::System::h\nh_ # inline virtual Vector h_( Vector x ) override Reimplements: lds::System::h_\nRecurseKe # virtual void RecurseKe() override Reimplements: lds::System::RecurseKe\nRecursively recalculate estimator gain (Ke).\nReferences:\nSmith AC, Brown EN. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation 15.\nEden UT, \u0026hellip;, Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Process Adaptive Filtering Neural Computation 16.\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":45,"href":"/docs/api/classes/classlds_1_1_s_s_i_d/","title":"lds::SSID","section":"Classes","content":" lds::SSID # More\u0026hellip;\nInherited by lds::gaussian::FitSSID, lds::poisson::FitSSID\nPublic Functions # Name SSID() =default\nConstructs a new SSIDFit type. SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf))\nConstructs a new SSIDFit type. std::tuple\u0026lt; Fit, Vector \u0026gt; Run(SSIDWt ssid_wt)\nRuns fitting by subspace identification (SSID) std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData()\nReturns the I/O data to caller. Protected Functions # Name void CalcD(data_t t_silence =0.1, data_t thresh_silence =0.001)\nUsing periods of silence in inputs (u), calculates the output \\ bias (d) void CreateHankelDataMat()\nCreates the block-hankel I/O data matrix. virtual void DecomposeData() =0\nDecompose data to lower-triangular matrix (used in Solve) void CalcSVD(SSIDWt wt)\nperforms the singular value decomposition (SVD) void Solve(data_t wt_dc)\nsolves for LDS parameters void RecomputeExtObs()\nrecompute extended observability matrix from estimates of A, C Protected Attributes # Name UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_ input training data UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_ measurement training data Matrix D_ block-Hankel I/O data matrix Fit fit_ fit Matrix g_dc_ I/O gain @ DC. data_t dt_ sample period size_t n_u_ number of inputs size_t n_x_ number of states size_t n_y_ number of outputs size_t n_h_ size_t n_trials_ number of input/output data sequences std::vector\u0026lt; size_t \u0026gt; n_t_ number of time steps size_t n_t_tot_ total number of time steps across trials Matrix L_ lower triangle decomp of covariance matrix Vector s_ singular values Matrix ext_obs_t_ extended observability matrix Detailed Description # template \u0026lt;typename Fit \u0026gt; class lds::SSID; Public Function Details # SSID # SSID() =default SSID # SSID( size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026amp;\u0026amp; z_train, const Vector \u0026amp; d =Vector(1).fill(-kInf) ) Parameters:\nn_x number of states n_h size of block-hankel data matrix dt sample period u_train input training data z_train measurement training data d output bias Run # std::tuple\u0026lt; Fit, Vector \u0026gt; Run( SSIDWt ssid_wt ) Parameters:\nssid_wt weight for singular value decomp Return: tuple (Fit, singular values)\nReturnData # inline std::tuple\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt;, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; ReturnData() Return: tuple(input data, output data)\nProtected Function Details # CalcD # void CalcD( data_t t_silence =0.1, data_t thresh_silence =0.001 ) Parameters:\nt_silence threshold on period of time that qualifies as \u0026ldquo;silence\u0026rdquo; thresh_silence threshold on input amplitude u that qualifies as \u0026ldquo;silence\u0026rdquo; CreateHankelDataMat # void CreateHankelDataMat() Creates the block-hankel I/O data matrix. Also calculates I/O gain @ DC.\nDecomposeData # virtual void DecomposeData() =0 Reimplemented by: lds::gaussian::FitSSID::DecomposeData, lds::poisson::FitSSID::DecomposeData\nCalcSVD # void CalcSVD( SSIDWt wt ) Parameters:\nssid_wt weight for SVD Solve # void Solve( data_t wt_dc ) Parameters:\nwt_dc weight placed on getting correct DC I/O gain RecomputeExtObs # void RecomputeExtObs() Protected Attribute Details # u_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u_; z_ # UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z_; D_ # Matrix D_; fit_ # Fit fit_; g_dc_ # Matrix g_dc_; dt_ # data_t dt_ {}; n_u_ # size_t n_u_ {}; n_x_ # size_t n_x_ {}; n_y_ # size_t n_y_ {}; n_h_ # size_t n_h_ {}; n_trials_ # size_t n_trials_ {}; n_t_ # std::vector\u0026lt; size_t \u0026gt; n_t_; n_t_tot_ # size_t n_t_tot_ {}; L_ # Matrix L_; s_ # Vector s_; ext_obs_t_ # Matrix ext_obs_t_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":46,"href":"/docs/api/classes/classlds_1_1_switched_controller/","title":"lds::SwitchedController","section":"Classes","content":" lds::SwitchedController # SwitchedController Type. More\u0026hellip;\n#include \u0026lt;lds_sctrl.h\u0026gt;\nInherits from lds::Controller\u0026lt; System \u0026gt;\nInherited by lds::gaussian::SwitchedController, lds::poisson::SwitchedController\nPublic Functions # Name SwitchedController() =default\nConstructs a new SwitchedController. SwitchedController(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController. SwitchedController(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new SwitchedController (moves systems). void Switch(size_t idx, bool do_force_switch =false)\nSwitch to a different sub-system/controller. void set_Kc(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc)\nsets state feedback gains void set_Kc(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc)\nsets state feedback gains (moving) void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty)\nsets integral feedback gains void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty)\nsets integral feedback gains (moving) void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u)\nsets input feedback gains void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u)\nsets input feedback gains (moving) void set_g_design(const UniformVectorList \u0026amp; g)\nsets input gain used during controller design void set_g_design(UniformVectorList \u0026amp;\u0026amp; g)\nsets input gain used during controller design (moving) Protected Attributes # Name std::vector\u0026lt; System \u0026gt; systems_ underlying sub-systems which are switched between size_t n_sys_ number of systems size_t idx_ current system/controller index. UniformMatrixList Kc_list_ UniformMatrixList Kc_inty_list_ UniformMatrixList Kc_u_list_ UniformVectorList g_design_list_ Additional inherited members # Public Functions inherited from lds::Controller\u0026lt; System \u0026gt;\nName Controller() =default\nConstructs a new Controller. Controller(const System \u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller. Controller(System \u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type =0)\nConstructs a new Controller by moving the system object. const Vector \u0026amp; Control(const Vector \u0026amp; z, bool do_control =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal (single-step) const Vector \u0026amp; ControlOutputReference(const Vector \u0026amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, data_t sigma_soft_start =0, data_t sigma_u_noise =0, bool do_reset_at_control_onset =true)\nupdates control signal, given previously-set (single-step) const System \u0026amp; sys() const const Matrix \u0026amp; Kc() const\nGet state feedback controller gain. const Matrix \u0026amp; Kc_inty() const\nGet integral controller gain. const Matrix \u0026amp; Kc_u() const\nGet input feedback controller gain. const Vector \u0026amp; g_design() const\nGet input gain used in controller design. const Vector \u0026amp; u_ref() const\nGet reference input. const Vector \u0026amp; x_ref() const\nGet reference state. const Vector \u0026amp; y_ref() const\nGet reference output. size_t control_type() const\nGet controller type. data_t tau_awu() const\nGet time constant of anti-integral-windup. data_t u_lb() const\nGet control lower bound. data_t u_ub() const\nGet control upper bound. void set_sys(const System \u0026amp; sys)\nSet system. void set_u_ref(const Vector \u0026amp; u_ref)\nSet reference input (u_ref) void set_x_ref(const Vector \u0026amp; x_ref)\nSet reference state (x_ref) virtual void set_y_ref(const Vector \u0026amp; y_ref)\nSet reference output (y_ref) void set_tau_awu(data_t tau)\nSet time constant of anti-integral-windup. void set_control_type(size_t control_type)\nSets the control type. void set_u_lb(data_t u_lb)\nsets control lower bound void set_u_ub(data_t u_ub)\nSets control upper bound. void Reset()\nreset system and control variables. void Print()\nprints variables to stdout Protected Attributes inherited from lds::Controller\u0026lt; System \u0026gt;\nName System sys_ underlying LDS Vector u_ control signal Vector u_return_ control signal that is returned to user Vector g_design_ input gain of the system used for controller design Vector u_ref_ reference input Vector u_ref_prev_ reference input at previous time step Vector x_ref_ reference state Vector y_ref_ reference output Vector cx_ref_ Matrix Kc_ state controller gain Matrix Kc_u_ input controller gain (optional when control updates \\deltaU) Matrix Kc_inty_ integral controller gain Vector du_ref_ Vector dv_ref_ Vector v_ref_ Vector dv_ Vector v_ Control after g inversion (e.g., control in physical units) Vector int_e_ integrated error Vector int_e_awu_adjust_ anti-windup adjustment to intE Vector u_sat_ control signal after saturation (for antiWindup) bool do_control_prev_ bool do_lock_control_prev_ bool u_saturated_ whether control signal has reached saturation limits data_t u_lb_ lower bound on control data_t u_ub_ upper bound on control data_t tau_awu_ antiwindup time constant data_t k_awu_ data_t t_since_control_onset_ time since control epoch onset size_t control_type_ controller type Detailed Description # template \u0026lt;typename System \u0026gt; class lds::SwitchedController; Public Function Details # SwitchedController # SwitchedController() =default SwitchedController # inline SwitchedController( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask SwitchedController # inline SwitchedController( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type =0 ) Parameters:\nsystems vector of sub-systems u_lb lower bound on control (u) u_ub upper bound on control (u) control_type [optional] control type bit mask Switch # inline void Switch( size_t idx, bool do_force_switch =false ) Parameters:\nidx index do_force_switch whether to force a system switch even if already there. set_Kc # inline void set_Kc( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc ) set_Kc # inline void set_Kc( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc ) set_Kc_inty # inline void set_Kc_inty( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_inty ) set_Kc_inty # inline void set_Kc_inty( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_inty ) set_Kc_u # inline void set_Kc_u( const UniformMatrixList\u0026lt;\u0026gt; \u0026amp; Kc_u ) set_Kc_u # inline void set_Kc_u( UniformMatrixList\u0026lt;\u0026gt; \u0026amp;\u0026amp; Kc_u ) set_g_design # inline void set_g_design( const UniformVectorList \u0026amp; g ) set_g_design # inline void set_g_design( UniformVectorList \u0026amp;\u0026amp; g ) Protected Attribute Details # systems_ # std::vector\u0026lt; System \u0026gt; systems_; n_sys_ # size_t n_sys_ {}; idx_ # size_t idx_ {}; Kc_list_ # UniformMatrixList Kc_list_; Kc_inty_list_ # UniformMatrixList Kc_inty_list_; Kc_u_list_ # UniformMatrixList Kc_u_list_; g_design_list_ # UniformVectorList g_design_list_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":47,"href":"/docs/api/classes/classlds_1_1_system/","title":"lds::System","section":"Classes","content":" lds::System # Linear Dynamical System Type. #include \u0026lt;lds_sys.h\u0026gt;\nInherited by lds::gaussian::System, lds::poisson::System\nPublic Functions # Name System() =default\nConstructs a new System. System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0)\nconstructs a new System virtual ~System() void Filter(const Vector \u0026amp; u_tm1, const Vector \u0026amp; z)\nFilter data to produce causal state estimates. virtual const Vector \u0026amp; Simulate(const Vector \u0026amp; u_tm1) =0\nsimulates system (single time step) void f(const Vector \u0026amp; u, bool do_add_noise =false)\nsystem dynamics function virtual void h() =0\nsystem output function virtual Vector h_(Vector x) =0\nsystem output function (stateless) size_t n_u() const\nGet number of inputs. size_t n_x() const\nGet number of states. size_t n_y() const\nGet number of outputs. data_t dt() const\nGet sample period. const Vector \u0026amp; x() const\nGet current state. const Matrix \u0026amp; P() const\nGet covariance of state estimate. const Vector \u0026amp; m() const\nGet current process disturbance/bias. const Matrix \u0026amp; P_m() const\nGet covariance of process disturbance estimate. const Vector \u0026amp; cx() const\nGet C*x. const Vector \u0026amp; y() const\nGet output. const Vector \u0026amp; x0() const\nGet initial state. const Vector \u0026amp; m0() const\nGet initial disturbance. const Matrix \u0026amp; A() const\nGet state matrix. const Matrix \u0026amp; B() const\nGet input matrix. const Vector \u0026amp; g() const\nGet input gain/conversion factor. const Matrix \u0026amp; C() const\nGet output matrix. const Vector \u0026amp; d() const\nGet output bias. const Matrix \u0026amp; Ke() const\nGet estimator gain. const Matrix \u0026amp; Ke_m() const\nGet estimator gain for process disturbance (m) const Matrix \u0026amp; Q()\nGet process noise covariance. const Matrix \u0026amp; Q_m()\nGet process noise covariance of disturbance evoluation. const Matrix \u0026amp; P0()\nGet covariance of initial state. const Matrix \u0026amp; P0_m()\nGet covariance of initial process disturbance. void set_A(const Matrix \u0026amp; A)\nSet state matrix. void set_B(const Matrix \u0026amp; B)\nSet input matrix. void set_m(const Vector \u0026amp; m, bool do_force_assign =false)\nSet process disturbance. void set_g(const Vector \u0026amp; g)\nSet input gain. void set_Q(const Matrix \u0026amp; Q)\nSet process noise covariance. void set_Q_m(const Matrix \u0026amp; Q_m)\nSet process noise covariance of disturbance evoluation. void set_x0(const Vector \u0026amp; x0)\nSet initial state. void set_P0(const Matrix \u0026amp; P0)\nSet covariance of initial state. void set_P0_m(const Matrix \u0026amp; P0_m)\nSet covariance of initial process disturbance. void set_C(const Matrix \u0026amp; C)\nSet output matrix. void set_d(const Vector \u0026amp; d)\nSet output bias. void set_x(const Vector \u0026amp; x)\nSet state of system. void Reset()\nReset system variables. std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block(UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1) void Print()\nPrint system variables to stdout. Protected Functions # Name virtual void RecurseKe() =0\nRecursively recalculate estimator gain (Ke) void InitVars(data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0) Public Attributes # Name bool do_adapt_m whether to adaptively estimate disturbance m Protected Attributes # Name std::size_t n_x_ number of states std::size_t n_u_ number of inputs std::size_t n_y_ number of outputs data_t dt_ sample period Vector x_ state Matrix P_ covariance of state estimate Vector m_ process disturbance Matrix P_m_ covariance of disturbance estimate Vector cx_ C*x. Vector y_ output Vector z_ measurement Vector x0_ initial state Matrix P0_ covariance of initial state estimate Vector m0_ initial process disturbance Matrix P0_m_ covariance of initial disturbance est. Matrix A_ state matrix Matrix B_ input matrix Vector g_ input gain Matrix Q_ covariance of process noise Matrix Q_m_ covariance of disturbance random walk Matrix C_ output matrix Vector d_ output bias Matrix Ke_ estimator gain Matrix Ke_m_ estimator gain for process disturbance Public Function Details # System # System() =default System # System( size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Parameters:\nn_u number of inputs n_x number of states n_y number of outputs dt sample period p0 diagonal elements for state estimate covariance q0 diagonal elements for process noise covariance ~System # inline virtual ~System() Filter # void Filter( const Vector \u0026amp; u_tm1, const Vector \u0026amp; z ) Parameters:\nu_tm1 input at t-minus-1 z_t current measurement Given current measurement and input, filter data to produce causal state estimates using Kalman filtering, which procedes by predicting the state and subsequently updating.\nSimulate # virtual const Vector \u0026amp; Simulate( const Vector \u0026amp; u_tm1 ) =0 Parameters:\nu_tm1 input at time t-1 Return: simulated measurement at time t\nReimplemented by: lds::gaussian::System::Simulate, lds::poisson::System::Simulate\nf # inline void f( const Vector \u0026amp; u, bool do_add_noise =false ) Parameters:\nu input do_add_noise whether to add simulated process noise h # virtual void h() =0 Reimplemented by: lds::gaussian::System::h, lds::poisson::System::h\nh_ # virtual Vector h_( Vector x ) =0 Parameters:\nx_t state at time t Return: predicted state at time t + 1\nReimplemented by: lds::gaussian::System::h_, lds::poisson::System::h_\nn_u # inline size_t n_u() const n_x # inline size_t n_x() const n_y # inline size_t n_y() const dt # inline data_t dt() const x # inline const Vector \u0026amp; x() const P # inline const Matrix \u0026amp; P() const m # inline const Vector \u0026amp; m() const P_m # inline const Matrix \u0026amp; P_m() const cx # inline const Vector \u0026amp; cx() const y # inline const Vector \u0026amp; y() const x0 # inline const Vector \u0026amp; x0() const m0 # inline const Vector \u0026amp; m0() const A # inline const Matrix \u0026amp; A() const B # inline const Matrix \u0026amp; B() const g # inline const Vector \u0026amp; g() const C # inline const Matrix \u0026amp; C() const d # inline const Vector \u0026amp; d() const Ke # inline const Matrix \u0026amp; Ke() const Ke_m # inline const Matrix \u0026amp; Ke_m() const Q # inline const Matrix \u0026amp; Q() Q_m # inline const Matrix \u0026amp; Q_m() P0 # inline const Matrix \u0026amp; P0() P0_m # inline const Matrix \u0026amp; P0_m() set_A # inline void set_A( const Matrix \u0026amp; A ) set_B # inline void set_B( const Matrix \u0026amp; B ) set_m # inline void set_m( const Vector \u0026amp; m, bool do_force_assign =false ) set_g # inline void set_g( const Vector \u0026amp; g ) set_Q # inline void set_Q( const Matrix \u0026amp; Q ) set_Q_m # inline void set_Q_m( const Matrix \u0026amp; Q_m ) set_x0 # inline void set_x0( const Vector \u0026amp; x0 ) set_P0 # inline void set_P0( const Matrix \u0026amp; P0 ) set_P0_m # inline void set_P0_m( const Matrix \u0026amp; P0_m ) set_C # inline void set_C( const Matrix \u0026amp; C ) set_d # inline void set_d( const Vector \u0026amp; d ) set_x # inline void set_x( const Vector \u0026amp; x ) Reset # void Reset() nstep_pred_block # std::vector\u0026lt; UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; \u0026gt; nstep_pred_block( UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; u, UniformMatrixList\u0026lt; kMatFreeDim2 \u0026gt; z, size_t n_pred =1 ) Print # void Print() Protected Function Details # RecurseKe # virtual void RecurseKe() =0 Reimplemented by: lds::gaussian::System::RecurseKe, lds::poisson::System::RecurseKe\nInitVars # void InitVars( data_t p0 =kDefaultP0, data_t q0 =kDefaultQ0 ) Public Attribute Details # do_adapt_m # bool do_adapt_m {}; Protected Attribute Details # n_x_ # std::size_t n_x_ {}; n_u_ # std::size_t n_u_ {}; n_y_ # std::size_t n_y_ {}; dt_ # data_t dt_ {}; x_ # Vector x_; P_ # Matrix P_; m_ # Vector m_; P_m_ # Matrix P_m_; cx_ # Vector cx_; y_ # Vector y_; z_ # Vector z_; x0_ # Vector x0_; P0_ # Matrix P0_; m0_ # Vector m0_; P0_m_ # Matrix P0_m_; A_ # Matrix A_; B_ # Matrix B_; g_ # Vector g_; Q_ # Matrix Q_; Q_m_ # Matrix Q_m_; C_ # Matrix C_; d_ # Vector d_; Ke_ # Matrix Ke_; Ke_m_ # Matrix Ke_m_; Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":48,"href":"/docs/api/classes/classlds_1_1_uniform_matrix_list/","title":"lds::UniformMatrixList","section":"Classes","content":" lds::UniformMatrixList # More\u0026hellip;\nInherits from std::vector\u0026lt; Matrix \u0026gt;\nPublic Functions # Name UniformMatrixList() =default\nConstructs a new UniformMatrixList. UniformMatrixList(const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by copying existing vector of Matrix if dimensions consistent. UniformMatrixList(std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList by moving existing vector of Matrix if dimensions consistent. UniformMatrixList(std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0})\nConstructs a new UniformMatrixList from initializer_list of Matrix if dimensions consistent. UniformMatrixList(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nConstructs a new UniformMatrixList (copy). UniformMatrixList(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nConstructs a new UniformMatrixList (move). ~UniformMatrixList() =default\nDestroys the object. const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim(size_t n =0) const\ngets dimensions of uniformly sized matrices size_t size()\nsize of container const Matrix \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Matrix \u0026amp; that, size_t n)\nswaps input matrix with n^th matrix of list UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that)\nassigns the contents (copy) UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=(UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that)\nassigns the contents (move) void append(const Matrix \u0026amp; mat)\nappends a matrix to the list Detailed Description # template \u0026lt;MatrixListFreeDim D =kMatFreeDimNone\u0026gt; class lds::UniformMatrixList; Public Function Details # UniformMatrixList # UniformMatrixList() =default UniformMatrixList # explicit UniformMatrixList( const std::vector\u0026lt; Matrix \u0026gt; \u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # explicit UniformMatrixList( std::vector\u0026lt; Matrix \u0026gt; \u0026amp;\u0026amp; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( std::initializer_list\u0026lt; Matrix \u0026gt; mats, std::array\u0026lt; size_t, 2 \u0026gt; dim ={0, 0} ) Parameters:\nmats input matrices dim dimensions UniformMatrixList # UniformMatrixList( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList UniformMatrixList # UniformMatrixList( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList ~UniformMatrixList # ~UniformMatrixList() =default dim # inline const std::array\u0026lt; size_t, 2 \u0026gt; \u0026amp; dim( size_t n =0 ) const Parameters:\nn [optional] index in list of matrices Return: dimensions\nsize # inline size_t size() at # inline const Matrix \u0026amp; at( size_t n ) Swap # inline void Swap( Matrix \u0026amp; that, size_t n ) Parameters:\nthat input matrix n index where the matrix is moved operator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( const UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\noperator= # inline UniformMatrixList\u0026lt; D \u0026gt; \u0026amp; operator=( UniformMatrixList\u0026lt; D \u0026gt; \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformMatrixList Return: reference to object\nappend # void append( const Matrix \u0026amp; mat ) Parameters:\nmat input matrix Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":49,"href":"/docs/api/classes/classlds_1_1_uniform_system_list/","title":"lds::UniformSystemList","section":"Classes","content":" lds::UniformSystemList # More\u0026hellip;\nInherits from std::vector\u0026lt; System \u0026gt;\nPublic Functions # Name UniformSystemList() =default\nConstructs a new UniformSystemList. UniformSystemList(const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by copying existing vector of System if dimensions consistent. UniformSystemList(std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList by moving existing vector of System if dimensions consistent. UniformSystemList(std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0})\nConstructs a new UniformSystemList from initializer_list of System if dimensions consistent. UniformSystemList(const UniformSystemList \u0026amp; that)\nConstructs a new UniformSystemList (copy). UniformSystemList(UniformSystemList \u0026amp;\u0026amp; that)\nConstructs a new UniformSystemList (move). ~UniformSystemList() =default\nDestroys the object. const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const\ngets dimensions of the uniformly sized systems size_t size()\nsize of container const System \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(System \u0026amp; that, size_t n)\nswaps input system with n^th system of list UniformSystemList \u0026amp; operator=(const UniformSystemList \u0026amp; that)\nassigns the contents (copy) UniformSystemList \u0026amp; operator=(UniformSystemList \u0026amp;\u0026amp; that)\nassigns the contents (move) Detailed Description # template \u0026lt;typename System \u0026gt; class lds::UniformSystemList; Public Function Details # UniformSystemList # UniformSystemList() =default UniformSystemList # explicit UniformSystemList( const std::vector\u0026lt; System \u0026gt; \u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # explicit UniformSystemList( std::vector\u0026lt; System \u0026gt; \u0026amp;\u0026amp; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( std::initializer_list\u0026lt; System \u0026gt; systems, std::array\u0026lt; size_t, 3 \u0026gt; dim ={0, 0, 0} ) Parameters:\nsystems input systems dim dimensions (n_u, n_x, n_y) UniformSystemList # UniformSystemList( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList UniformSystemList # UniformSystemList( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList ~UniformSystemList # ~UniformSystemList() =default dim # inline const std::array\u0026lt; size_t, 3 \u0026gt; \u0026amp; dim() const size # inline size_t size() at # inline const System \u0026amp; at( size_t n ) Swap # inline void Swap( System \u0026amp; that, size_t n ) Parameters:\nthat input system n index where the system is moved operator= # inline UniformSystemList \u0026amp; operator=( const UniformSystemList \u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\noperator= # inline UniformSystemList \u0026amp; operator=( UniformSystemList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformSystemList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":50,"href":"/docs/api/classes/classlds_1_1_uniform_vector_list/","title":"lds::UniformVectorList","section":"Classes","content":" lds::UniformVectorList # Inherits from std::vector\u0026lt; Vector \u0026gt;\nPublic Functions # Name UniformVectorList() =default\nConstructs a new UniformVectorList. UniformVectorList(const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by copying existing vector of Vector if dimensions consistent. UniformVectorList(std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0)\nConstructs a new UniformVectorList by moving existing vector of Vector if dimensions consistent. UniformVectorList(std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0)\nConstructs a new UniformVectorList from initializer_list of Vector if dimensions consistent. UniformVectorList(const UniformVectorList \u0026amp; that)\nConstructs a new UniformVectorList (copy) UniformVectorList(UniformVectorList \u0026amp;\u0026amp; that)\nConstructs a new UniformVectorList (move) ~UniformVectorList() =default\nDestroys the object. size_t dim() const\ngets dimensions of the uniformly sized matrices size_t size()\nsize of container const Vector \u0026amp; at(size_t n)\ngets reference to n^th element void Swap(Vector \u0026amp; that, size_t n)\nswaps input matrix with n^th vector of list UniformVectorList \u0026amp; operator=(const UniformVectorList \u0026amp; that)\nassigns the contents (copy) UniformVectorList \u0026amp; operator=(UniformVectorList \u0026amp;\u0026amp; that)\nassigns the contents (move) Public Function Details # UniformVectorList # UniformVectorList() =default UniformVectorList # explicit UniformVectorList( const std::vector\u0026lt; Vector \u0026gt; \u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dims dimension UniformVectorList # explicit UniformVectorList( std::vector\u0026lt; Vector \u0026gt; \u0026amp;\u0026amp; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( std::initializer_list\u0026lt; Vector \u0026gt; vecs, size_t dim =0 ) Parameters:\nvecs input vectors dim dimension UniformVectorList # UniformVectorList( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList UniformVectorList # UniformVectorList( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList ~UniformVectorList # ~UniformVectorList() =default dim # inline size_t dim() const size # inline size_t size() at # inline const Vector \u0026amp; at( size_t n ) Swap # inline void Swap( Vector \u0026amp; that, size_t n ) Parameters:\nthat input vector n index where the vector is moved operator= # inline UniformVectorList \u0026amp; operator=( const UniformVectorList \u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\noperator= # inline UniformVectorList \u0026amp; operator=( UniformVectorList \u0026amp;\u0026amp; that ) Parameters:\nthat another UniformVectorList Return: reference to object\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":51,"href":"/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/","title":"ldsCtrlEst_h","section":"Files","content":" ldsCtrlEst_h # Files # Name ldsCtrlEst_h/lds.h lds namespace ldsCtrlEst_h/lds_ctrl.h Controller. ldsCtrlEst_h/lds_fit.h LDS base fit type. ldsCtrlEst_h/lds_fit_em.h subspace identification ldsCtrlEst_h/lds_fit_ssid.h subspace identification ldsCtrlEst_h/lds_gaussian.h glds namespace ldsCtrlEst_h/lds_gaussian_ctrl.h GLDS Controller. ldsCtrlEst_h/lds_gaussian_fit.h GLDS fit type. ldsCtrlEst_h/lds_gaussian_fit_em.h GLDS E-M fit type. ldsCtrlEst_h/lds_gaussian_fit_ssid.h GLDS SSID fit type. ldsCtrlEst_h/lds_gaussian_sctrl.h GLDS switched controller type. ldsCtrlEst_h/lds_gaussian_sys.h GLDS base type. ldsCtrlEst_h/lds_poisson.h plds namespace ldsCtrlEst_h/lds_poisson_ctrl.h PLDS controller type. ldsCtrlEst_h/lds_poisson_fit.h PLDS base fit type. ldsCtrlEst_h/lds_poisson_fit_em.h PLDS E-M fit type. ldsCtrlEst_h/lds_poisson_fit_ssid.h PLDS SSID fit type. ldsCtrlEst_h/lds_poisson_sctrl.h PLDS switched controller type. ldsCtrlEst_h/lds_poisson_sys.h PLDS base type. ldsCtrlEst_h/lds_sctrl.h SwitchedController type. ldsCtrlEst_h/lds_sys.h LDS base type. ldsCtrlEst_h/lds_uniform_mats.h List of uniformly sized matrices. ldsCtrlEst_h/lds_uniform_systems.h List of uniformly sized Systems. ldsCtrlEst_h/lds_uniform_vecs.h List of uniformly sized vectors. ldsCtrlEst_h/mex_c_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) ldsCtrlEst_h/mex_cpp_util.h arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":52,"href":"/docs/api/files/lds__ctrl_8h/","title":"ldsCtrlEst_h/lds_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_ctrl.h # Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Controller Detailed Description # This file declares the type for control of a linear dynamical system (lds::Controller).\nSource code # //===-- ldsCtrlEst_h/lds_control.h - Controller -----------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_CTRL_H #define LDSCTRLEST_LDS_CTRL_H // namespace #include \u0026#34;lds.h\u0026#34; // system type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class Controller { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); public: Controller() = default; Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type = 0); const Vector\u0026amp; Control(const Vector\u0026amp; z, bool do_control = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); const Vector\u0026amp; ControlOutputReference(const Vector\u0026amp; z, bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); // get methods: const System\u0026amp; sys() const { return sys_; }; const Matrix\u0026amp; Kc() const { return Kc_; }; const Matrix\u0026amp; Kc_inty() const { return Kc_inty_; }; const Matrix\u0026amp; Kc_u() const { return Kc_u_; }; const Vector\u0026amp; g_design() const { return g_design_; }; const Vector\u0026amp; u_ref() const { return u_ref_; }; const Vector\u0026amp; x_ref() const { return x_ref_; }; const Vector\u0026amp; y_ref() const { return y_ref_; }; size_t control_type() const { return control_type_; }; data_t tau_awu() const { return tau_awu_; }; data_t u_lb() const { return u_lb_; }; data_t u_ub() const { return u_ub_; }; // set methods void set_sys(const System\u0026amp; sys) { bool does_match = sys_.n_u() == sys.n_u(); does_match = does_match \u0026amp;\u0026amp; (sys_.n_x() == sys.n_x()); does_match = does_match \u0026amp;\u0026amp; (sys_.n_y() == sys.n_y()); if (does_match) { sys_ = sys; } else { throw std::runtime_error( \u0026#34;new system argument to `set_sys` does not match dimensionality of \u0026#34; \u0026#34;existing system\u0026#34;); } }; void set_g_design(const Vector\u0026amp; g_design) { Reassign(g_design_, g_design); }; void set_u_ref(const Vector\u0026amp; u_ref) { Reassign(u_ref_, u_ref); }; void set_x_ref(const Vector\u0026amp; x_ref) { Reassign(x_ref_, x_ref); cx_ref_ = sys_.C() * x_ref_; }; // y_ref needs to be handled differently depending on output fn. // (need to populate cx_ref_ too, which depends on output fn) virtual void set_y_ref(const Vector\u0026amp; y_ref) { Reassign(y_ref_, y_ref); }; void set_Kc(const Matrix\u0026amp; Kc) { Reassign(Kc_, Kc); }; void set_Kc_inty(const Matrix\u0026amp; Kc_inty) { Reassign(Kc_inty_, Kc_inty); }; void set_Kc_u(const Matrix\u0026amp; Kc_u) { Reassign(Kc_u_, Kc_u); }; void set_tau_awu(data_t tau) { tau_awu_ = tau; k_awu_ = sys_.dt() / tau_awu_; }; void set_control_type(size_t control_type); // There is no reason u_lb/ub should not be public, but making set methods // anyway. void set_u_lb(data_t u_lb) { u_lb_ = u_lb; }; void set_u_ub(data_t u_ub) { u_ub_ = u_ub; }; void Reset() { sys_.Reset(); u_ref_.zeros(); u_ref_prev_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); u_saturated_ = false; t_since_control_onset_ = 0.0; }; void Print() { sys_.Print(); std::cout \u0026lt;\u0026lt; \u0026#34;g_design : \u0026#34; \u0026lt;\u0026lt; g_design_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_lb : \u0026#34; \u0026lt;\u0026lt; u_lb_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;u_ub : \u0026#34; \u0026lt;\u0026lt; u_ub_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; }; protected: System sys_; Vector u_; Vector u_return_; Vector g_design_; // reference signals Vector u_ref_; // create no set method for this: Vector u_ref_prev_; Vector x_ref_; Vector y_ref_; Vector cx_ref_; // Controller gains Matrix Kc_; Matrix Kc_u_; Matrix Kc_inty_; // control after g inversion // do not need set methods for these. Vector du_ref_; Vector dv_ref_; Vector v_ref_; Vector dv_; Vector v_; // integral error // do not need set method for this Vector int_e_; Vector int_e_awu_adjust_; Vector u_sat_; bool do_control_prev_ = false; bool do_lock_control_prev_ = false; // whether the g of system has become inverted from what you think it is // (gain_ref) bool u_saturated_ = false; // should be safe to have references here bc nothing needs to be done // (like reset vars) when it changes... data_t u_lb_{}; data_t u_ub_{}; data_t tau_awu_{}; data_t k_awu_ = 0; data_t t_since_control_onset_ = 0; size_t control_type_{}; private: void CalcControl(bool do_control = true, bool do_estimation = true, bool do_lock_control = false, data_t sigma_soft_start = 0, data_t sigma_u_noise = 0, bool do_reset_at_control_onset = true); void CalcSteadyStateSetPoint(); void AntiWindup(); void InitVars(size_t control_type); }; // Implement the above: template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(const System\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(sys), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline Controller\u0026lt;System\u0026gt;::Controller(System\u0026amp;\u0026amp; sys, data_t u_lb, data_t u_ub, size_t control_type) : sys_(std::move(sys)), u_lb_(u_lb), u_ub_(u_ub), tau_awu_(lds::kInf) { InitVars(control_type); } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::set_control_type(size_t control_type) { if (control_type_ == control_type) { return; } // creating a blank slate... control_type_ = 0; Kc_inty_.zeros(0, 0); Kc_u_.zeros(0, 0); int_e_.zeros(0, 0); int_e_awu_adjust_.zeros(0, 0); // controller was designed to minimize integral error if (control_type \u0026amp; kControlTypeIntY) { Kc_inty_.zeros(sys_.n_u(), sys_.n_y()); int_e_.zeros(sys_.n_y()); int_e_awu_adjust_.zeros(sys_.n_u()); control_type_ = control_type_ | kControlTypeIntY; } // controller was designed to minimize deltaU // (i.e. state augmented with u) if (control_type \u0026amp; kControlTypeDeltaU) { Kc_u_.zeros(sys_.n_u(), sys_.n_u()); control_type_ = control_type_ | kControlTypeDeltaU; } // whether to adapt set point calculate with (re-estimated) process // disturbance (m) if (control_type \u0026amp; kControlTypeAdaptM) { if (sys_.do_adapt_m) // only if adapting m... { control_type_ = control_type_ | kControlTypeAdaptM; } } } // set_control_type template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::Control( const Vector\u0026amp; z, bool do_control, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement sys_.Filter(u_, z); bool do_estimation = true; // always have estimator on in this case // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline const Vector\u0026amp; Controller\u0026lt;System\u0026gt;::ControlOutputReference( const Vector\u0026amp; z, bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { // update state estimates, given latest measurement if (do_estimation) { sys_.Filter(u_, z); } else { sys_.f(u_); } // calculate the set point // solves for u_ref and x_ref when output is at y_ref at steady state. if (do_control) { CalcSteadyStateSetPoint(); } // calculate control signal CalcControl(do_control, do_estimation, do_lock_control, sigma_soft_start, sigma_u_noise, do_reset_at_control_onset); return u_return_; } template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcControl(bool do_control, bool do_estimation, bool do_lock_control, data_t sigma_soft_start, data_t sigma_u_noise, bool do_reset_at_control_onset) { if (do_control \u0026amp;\u0026amp; do_estimation) { if (!do_control_prev_) { if (do_reset_at_control_onset) { Reset(); } t_since_control_onset_ = 0.0; } else { t_since_control_onset_ += sys_.dt(); } // enforce softstart on control vars. if (sigma_soft_start \u0026gt; 0) { // half-Gaussian soft-start scaling factor data_t soft_start_sf = 1 - exp(-pow(t_since_control_onset_, 2) / (2 * pow(sigma_soft_start, 2))); u_ref_ *= soft_start_sf; // TODO(mfbolus): May be appropriate to soft-start x_ref, y_ref too // x_ref_ *= soft_start_sf; // cx_ref_ *= soft_start_sf; // y_ref_ *= soft_start_sf; } if (!do_lock_control) { // first do u -\u0026gt; v change of vars. (v = g.*u) // e.g., convert into physical units (e.g., v[=] mW/mm2 rather than driver // control voltage u[=]V) v_ref_ = g_design_ % u_ref_; // Given FB, calc. the change in control if (control_type_ \u0026amp; kControlTypeDeltaU) { // if control designed to minimize not u but deltaU (i.e. state aug with // u): // TODO(mfbolus): Commented out for now. See note below. // du_ref_ = u_ref_ - u_ref_prev_; // dv_ref_ = g_design_ % du_ref_; // TODO(mfbolus): Assuming users want *smooth* control signals if using // kControlTypeDeltaU, it should be the case that dv_ref_ is --\u0026gt; 0. May // want to revisit, but I am going to force it to be zero unless a // situation arises that argues for keeping the above. dv_ref_.zeros(); dv_ = dv_ref_; // nominally-optimal. dv_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error dv_ -= Kc_u_ * (v_ - v_ref_); // penalty on amp u (rel to ref) if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if(!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error dv_ -= Kc_inty_ * int_e_; // control for integrated error } // update the control v_ += dv_; } else { v_ = v_ref_; // nominally-optimal. v_ -= Kc_ * (sys_.x() - x_ref_); // instantaneous state error if (control_type_ \u0026amp; kControlTypeIntY) { // TODO(mfbolus): one approach to protection against integral windup // would be to not integrate error when control signal saturated: // if (!uSaturated) int_e_ += (sys_.cx() - cx_ref_) * sys_.dt(); // integrated error v_ -= Kc_inty_ * int_e_; // control for integrated error } } // convert back to control voltage u[=]V u_ = v_ / sys_.g(); } // else do nothing until lock is low } else { // if not control // feed through u_ref in open loop u_ = u_ref_ % g_design_ / sys_.g(); v_ = sys_.g() % u_; u_ref_.zeros(); int_e_.zeros(); int_e_awu_adjust_.zeros(); u_sat_.zeros(); } // ends do_control // enforce box constraints (and antiwindup) AntiWindup(); // add noise to input? // The value for u that is *returned* to user after addition of any noise, // while keeping controller/estimator blind to this addition. u_return_ = u_; if ((sigma_u_noise \u0026gt; 0.0) \u0026amp;\u0026amp; (do_control \u0026amp;\u0026amp; !do_lock_control)) { u_return_ += sigma_u_noise * Vector(sys_.n_u(), fill::randn); Limit(u_return_, u_lb_, u_ub_); }; // For next time step: u_ref_prev_ = u_ref_; do_control_prev_ = do_control; do_lock_control_prev_ = do_lock_control; } // CalcControl template \u0026lt;typename System\u0026gt; inline void Controller\u0026lt;System\u0026gt;::CalcSteadyStateSetPoint() { // Linearly-constrained least squares (ls). // // _reference: // Boyd \u0026amp; Vandenberghe (2018) Introduction to Applied Linear Algebra // Matrix a_ls = join_horiz(sys_.C(), Matrix(sys_.n_y(), sys_.n_u(), fill::zeros)); Vector b_ls = cx_ref_; Matrix c_ls = join_horiz(sys_.A() - Matrix(sys_.n_x(), sys_.n_x(), fill::eye), sys_.B() * arma::diagmat(sys_.g())); Vector d_ls = -sys_.m0(); if (control_type_ \u0026amp; kControlTypeAdaptM) { d_ls = -sys_.m(); // adapt setpoint calc with disturbance? } Matrix a_ls_t = a_ls.t(); // TODO(mfbolus): not sure why but causes seg // fault if I do not do this. Matrix phi_ls = join_vert(join_horiz(2 * a_ls_t * a_ls, c_ls.t()), join_horiz(c_ls, Matrix(sys_.n_x(), sys_.n_x(), fill::zeros))); // TODO(mfbolus): should be actual inverse, rather than pseudo-inverse: Matrix inv_phi = pinv(phi_ls); Vector xulam = inv_phi * join_vert(2 * a_ls_t * b_ls, d_ls); x_ref_ = xulam.subvec(0, sys_.n_x() - 1); u_ref_ = xulam.subvec(sys_.n_x(), sys_.n_x() + sys_.n_u() - 1); cx_ref_ = sys_.C() * x_ref_; } // CalcSteadyStateSetPoint template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::AntiWindup() { u_saturated_ = false; u_sat_ = u_; // limit u and flag whether saturated for (size_t k = 0; k \u0026lt; u_.n_elem; k++) { if (u_[k] \u0026lt; u_lb_) { u_sat_[k] = u_lb_; u_saturated_ = true; } if (u_[k] \u0026gt; u_ub_) { u_sat_[k] = u_ub_; u_saturated_ = true; } } if ((control_type_ \u0026amp; kControlTypeIntY) \u0026amp;\u0026amp; (tau_awu_ \u0026lt; lds::kInf)) { // one-step back-calculation (calculate intE for u=u_sat) // (Astroem, Rundqwist 1989 warn against using this...) // int_e_awu_adjust_ = // solve(Kc_inty_, (u_ - u_sat_)); // pinv(Kc_inty) * (u-uSat); // gradual: see Astroem, Rundqwist 1989 // this is a fudge for doing MIMO gradual // n.b., went ahead and multiplied 1/T by dt so don\u0026#39;t have to do that here. int_e_awu_adjust_ = k_awu_ * (sign(Kc_inty_).t() / sys_.n_u()) * (u_ - u_sat_); // int_e_awu_adjust_ = k_awu_ * (u_-u_sat_); int_e_ += int_e_awu_adjust_; } // set u to saturated version u_ = u_sat_; } template \u0026lt;typename System\u0026gt; void Controller\u0026lt;System\u0026gt;::InitVars(size_t control_type) { // initialize to default values u_ref_ = Vector(sys_.n_u(), fill::zeros); u_ref_prev_ = Vector(sys_.n_u(), fill::zeros); x_ref_ = Vector(sys_.n_x(), fill::zeros); y_ref_ = Vector(sys_.n_y(), fill::zeros); cx_ref_ = Vector(sys_.n_y(), fill::zeros); u_ = Vector(sys_.n_u(), fill::zeros); u_return_ = Vector(sys_.n_u(), fill::zeros); u_sat_ = Vector(sys_.n_u(), fill::zeros); // Might not need all these, so zero elements until later. Kc_ = Matrix(sys_.n_u(), sys_.n_x(), fill::zeros); Kc_u_ = Matrix(0, 0, fill::zeros); Kc_inty_ = Matrix(0, 0, fill::zeros); g_design_ = sys_.g(); // by default, same as model dv_ = Vector(sys_.n_u(), fill::zeros); v_ = Vector(sys_.n_u(), fill::zeros); du_ref_ = Vector(sys_.n_u(), fill::zeros); dv_ref_ = Vector(sys_.n_u(), fill::zeros); v_ref_ = Vector(sys_.n_u(), fill::zeros); int_e_ = Vector(0, fill::zeros); int_e_awu_adjust_ = Vector(0, fill::zeros); set_control_type(control_type); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":53,"href":"/docs/api/files/lds__fit__em_8h/","title":"ldsCtrlEst_h/lds_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_em.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::EM Detailed Description # This file declares the type for fitting a linear dynamical system by expectation-maximization (lds::EM).\nSource code # //===-- ldsCtrlEst_h/lds_fit_em.h - EM Fit ----------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_EMAX_H #define LDSCTRLEST_LDS_EMAX_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class EM { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: EM() = default; EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train); virtual ~EM() = default; const Fit\u0026amp; Run(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = true, bool calc_output = true, bool calc_measurement = true, size_t max_iter = 100, data_t tol = 1e-2); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); // auto tuple = std::make_tuple(u_, z_); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; x() const { return x_; }; const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; y() const { return y_; }; const Matrix\u0026amp; sum_E_x_t_x_t() const { return sum_E_x_t_x_t_; }; const Matrix\u0026amp; sum_E_xu_tm1_xu_tm1() const { return sum_E_xu_tm1_xu_tm1_; }; const Matrix\u0026amp; sum_E_xu_t_xu_tm1() const { return sum_E_xu_t_xu_tm1_; }; size_t n_t_tot() { return n_t_tot_; } const Vector\u0026amp; theta() const { return theta_; }; protected: void Expectation(bool force_common_initial = false); void Maximization(bool calc_dynamics = true, bool calc_Q = true, bool calc_init = false, bool calc_output = false, bool calc_measurement = false); void MaximizeDynamics(); void MaximizeQ(); void MaximizeInitial(); virtual void MaximizeOutput() = 0; virtual void MaximizeMeasurement() = 0; void Smooth(bool force_common_initial); virtual void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) = 0; void Reset(); void InitVars(); Vector UpdateTheta(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; std::vector\u0026lt;Matrix\u0026gt; x_; std::vector\u0026lt;Cube\u0026gt; P_; std::vector\u0026lt;Cube\u0026gt; P_t_tm1_; std::vector\u0026lt;Matrix\u0026gt; y_; Matrix diag_y_; // expectations calculated in E-step Matrix sum_E_x_t_x_t_; Matrix sum_E_xu_tm1_xu_tm1_; Matrix sum_E_xu_t_xu_tm1_; Fit fit_; Vector theta_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; }; template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(size_t n_x, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; fit_ = Fit(n_u_, n_x, n_y_, dt); u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; EM\u0026lt;Fit\u0026gt;::EM(const Fit\u0026amp; fit0, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train) { // make sure fit dims match I/O data if (fit0.n_u() != u_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and input training data have inconsistent dimensions\u0026#34;); } if (fit0.n_y() != z_train.at(0).n_rows) { throw std::runtime_error( \u0026#34;Initial fit and output training data have inconsistent dimensions\u0026#34;); } fit_ = fit0; u_ = std::move(u_train); z_ = std::move(z_train); InitVars(); } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::InitVars() { // check input/output data dimensions are consistent if (z_.size() != u_.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_.at(trial).n_cols != u_.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } n_u_ = fit_.n_u(); n_x_ = fit_.n_x(); n_y_ = fit_.n_y(); dt_ = fit_.dt(); x_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); P_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); P_t_tm1_ = std::vector\u0026lt;Cube\u0026gt;(n_trials_); y_ = std::vector\u0026lt;Matrix\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial] = Matrix(n_x_, n_t_[trial], fill::zeros); P_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); P_t_tm1_[trial] = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); y_[trial] = Matrix(n_y_, n_t_[trial], fill::zeros); } diag_y_ = Matrix(n_y_, n_y_, fill::zeros); // covariances in expectation step sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); } template \u0026lt;typename Fit\u0026gt; const Fit\u0026amp; EM\u0026lt;Fit\u0026gt;::Run(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement, size_t max_iter, data_t tol) { Reset(); // to initial conditions size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_ * n_y_; Vector theta(n_params); Vector theta_new(n_params); data_t max_dtheta = 1; // if solving for initial conditions, allow them be varied. // otherwise, freeze at provided values. bool force_common_initial = !calc_init; // go until parameter convergence for (size_t l = 0; l \u0026lt; max_iter; l++) { theta_ = UpdateTheta(); std::cout \u0026lt;\u0026lt; \u0026#34;Iteration \u0026#34; \u0026lt;\u0026lt; l + 1 \u0026lt;\u0026lt; \u0026#34;/\u0026#34; \u0026lt;\u0026lt; max_iter \u0026lt;\u0026lt; \u0026#34; ...\\n\u0026#34;; Expectation(force_common_initial); Maximization(calc_dynamics, calc_Q, calc_init, calc_output, calc_measurement); // check convergence theta_new = UpdateTheta(); Vector dtheta = abs(theta_new - theta_) / abs(theta_); // some parameters could be zero... arma::uvec ubi_finite = find_finite(dtheta); max_dtheta = max(dtheta.elem(ubi_finite)); std::cout \u0026lt;\u0026lt; \u0026#34;max dtheta: \u0026#34; \u0026lt;\u0026lt; max_dtheta \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; if (max_dtheta \u0026lt; tol) { std::cout \u0026lt;\u0026lt; \u0026#34;Converged.\\n\u0026#34;; break; } std::cout \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } return fit_; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Smooth(bool force_common_initial) { Matrix k_e(n_x_, n_y_); // estimator gain Cube k_backfilt; // back-filtering gains // TODO(mfbolus): this loop could be made parallel for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { Matrix x_pre(n_x_, n_t_[trial], fill::zeros); Cube p_pre(n_x_, n_x_, n_t_[trial], fill::zeros); Matrix x_post(n_x_, n_t_[trial], fill::zeros); Cube p_post(n_x_, n_x_, n_t_[trial], fill::zeros); if (force_common_initial) // forces all trials to have same initial // conditions. { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); } y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); // This *should not* be necessary but make sure P is symmetric. ForceSymPD(P_[trial].slice(0)); x_pre.col(0) = x_[trial].col(0); p_pre.slice(0) = P_[trial].slice(0); x_post.col(0) = x_[trial].col(0); p_post.slice(0) = P_[trial].slice(0); // filter for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // predict fit_.f(x_pre, x_post, u_.at(trial), t); fit_.h(y_[trial], x_pre, t); diag_y_.diag() = y_[trial].col(t); // TODO(mfbolus): change if parallel // update --\u0026gt; posterior estimation RecurseKe(k_e, p_pre, p_post, t); x_post.col(t) = x_pre.col(t) + k_e * (z_.at(trial).col(t) - y_[trial].col(t)); y_[trial].col(t) = fit_.C() * x_post.col(t) + fit_.d(); } // backfilter -\u0026gt; Smoothed estimate // Reference: // Shumway et Stoffer (1982) ForceSymPD(p_post.slice(n_t_[trial] - 1)); k_backfilt = Cube(n_x_, n_x_, n_t_[trial], fill::zeros); x_[trial].col(n_t_[trial] - 1) = x_post.col(n_t_[trial] - 1); P_[trial].slice(n_t_[trial] - 1) = p_post.slice(n_t_[trial] - 1); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 0; t--) { // TODO(mfmbolus): should not be necessary to force symm positive def ForceSymPD(p_pre.slice(t)); ForceSymPD(p_post.slice(t - 1)); ForceSymPD(P_[trial].slice(t)); k_backfilt.slice(t - 1) = p_post.slice(t - 1) * fit_.A().t() * inv_sympd(p_pre.slice(t)); x_[trial].col(t - 1) = x_post.col(t - 1) + k_backfilt.slice(t - 1) * (x_[trial].col(t) - x_pre.col(t)); P_[trial].slice(t - 1) = p_post.slice(t - 1) + k_backfilt.slice(t - 1) * (P_[trial].slice(t) - p_pre.slice(t)) * k_backfilt.slice(t - 1).t(); } // do the same for P_t_tm1 Matrix id(n_x_, n_x_, fill::eye); P_t_tm1_[trial].slice(n_t_[trial] - 1) = (id - k_e * fit_.C()) * fit_.A() * p_post.slice(n_t_[trial] - 2); for (size_t t = (n_t_[trial] - 1); t \u0026gt; 1; t--) { P_t_tm1_[trial].slice(t - 1) = p_post.slice(t - 1) * k_backfilt.slice(t - 2).t() + k_backfilt.slice(t - 1) * (P_t_tm1_[trial].slice(t) - fit_.A() * p_post.slice(t - 1)) * k_backfilt.slice(t - 2).t(); } // finally, get smoothed estimate of output for (size_t t = 0; t \u0026lt; n_t_[trial]; t++) { fit_.h(y_[trial], x_[trial], t); } // samps loop } // trial loop } // Smooth // template \u0026lt;typename Fit\u0026gt; // void EM\u0026lt;Fit\u0026gt;::RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) { // // predict covar // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + fit_.Q(); // // update Ke // Ke = P_pre.slice(t) * fit_.C().t() * // inv_sympd(fit_.C() * P_pre.slice(t) * fit_.C().t() + fit_.R()); // // update cov // // Reference: Ghahramani et Hinton (1996) // P_post.slice(t) = P_pre.slice(t) - Ke * fit_.C() * P_pre.slice(t); // // // n.b. for poisson : // // P_pre.slice(t) = fit_.A() * P_post.slice(t - 1) * fit_.A().t() + // fit_.Q(); // // // update cov // // P_post.slice(t) = pinv(pinv(P_pre.slice(t)) + fit_.C().t() * diag_y_ * // // fit_.C()); // // // update Ke // // Ke = P_post.slice(t) * fit_.C(); // } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Expectation(bool force_common_initial) { // calculate the mean/cov of state needed for maximizing E[pr(z|theta)] Smooth(force_common_initial); // now get the various forms of sum(E[xx\u0026#39;]) needed // n.b. Going to start at t=1 rather than 0 bc most max terms need that. // so really \u0026#34;n_t_tot_\u0026#34; is (n_t_tot_-1) n_t_tot_ = 0; sum_E_x_t_x_t_ = Matrix(n_x_, n_x_, fill::zeros); sum_E_xu_tm1_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); sum_E_xu_t_xu_tm1_ = Matrix(n_x_ + n_u_, n_x_ + n_u_, fill::zeros); Vector xu_tm1(n_x_ + n_u_, fill::zeros); Vector xu_t(n_x_ + n_u_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { // ------------ sum_E_x_t_x_t ------------ sum_E_x_t_x_t_ += x_[trial].col(t) * x_[trial].col(t).t(); sum_E_x_t_x_t_ += P_[trial].slice(t); // ------------ sum_E_xu_tm1_xu_tm1 ------------ xu_tm1 = join_vert(x_[trial].col(t - 1), u_.at(trial).col(t - 1)); sum_E_xu_tm1_xu_tm1_ += xu_tm1 * xu_tm1.t(); sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t - 1); // ------------ sum_E_xu_t_xu_tm1 ------------ xu_t = join_vert(x_[trial].col(t), u_.at(trial).col(t)); sum_E_xu_t_xu_tm1_ += xu_t * xu_tm1.t(); sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_t_tm1_[trial].slice(t); n_t_tot_ += 1; } // time } // trial } // Expectation template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Maximization(bool calc_dynamics, bool calc_Q, bool calc_init, bool calc_output, bool calc_measurement) { if (calc_output) { MaximizeOutput(); } if (calc_measurement) { MaximizeMeasurement(); } if (calc_dynamics) { MaximizeDynamics(); } if (calc_Q) { MaximizeQ(); } if (calc_init) { MaximizeInitial(); } } // Maximization template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeDynamics() { // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) Matrix ab = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1) * inv_sympd(sum_E_xu_tm1_xu_tm1_); fit_.set_A(ab.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_B(ab.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1)); std::cout \u0026lt;\u0026lt; \u0026#34;A_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.A()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.B()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeQ() { // // Shumway, Stoffer (1982); Ghahgramani, Hinton (1996) // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix q = sum_E_x_t_x_t_ - sum_e_x_t_xu_tm1 * // inv_sympd(sum_E_xu_tm1_xu_tm1_) * // sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // this way is same as above iff dynamics were just updated: // View sum_e_x_t_xu_tm1 = // sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ + n_u_ - 1); // Matrix ab = arma::join_horiz(fit_.A(), fit_.B()); // Matrix q = sum_E_x_t_x_t_ - ab * sum_e_x_t_xu_tm1.t(); // q /= n_t_tot_; // From scratch method: // Q is covariance of the error between state and dynamics-predicted state // (aka process noise) // Q* = E[(x_t - Ax_{t-1} - Bu_{t-1})*(x_t - Ax_{t-1} - Bu_{t-1})\u0026#39;] // t-1 terms: View sum_e_x_tm1_x_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_u_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(n_x_, n_x_, n_x_ + n_u_ - 1, n_x_ + n_u_ - 1); View sum_e_x_tm1_u_tm1 = sum_E_xu_tm1_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); // t, t-1 terms: View sum_e_x_t_x_tm1 = sum_E_xu_t_xu_tm1_.submat(0, 0, n_x_ - 1, n_x_ - 1); View sum_e_x_t_u_tm1 = sum_E_xu_t_xu_tm1_.submat(0, n_x_, n_x_ - 1, n_x_ + n_u_ - 1); Matrix q = sum_E_x_t_x_t_; q += fit_.A() * sum_e_x_tm1_x_tm1 * fit_.A().t(); q -= sum_e_x_t_x_tm1 * fit_.A().t(); q -= fit_.A() * sum_e_x_t_x_tm1.t(); // input-related terms: q += fit_.B() * sum_e_u_tm1_u_tm1 * fit_.B().t(); q -= sum_e_x_t_u_tm1 * fit_.B().t(); q -= fit_.B() * sum_e_x_t_u_tm1.t(); q += fit_.A() * sum_e_x_tm1_u_tm1 * fit_.B().t(); q += fit_.B() * sum_e_x_tm1_u_tm1.t() * fit_.A().t(); q /= n_t_tot_; fit_.set_Q(q); std::cout \u0026lt;\u0026lt; \u0026#34;Q_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.Q()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // std::cout \u0026lt;\u0026lt; \u0026#34;Q_new: \\n\u0026#34; \u0026lt;\u0026lt; fit_.Q() \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeInitial() { Vector x0 = fit_.x0(); x0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { x0 += x_[trial].col(0); } x0 /= z_.size(); std::cout \u0026lt;\u0026lt; \u0026#34;x0_new[0]: \u0026#34; \u0026lt;\u0026lt; x0[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // always recalc P0 even if the initial state is fixed (at zero, for // example) Matrix e_var(n_x_, n_x_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { e_var += (x_[trial].col(0) - x0) * (x_[trial].col(0) - x0).t(); } e_var /= z_.size(); // go ahead and subtract x0*x0\u0026#39; so don\u0026#39;t have to below. e_var -= x0 * x0.t(); // To get P0, going to get initial P_ per trial and average. // (which might be wrong, but need a single number) Matrix p0 = fit_.P0(); p0.zeros(); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { p0 += (x_[trial].col(0) * x_[trial].col(0).t()) + P_[trial].slice(0) + e_var; } p0 /= z_.size(); fit_.set_P0(p0); std::cout \u0026lt;\u0026lt; \u0026#34;P0_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.P0()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeOutput() { // solve for C+d: Matrix sum_zx(n_y_, n_x_ + 1, fill::zeros); Vector x1(n_x_ + 1, fill::zeros); x1[n_x_] = 1.0; // augment with one to solve for bias Matrix sum_e_x1_x1(n_x_ + 1, n_x_ + 1, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { x1.subvec(0, n_x_ - 1) = x_[trial].col(t); sum_zx += z_.at(trial).col(t) * x1.t(); sum_e_x1_x1 += x1 * x1.t(); sum_e_x1_x1.submat(0, 0, n_x_ - 1, n_x_ - 1) += P_[trial].slice(t); } } Matrix cd = sum_zx * inv_sympd(sum_e_x1_x1); fit_.set_C(cd.submat(0, 0, n_y_ - 1, n_x_ - 1)); fit_.set_d(vectorise(cd.submat(0, n_x_, n_y_ - 1, n_x_))); std::cout \u0026lt;\u0026lt; \u0026#34;C_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.C()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.d()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::MaximizeMeasurement() { // Solve for measurement noise covar size_t n_t_tot = 0; // Ghahgramani, Hinton 1996: Matrix sum_zz(n_y_, n_y_, fill::zeros); Matrix sum_yz(n_y_, n_y_, fill::zeros); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { for (size_t t = 1; t \u0026lt; n_t_[trial]; t++) { sum_zz += z_.at(trial).col(t) * z_.at(trial).col(t).t(); // Use Cnew: sum_yz += (fit_.C() * x_[trial].col(t) + fit_.d()) * z_.at(trial).col(t).t(); n_t_tot += 1; } } fit_.set_R((sum_zz - sum_yz) / n_t_tot); std::cout \u0026lt;\u0026lt; \u0026#34;R_new[0]: \u0026#34; \u0026lt;\u0026lt; fit_.R()[0] \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } template \u0026lt;typename Fit\u0026gt; void EM\u0026lt;Fit\u0026gt;::Reset() { // reset to initial conditions for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { x_[trial].col(0) = fit_.x0(); P_[trial].slice(0) = fit_.P0(); y_[trial].col(0) = fit_.C() * x_[trial].col(0) + fit_.d(); } } template \u0026lt;typename Fit\u0026gt; Vector EM\u0026lt;Fit\u0026gt;::UpdateTheta() { // TODO(mfbolus): This should include n_y_ more params for d. size_t n_params = 3 * n_x_ * n_x_ + n_x_ * n_u_ + n_x_ + n_y_ * n_x_ + n_y_; if (fit_.R().n_elem \u0026gt; 0) { n_params += n_y_ * n_y_; } Vector theta(n_params); size_t idx_start = 0; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.A()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_u_ - 1) = vectorise(fit_.B()); idx_start += n_x_ * n_u_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.Q()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_x_ - 1) = vectorise(fit_.x0()); idx_start += n_x_; theta.subvec(idx_start, idx_start + n_x_ * n_x_ - 1) = vectorise(fit_.P0()); idx_start += n_x_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ * n_x_ - 1) = vectorise(fit_.C()); idx_start += n_y_ * n_x_; theta.subvec(idx_start, idx_start + n_y_ - 1) = vectorise(fit_.d()); idx_start += n_y_; if (fit_.R().n_elem \u0026gt; 0) { theta.subvec(idx_start, idx_start + n_y_ * n_y_ - 1) = vectorise(fit_.R()); } return theta; } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":54,"href":"/docs/api/files/lds__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_fit_ssid.h # subspace identification More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SSID Detailed Description # This file declares and partially defines a template type by which LDS models are fit by a subspace identification (SSID) algorithm ([lds::SSID](/docs/api/classes/classlds_1_1_s_s_i_d/)\u0026lt;Fit\u0026gt;).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_fit_ssid.h - SSID Fit ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_FIT_SSID_H #define LDSCTRLEST_LDS_FIT_SSID_H #include \u0026#34;lds_fit.h\u0026#34; namespace lds { template \u0026lt;typename Fit\u0026gt; class SSID { static_assert(std::is_base_of\u0026lt;lds::Fit, Fit\u0026gt;::value, \u0026#34;Fit must be derived from lds::Fit type.\u0026#34;); public: SSID() = default; SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d = Vector(1).fill(-kInf)); std::tuple\u0026lt;Fit, Vector\u0026gt; Run(SSIDWt ssid_wt); std::tuple\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; ReturnData() { auto tuple = std::make_tuple(std::move(u_), std::move(z_)); u_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); z_ = UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;(); return tuple; } protected: void CalcD(data_t t_silence = 0.1, data_t thresh_silence = 0.001); void CreateHankelDataMat(); virtual void DecomposeData() = 0; void CalcSVD(SSIDWt wt); void Solve(data_t wt_dc); void RecomputeExtObs(); // input/output training data UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u_; UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z_; Matrix D_; Fit fit_; Matrix g_dc_; data_t dt_{}; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; size_t n_h_{}; size_t n_trials_{}; std::vector\u0026lt;size_t\u0026gt; n_t_; size_t n_t_tot_{}; Matrix L_; Vector s_; Matrix ext_obs_t_; }; template \u0026lt;typename Fit\u0026gt; SSID\u0026lt;Fit\u0026gt;::SSID(size_t n_x, size_t n_h, data_t dt, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; u_train, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026amp;\u0026amp; z_train, const Vector\u0026amp; d) { // check input/output data dimensions are consistent if (z_train.size() != u_train.size()) { throw std::runtime_error( \u0026#34;I/O training data have different number of trials.\u0026#34;); } n_trials_ = u_train.size(); n_t_tot_ = 0; n_t_ = std::vector\u0026lt;size_t\u0026gt;(n_trials_); for (size_t trial = 0; trial \u0026lt; n_trials_; trial++) { if (z_train.at(trial).n_cols != u_train.at(trial).n_cols) { throw std::runtime_error( \u0026#34;I/O training data have different number of time steps.\u0026#34;); } n_t_[trial] = u_train.at(trial).n_cols; n_t_tot_ += n_t_[trial]; } dt_ = dt; n_x_ = n_x; n_u_ = u_train.at(0).n_rows; n_y_ = z_train.at(0).n_rows; n_h_ = n_h; // dimensionality check for eventual block-hankel data matrix size_t len = n_t_tot_ - 2 * n_h_ + 1; if (len \u0026lt; (2 * n_h_ * (n_u_ + n_y_))) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;Dataset problem! More rows than columns in block-hankel data \u0026#34; \u0026#34;matrix: 2*(n_u+n_y)*n_h \u0026gt; data-length! Need higher data-length or \u0026#34; \u0026#34;lower n_h.\u0026#34;; throw std::runtime_error(ss.str()); } fit_ = Fit(n_u_, n_x_, n_y_, dt_); u_ = std::move(u_train); z_ = std::move(z_train); if (!d.is_finite() || (d.n_rows != n_y_)) { // TODO(mfbolus): implement least-square solution for impulse response with // a second input of ones. Data-driven way of accounting for offset *not* // driven by an input. // // For now, calculate output bias (d) as the // output wherever the stimulus has not been on for some amount of time. // convolve u with rectangle and take all samples. This is a reasonable // approach, since often when autonomous systems are fit (i.e., systems with // no input), they will subtract off the mean of the output. This // essentially amounts to setting output bias to the mean of the output when // there is no stimulation. data_t t_silence = 0.1; data_t thresh_silence = 0.001; CalcD(t_silence, thresh_silence); } else { fit_.set_d(d); } } template \u0026lt;typename Fit\u0026gt; std::tuple\u0026lt;Fit, Vector\u0026gt; SSID\u0026lt;Fit\u0026gt;::Run(SSIDWt ssid_wt) { // the weight on minimizing dc I/O gain only works for gaussian, // and hopefully not necessary with appropriate dataset. data_t wt_dc = 0; // std::cout \u0026lt;\u0026lt; \u0026#34;creating hankel mat\\n\u0026#34;; CreateHankelDataMat(); // std::cout \u0026lt;\u0026lt; \u0026#34;decomposing data\\n\u0026#34;; DecomposeData(); // std::cout \u0026lt;\u0026lt; \u0026#34;calculating svd\\n\u0026#34;; CalcSVD(ssid_wt); // std::cout \u0026lt;\u0026lt; \u0026#34;solving for params\\n\u0026#34;; Solve(wt_dc); // std::cout \u0026lt;\u0026lt; \u0026#34;fin\\n\u0026#34;; return std::make_tuple(fit_, s_); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcD(data_t t_silence, data_t thresh_silence) { Vector d(z_.at(0).n_rows, fill::zeros); Vector win(static_cast\u0026lt;size_t\u0026gt;(t_silence / dt_), fill::ones); Vector sum_z_silence(n_y_, fill::zeros); size_t n_silence(0); for (size_t trial = 0; trial \u0026lt; u_.size(); trial++) { // find silent samples // start by convolving with Vector sum_u = vectorise(sum(abs(u_.at(trial)), 0)); Vector u_conv = conv(sum_u, win, \u0026#34;same\u0026#34;); // get only the samples that are silent... arma::uvec ubi_silence = find(u_conv \u0026lt;= thresh_silence); if (ubi_silence.n_elem \u0026gt; 0) { sum_z_silence += arma::sum(z_.at(trial).cols(ubi_silence), 1); n_silence += ubi_silence.n_elem; } } if (n_silence \u0026gt; 0) { d = sum_z_silence / n_silence; } fit_.set_d(d); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat() { // temporary copy of data Matrix z(n_y_, n_t_tot_, fill::zeros); Matrix u(n_u_, n_t_tot_, fill::zeros); size_t so_far(0); for (size_t trial = 0; trial \u0026lt; z_.size(); trial++) { z.submat(0, so_far, n_y_ - 1, so_far + n_t_.at(trial) - 1) = z_.at(trial); u.submat(0, so_far, n_u_ - 1, so_far + n_t_.at(trial) - 1) = u_.at(trial); so_far += n_t_.at(trial); } // remove output bias z.each_col() -= fit_.d(); // calculate I/O gain @ DC while data in convenient form g_dc_ = z * pinv(u); // std::cout \u0026lt;\u0026lt; \u0026#34;G0_data = \u0026#34; \u0026lt;\u0026lt; g_dc_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // create hankel data matrix size_t len = z.n_cols - 2 * n_h_ + 1; // data length in hankel mat // block-hankel data matrix D_ = Matrix(2 * n_h_ * (n_u_ + n_y_), len, fill::zeros); // past input auto u_p = D_.submat(0, 0, n_h_ * n_u_ - 1, len - 1); // future input auto u_f = D_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, len - 1); // past output auto y_p = D_.submat(2 * n_h_ * n_u_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, len - 1); // future output auto y_f = D_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, len - 1); size_t idx = 0; for (size_t k = 0; k \u0026lt; len; k++) { idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { u_p.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { u_f.col(k).subvec(idx, idx + n_u_ - 1) = u.col(kk); idx += n_u_; } idx = 0; for (size_t kk = k; kk \u0026lt; (n_h_ + k); kk++) { y_p.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } idx = 0; for (size_t kk = (n_h_ + k); kk \u0026lt; (2 * n_h_ + k); kk++) { y_f.col(k).subvec(idx, idx + n_y_ - 1) = z.col(kk); idx += n_y_; } } D_ /= sqrt(static_cast\u0026lt;data_t\u0026gt;(len)); } // template \u0026lt;typename Fit\u0026gt; // void SSID\u0026lt;Fit\u0026gt;::DecomposeData() { // // do LQ decomp instead of calculating covariance expensive way // // Note that \u0026#34;R\u0026#34; in van Overschee is lower-triangular (L), not \u0026#34;R\u0026#34; in QR // // decomp. Very confusing. // Matrix q_t; // lq(L_, q_t, D_); // // van Overschee zeros out the other elements. // L_ = trimatl(L_); // } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::CalcSVD(SSIDWt wt) { // submats that will be needed: auto R_14_14 = L_.submat(0, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_14 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_11_13 = L_.submat(0, 0, n_h_ * n_u_ - 1, n_h_ * (2 * n_u_) - 1); auto R_23_13 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, 2 * n_h_ * n_u_ - 1); auto R_44_14 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_44_13 = L_.submat(2 * n_u_ * n_h_, 0, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_) - 1); auto R_44 = L_.submat(2 * n_u_ * n_h_, 2 * n_u_ * n_h_, n_h_ * (2 * n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); Matrix Lup_Luf_Lyp = R_56_14 * pinv(R_14_14); auto Lup = Lup_Luf_Lyp.submat(0, 0, n_h_ * n_y_ - 1, n_h_ * n_u_ - 1); auto Luf = Lup_Luf_Lyp.submat(0, n_h_ * n_u_, n_h_ * n_y_ - 1, 2 * n_h_ * n_u_ - 1); auto Lyp = Lup_Luf_Lyp.submat(0, 2 * n_h_ * n_u_, n_h_ * n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) - 1); // aka: R_f Matrix R_56_16 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, L_.n_cols - 1); // from van Overschee subid.m: // Rf = R((2*m+l)*i+1:2*(m+l)*i,:); % Future outputs Matrix U; Matrix V; switch (wt) { case kSSIDNone: { // No weighting. (what van Overschee calls \u0026#34;N4SID\u0026#34;) Matrix O_k_sans_Qt = Lup * R_11_14 + Lyp * R_44_14; arma::svd(U, s_, V, O_k_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDMOESP: { // MOESP weighting // This is what they use in the \u0026#34;robust\u0026#34; algorithm van Overschee, de Moor // 1996 Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); svd(U, s_, V, O_k_ortho_Uf_sans_Qt, \u0026#34;std\u0026#34;); } break; case kSSIDCVA: { // CVA weighting // See van Overschee\u0026#39;s matlab code (subid.m): // https://www.mathworks.com/matlabcentral/fileexchange/2290-subspace-identification-for-linear-systems Matrix Pi = Matrix(2 * n_h_ * n_u_, 2 * n_h_ * n_u_, fill::eye) - R_23_13.t() * inv(R_23_13 * R_23_13.t()) * R_23_13; Matrix O_k_ortho_Uf_sans_Qt = join_horiz((Lup * R_11_13 + Lyp * R_44_13) * Pi, Lyp * R_44); Matrix inv_w1; Matrix qt1; lq(inv_w1, qt1, R_56_16); // lq decomp of R_f (future output data) inv_w1 = trimatl(inv_w1); inv_w1 = inv_w1.submat(0, 0, n_y_ * n_h_ - 1, n_y_ * n_h_ - 1); Matrix w_o_w = arma::solve( inv_w1, O_k_ortho_Uf_sans_Qt); // alternatively // pinv(inv_W1)*O_k_ortho_Uf_sans_Qt svd(U, s_, V, w_o_w, \u0026#34;std\u0026#34;); U = inv_w1 * U; break; } } // Truncate to model order (heart of ssid method) auto s_hat = s_.subvec(0, n_x_ - 1); Matrix diag_sqrt_s = diagmat(sqrt(s_hat)); auto u_hat = U.submat(0, 0, U.n_rows - 1, n_x_ - 1); // get extended observability and controllability mats ext_obs_t_ = u_hat * diag_sqrt_s; // extended observability matrix } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::Solve(data_t wt_dc) { // required submats auto R_56_14 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + 2 * n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) - 1); auto R_23_15 = L_.submat(n_h_ * n_u_, 0, 2 * n_h_ * n_u_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_66_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_) + n_y_, 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_55_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); auto R_56_15 = L_.submat(n_h_ * (2 * n_u_ + n_y_), 0, 2 * n_h_ * (n_u_ + n_y_) - 1, n_h_ * (2 * n_u_ + n_y_) + n_y_ - 1); // Solve for params using appropriate algorithm: // robust deterministic/stochastic algorithm in van Overschee 1996 // algorithm that the authors say \u0026#34;works\u0026#34; in practice. auto ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix // This is what textbook (1996) says: // // Matrix Tr = join_vert(pinv(ext_obs_t_) * R_56_15, R_23_15); // // HOWEVER, do not know why but have to fill the last place with zeros like // authors\u0026#39; matlab implementation (see `subid.m`) // Otherwise, get ridiculous covariances (although A,C estimates are close to // same...) Matrix Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); Matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Matrix S = Tl * pinv(Tr); // Use alternative in van Overschee 1996, p. 129. Apparently, should ensure // stability. fit_.set_C(ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1)); Matrix ext_obs_t_p1 = join_vert( ext_obs_t_.submat(n_y_, 0, ext_obs_t_.n_rows - 1, ext_obs_t_.n_cols - 1), Matrix(n_y_, ext_obs_t_.n_cols, fill::zeros)); fit_.set_A(pinv(ext_obs_t_) * ext_obs_t_p1); // At this point, van Overschee \u0026amp; de Moor suggest re-calculating ext_obs_t_, // ext_obs_tm1 from (A, C) because it was just an approximation. This is RecomputeExtObs(); ext_obs_tm1 = ext_obs_t_.submat( 0, 0, ext_obs_t_.n_rows - 1 - n_y_, ext_obs_t_.n_cols - 1); // extended observability matrix Tl = join_vert(pinv(ext_obs_tm1) * R_66_15, R_55_15); Tr = join_vert( join_horiz(pinv(ext_obs_t_) * R_56_14, Matrix(n_x_, n_y_, fill::zeros)), R_23_15); S = Tl * pinv(Tr); Matrix Lcurly = S.submat(0, 0, n_x_ + n_y_ - 1, n_x_ - 1) * pinv(ext_obs_t_); Matrix Mcurly = pinv(ext_obs_tm1); Matrix Pcurly = Tl - Lcurly * R_56_15; Vector Pvec = vectorise(Pcurly); Matrix Qcurly = R_23_15; // Identify [D; B], assuming D=0 and ensuring DC gain is correct Matrix sum_QcurlyT_kron_Ncurly( (n_h_ * (2 * n_u_ + n_y_) + n_y_) * (n_y_ + n_x_), n_u_ * (n_y_ + n_x_), fill::zeros); Matrix eye_ext_obs_tm1(n_y_ + ext_obs_tm1.n_rows, n_y_ + ext_obs_tm1.n_cols, fill::eye); eye_ext_obs_tm1.submat(n_y_, n_y_, eye_ext_obs_tm1.n_rows - 1, eye_ext_obs_tm1.n_cols - 1) = ext_obs_tm1; // van Overschee (1996) p. 126 Matrix N1_Tl = -Lcurly; N1_Tl.submat(0, 0, n_x_ - 1, N1_Tl.n_cols - 1) += join_horiz(Matrix(n_x_, n_y_, fill::zeros), Mcurly); N1_Tl.submat(n_x_, 0, n_x_ + n_y_ - 1, n_y_ - 1) += Matrix(n_y_, n_y_, fill::eye); Matrix Nk_Tl(N1_Tl.n_rows, N1_Tl.n_cols, fill::zeros); Matrix N_k; for (size_t k = 0; k \u0026lt; n_h_; k++) { auto Qcurly_k = Qcurly.submat(n_u_ * k, 0, n_u_ * (k + 1) - 1, Qcurly.n_cols - 1); Nk_Tl.zeros(); Nk_Tl.submat(0, 0, n_x_ + n_y_ - 1, Nk_Tl.n_cols - k * n_y_ - 1) = N1_Tl.submat(0, k * n_y_, N1_Tl.n_rows - 1, N1_Tl.n_cols - 1); N_k = Nk_Tl * eye_ext_obs_tm1; sum_QcurlyT_kron_Ncurly += kron(Qcurly_k.t(), N_k); } Matrix err_vec; if (wt_dc \u0026gt; 0) { // Constraints enforced by weighted least squares // // Reference: // // Privara S, ..., Ferkl L_. (2010) Subspace Identification of Poorly // Excited Industrial Systems. Conference in Decision and Control. // constraint 1: assume D=0 --\u0026gt; remove the components for Dvec (this is // actually a hard constraint in that it ignores D) Matrix sum_QcurlyT_kron_Ncurly_db = sum_QcurlyT_kron_Ncurly; sum_QcurlyT_kron_Ncurly = Matrix(sum_QcurlyT_kron_Ncurly_db.n_rows, n_x_ * n_u_); size_t kkk = 0; for (size_t k = 1; k \u0026lt; (n_u_ + 1); k++) { size_t start_idx = k * (n_y_ + n_x_) - n_x_; for (size_t kk = 0; kk \u0026lt; n_x_; kk++) { sum_QcurlyT_kron_Ncurly.col(kkk) = sum_QcurlyT_kron_Ncurly_db.col(start_idx + kk); kkk++; } } // constraint 2: Make sure DC I/O gain is correct Matrix b_to_g0 = fit_.C() * inv(Matrix(n_x_, n_x_, fill::eye) - fit_.A()); Matrix Pvec_Gvec = join_vert(Pvec, vectorise(g_dc_)); Matrix eye_kron_b_to_g0 = kron(Matrix(n_u_, n_u_, fill::eye), b_to_g0); Matrix sum_QcurlyT_kron_Ncurly_b_to_g0 = join_vert(sum_QcurlyT_kron_Ncurly, eye_kron_b_to_g0); // WEIGHTED LS // Important in practice because I care a lot about at least getting the DC // gain correct. Put x weight on minimizing error at DC, relative to others Matrix w(sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, sum_QcurlyT_kron_Ncurly_b_to_g0.n_rows, fill::eye); // Make weight on minimizing DC error immense so at least that // should be nailed. size_t start_row = sum_QcurlyT_kron_Ncurly.n_rows; size_t start_col = sum_QcurlyT_kron_Ncurly.n_rows; size_t stop_row = w.n_rows - 1; size_t stop_col = w.n_cols - 1; // w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc*N;// scale // weight with data length? w.submat(start_row, start_col, stop_row, stop_col) *= wt_dc; Vector b_vec = inv(sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * sum_QcurlyT_kron_Ncurly_b_to_g0) * sum_QcurlyT_kron_Ncurly_b_to_g0.t() * w * Pvec_Gvec; fit_.set_B(Matrix(b_vec.memptr(), n_x_, n_u_)); // Calculate residuals and their cov. // Because I\u0026#39;ve added constraints, I need to re-calculate the right term // with b_vec instead of how van Overschee do in final algorithm. err_vec = Pvec - sum_QcurlyT_kron_Ncurly * b_vec; } else { // default way: *no* constraint on G0 or D=0 Vector db_vec = pinv(sum_QcurlyT_kron_Ncurly) * Pvec; // TODO(mfbolus) n.b., this gets thrown away... // Matrix D = Matrix(db_vec.memptr(), n_y_, n_u_); fit_.set_B(Matrix(db_vec.memptr() + (n_u_ * n_y_), n_x_, n_u_)); err_vec = Pvec - sum_QcurlyT_kron_Ncurly * db_vec; } // Matrix err = Matrix(err_vec.memptr(), Pcurly.n_rows, Pcurly.n_cols); // TODO(mfbolus): Something is wrong with the error calculation above. // Use the way van overschee does it in `subid.m` // WARNING: this ignores any above constraints, so Q, R will be approximate... Matrix err = Tl - S * Tr; Matrix cov_err = err * err.t(); fit_.set_Q(cov_err.submat(0, 0, n_x_ - 1, n_x_ - 1)); fit_.set_R(cov_err.submat(n_x_, n_x_, n_x_ + n_y_ - 1, n_x_ + n_y_ - 1)); } template \u0026lt;typename Fit\u0026gt; void SSID\u0026lt;Fit\u0026gt;::RecomputeExtObs() { ext_obs_t_.submat(0, 0, n_y_ - 1, ext_obs_t_.n_cols - 1) = fit_.C(); for (size_t k = 2; k \u0026lt; (n_h_ + 1); k++) { ext_obs_t_.submat((k - 1) * n_y_, 0, k * n_y_ - 1, ext_obs_t_.n_cols - 1) = ext_obs_t_.submat((k - 2) * n_y_, 0, (k - 1) * n_y_ - 1, ext_obs_t_.n_cols - 1) * fit_.A(); } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":55,"href":"/docs/api/files/lds__fit_8h/","title":"ldsCtrlEst_h/lds_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_fit.h # LDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::Fit LDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a linear dynamical system. It is expounded upon by variants with Gaussian and Poisson observation assumptions for fitting.\nSource code # //===-- ldsCtrlEst_h/lds_fit.h - Fit Type for LDS ---------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDS_FIT_HPP #define LDS_FIT_HPP // namespace #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); virtual ~Fit() = default; // get methods size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; Q() const { return Q_; }; const Vector\u0026amp; x0() const { return x0_; }; const Matrix\u0026amp; P0() const { return P0_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; // gets measurement noise virtual const Matrix\u0026amp; R() const = 0; // set methods (e.g., seeding initial fit values) void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_m(const Vector\u0026amp; m) { Reassign(m_, m); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); ForceSymPD(Q_); }; virtual void set_R(const Matrix\u0026amp; R) = 0; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); ForceSymPD(P0_); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; View f(Matrix\u0026amp; x, const Matrix\u0026amp; u, size_t t) { x.col(t) = A_ * x.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x.col(t); }; View f(Matrix\u0026amp; x_pre, const Matrix\u0026amp; x_post, const Matrix\u0026amp; u, size_t t) { x_pre.col(t) = A_ * x_post.col(t - 1) + B_ * (g_ % u.col(t - 1)) + m_; return x_pre.col(t); }; virtual View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) = 0; protected: data_t dt_{}; // Dynamics Matrix A_; Matrix B_; Vector g_; Vector m_; Matrix Q_; // Output Matrix C_; Vector d_; Matrix R_; // initial conditions Vector x0_; Matrix P0_; size_t n_u_{}; size_t n_x_{}; size_t n_y_{}; }; } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":56,"href":"/docs/api/files/lds__gaussian__ctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_ctrl.h # GLDS Controller. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Controller Gaussian-observation Controller Type. Detailed Description # This file declares and partially defines the type for control of a gaussian-observation linear dynamical system (lds::gaussian::Controller). It inherits functionality from the underlying GLDS model type (lds::gaussian::System), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_ctrl.h - GLDS Controller ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_CTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_CTRL_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_gaussian_sys.h\u0026#34; // controller #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace gaussian { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); cx_ref_ = y_ref - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":57,"href":"/docs/api/files/lds__gaussian__fit__em_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_em.h # GLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitEM GLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_em.h - GLDS Fit (EM) ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":58,"href":"/docs/api/files/lds__gaussian__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit_ssid.h # GLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::FitSSID Subspace Identification (SSID) for GLDS. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by a subspace identification (SSID) algorithm (lds::gaussian::ssidFit_t).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer.\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit_ssid.h - GLDS Fit (SSID) --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_gaussian_fit.h\u0026#34; namespace lds { namespace gaussian { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; using SSID\u0026lt;Fit\u0026gt;::Run; private: using SSID\u0026lt;Fit\u0026gt;::CreateHankelDataMat; using SSID\u0026lt;Fit\u0026gt;::CalcSVD; using SSID\u0026lt;Fit\u0026gt;::Solve; void DecomposeData() override; void SolveVanOverschee(); }; } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":59,"href":"/docs/api/files/lds__gaussian__fit_8h/","title":"ldsCtrlEst_h/lds_gaussian_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_fit.h # GLDS fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::Fit GLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_fit.h - Fit Type for GLDS -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_FIT_H #define LDSCTRLEST_LDS_GAUSSIAN_FIT_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // fit type #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace gaussian { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt); const Matrix\u0026amp; R() const override { return R_; }; void set_R(const Matrix\u0026amp; R) override { Reassign(R_, R); ForceSymPD(R_); }; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = C_ * x.col(t) + d_; return y.col(t); }; }; }; // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":60,"href":"/docs/api/files/lds__gaussian__sctrl_8h/","title":"ldsCtrlEst_h/lds_gaussian_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sctrl.h # GLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::SwitchedController Gaussian-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sctrl.h - Switched Controller -*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H #define LDSCTRLEST_LDS_GAUSSIAN_SCTRL_H // controller type #include \u0026#34;lds_gaussian_ctrl.h\u0026#34; // switched controller #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace gaussian { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); cx_ref_ = y_ref - sys_.d(); } // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; }; // SwitchedController } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":61,"href":"/docs/api/files/lds__gaussian__sys_8h/","title":"ldsCtrlEst_h/lds_gaussian_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian_sys.h # GLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Classes # Name class lds::gaussian::System Gaussian LDS Type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems ([lds::gaussian::System](/docs/api/classes/classlds_1_1gaussian_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian_sys.h - GLDS ------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_SYS_H #define LDSCTRLEST_LDS_GAUSSIAN_SYS_H // namespace #include \u0026#34;lds_gaussian.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; namespace lds { namespace gaussian { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0, data_t r0 = kDefaultR0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; // get methods const Matrix\u0026amp; R() const { return R_; }; // set methods void set_Q(const Matrix\u0026amp; Q) { lds::System::set_Q(Q); do_recurse_Ke_ = true; } void set_R(const Matrix\u0026amp; R) { Reassign(R_, R); do_recurse_Ke_ = true; }; void set_Ke(const Matrix\u0026amp; Ke) { Reassign(Ke_, Ke); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void set_Ke_m(const Matrix\u0026amp; Ke_m) { Reassign(Ke_m_, Ke_m); // if users have set Ke, they must not want to calculate it online. do_recurse_Ke_ = false; }; void Print(); protected: void h() override { cx_ = C_ * x_; y_ = cx_ + d_; }; Vector h_(Vector x) override { return C_ * x + d_; }; void RecurseKe() override; // Gaussian-output-specific Matrix R_; bool do_recurse_Ke_{}; }; // System } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":62,"href":"/docs/api/files/lds__gaussian_8h/","title":"ldsCtrlEst_h/lds_gaussian.h","section":"Files","content":" ldsCtrlEst_h/lds_gaussian.h # glds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::gaussian Linear Dynamical Systems with Gaussian observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Gaussian observations ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)).\nSource code # //===-- ldsCtrlEst_h/lds_gaussian.h - LDS with Gaussian Output --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_GAUSSIAN_H #define LDSCTRLEST_LDS_GAUSSIAN_H // namespace #include \u0026#34;lds.h\u0026#34; namespace lds { namespace gaussian { // insert any Gaussian-specific things here... } // namespace gaussian } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":63,"href":"/docs/api/files/lds__poisson__ctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_ctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_ctrl.h # PLDS controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Controller PLDS Controller Type. Detailed Description # This file declares and partially defines the type for feedback control of a Poisson-output linear dynamical system ([lds::poisson::Controller](/docs/api/classes/classlds_1_1poisson_1_1_controller/)). It inherits functionality from the underlying PLDS model type ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)), including state estimation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_ctrl.h - PLDS Controller -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_CTRL_H #define LDSCTRLEST_LDS_POISSON_CTRL_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system type #include \u0026#34;lds_poisson_sys.h\u0026#34; // control type #include \u0026#34;lds_ctrl.h\u0026#34; namespace lds { namespace poisson { class Controller : public lds::Controller\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_, y_ref); lds::Limit(y_ref_, kYRefLb, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_sys; using lds::Controller\u0026lt;System\u0026gt;::set_g_design; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_Kc; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::set_Kc_u; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; private: constexpr static const data_t kYRefLb = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":64,"href":"/docs/api/files/lds__poisson__fit__em_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_em.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_em.h # PLDS E-M fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitEM PLDS E-M Fit Type. Detailed Description # This file declares and partially defines a type by which Gaussian-output LDS models are fit by the expectation-maximization (EM) algorithm (lds::gaussian::emFit_t).\nReferences: [1] Shumway RH, Stoffer DS. (1982) An Approach to Time Series Smoothing and Forecasting Using the EM Algorithm.Journal of Time Series Analysis 3(2).\n[2] Ghahramani Z, Hinton GE. (1996) Parameter Estimation for Linear Dynamical Systems. Technical Report CRG-TR-96-2.\n[3] Smith A, Brown E. (2003) Estimating a State-Space Model from Point Process Observations. Neural Computation.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_em.h - PLDS Fit (EM) -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_EM_H #define LDSCTRLEST_LDS_POISSON_FIT_EM_H #include \u0026#34;lds_fit_em.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitEM : public EM\u0026lt;Fit\u0026gt; { public: using EM\u0026lt;Fit\u0026gt;::EM; private: void MaximizeOutput() override; void MaximizeMeasurement() override{}; void RecurseKe(Matrix\u0026amp; Ke, Cube\u0026amp; P_pre, Cube\u0026amp; P_post, size_t t) override; data_t NewtonSolveC(); void AnalyticalSolveD(); }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":65,"href":"/docs/api/files/lds__poisson__fit__ssid_8h/","title":"ldsCtrlEst_h/lds_poisson_fit_ssid.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit_ssid.h # PLDS SSID fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::FitSSID Subspace Identification (SSID) for PLDS. Detailed Description # This file declares and partially defines a type by which Poisson-output LDS models are fit by a subspace identification (SSID) algorithm ([lds::gaussian::FitSSID](/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/)).\nReferences: [1] van Overschee P, de Moore B. (1996) Subspace Identification for Linear Systems. Boston: Springer. [2] Buesing L, Macke JH, Sahani M. (2012) Spectral learning of linear dynamics from generalised-linear observations with application to neural population data. NIPS 25.\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit_ssid.h - PLDS Fit (SSID) ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_SSID_H #define LDSCTRLEST_LDS_POISSON_FIT_SSID_H #include \u0026#34;lds_fit_ssid.h\u0026#34; #include \u0026#34;lds_poisson_fit.h\u0026#34; namespace lds { namespace poisson { class FitSSID : public SSID\u0026lt;Fit\u0026gt; { public: using SSID\u0026lt;Fit\u0026gt;::SSID; private: void DecomposeData() override; void CalcCov(); void PoissonToGaussianMoments(); Matrix cov_; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":66,"href":"/docs/api/files/lds__poisson__fit_8h/","title":"ldsCtrlEst_h/lds_poisson_fit.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_fit.h # PLDS base fit type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::Fit PLDS Fit Type. Detailed Description # This file declares and partially defines the base fit type for a Gaussian-output linear dynamical system. Models are fit by either subspace identification (SSID) or expectation-maximization (EM).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_fit.h - Fit Type for PLDS ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_FIT_H #define LDSCTRLEST_LDS_POISSON_FIT_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // fit #include \u0026#34;lds_fit.h\u0026#34; namespace lds { namespace poisson { class Fit : public lds::Fit { public: Fit() = default; Fit(size_t n_u, size_t n_x, size_t n_y, data_t dt) : lds::Fit(n_u, n_x, n_y, dt){}; View h(Matrix\u0026amp; y, const Matrix\u0026amp; x, size_t t) override { y.col(t) = exp(C_ * x.col(t) + d_); return y.col(t); }; void set_R(const Matrix\u0026amp; R) override { std::cerr \u0026lt;\u0026lt; \u0026#34;WARNING: Cannot set R (R[0] = \u0026#34; \u0026lt;\u0026lt; R.at(0) \u0026lt;\u0026lt; \u0026#34;). No Gaussian measurement noise in Poisson observation model.\\n\u0026#34;; }; const Matrix\u0026amp; R() const override { return R_; }; }; }; // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":67,"href":"/docs/api/files/lds__poisson__sctrl_8h/","title":"ldsCtrlEst_h/lds_poisson_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sctrl.h # PLDS switched controller type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::SwitchedController Poisson-observation SwitchedController Type. Detailed Description # This file declares and partially defines the type for switched feedback control of a system approximated as multiple discrete Poisson-output linear dynamical systems (lds::poisson::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sctrl.h - Switched Controller --*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #define LDSCTRLEST_LDS_POISSON_SWITCHED_CTRL_H #include \u0026#34;lds_poisson_ctrl.h\u0026#34; #include \u0026#34;lds_sctrl.h\u0026#34; namespace lds { namespace poisson { class SwitchedController : public lds::SwitchedController\u0026lt;System\u0026gt; { public: void set_y_ref(const Vector\u0026amp; y_ref) override { Reassign(y_ref_,y_ref); lds::Limit(y_ref_, kYRefLB, lds::kInf); cx_ref_ = log(y_ref_) - sys_.d(); }; // make sure base class template methods available using lds::SwitchedController\u0026lt;System\u0026gt;::SwitchedController; using lds::SwitchedController\u0026lt;System\u0026gt;::Switch; using lds::SwitchedController\u0026lt;System\u0026gt;::Control; using lds::SwitchedController\u0026lt;System\u0026gt;::ControlOutputReference; using lds::SwitchedController\u0026lt;System\u0026gt;::sys; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::set_g_design; using lds::SwitchedController\u0026lt;System\u0026gt;::set_u_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_x_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_y_ref; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_inty; using lds::SwitchedController\u0026lt;System\u0026gt;::set_Kc_u; using lds::SwitchedController\u0026lt;System\u0026gt;::set_tau_awu; using lds::SwitchedController\u0026lt;System\u0026gt;::set_control_type; using lds::SwitchedController\u0026lt;System\u0026gt;::Reset; using lds::SwitchedController\u0026lt;System\u0026gt;::Print; private: constexpr static data_t kYRefLB = 1e-4; }; } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":68,"href":"/docs/api/files/lds__poisson__sys_8h/","title":"ldsCtrlEst_h/lds_poisson_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson_sys.h # PLDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Classes # Name class lds::poisson::System Poisson System type. Detailed Description # This file declares and partially defines the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems ([lds::poisson::System](/docs/api/classes/classlds_1_1poisson_1_1_system/)). It inherits functionality from the underlying linear dynamical system ([lds::System](/docs/api/classes/classlds_1_1_system/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson_sys.h - PLDS -------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_SYS_H #define LDSCTRLEST_LDS_POISSON_SYS_H // namespace #include \u0026#34;lds_poisson.h\u0026#34; // system #include \u0026#34;lds_sys.h\u0026#34; // needed for Poisson random number generation #include \u0026lt;random\u0026gt; namespace lds { namespace poisson { class System : public lds::System { public: System() = default; System(std::size_t n_u, std::size_t n_x, std::size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) override; protected: void h() override { cx_ = C_ * x_; y_ = exp(cx_ + d_); diag_y_.diag() = y_; }; Vector h_(Vector x) override { return exp(C_ * x + d_); }; void RecurseKe() override; private: // Poisson-output-specific Matrix diag_y_; std::poisson_distribution\u0026lt;size_t\u0026gt; pd_; }; // System } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":69,"href":"/docs/api/files/lds__poisson_8h/","title":"ldsCtrlEst_h/lds_poisson.h","section":"Files","content":" ldsCtrlEst_h/lds_poisson.h # plds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. lds::poisson Linear Dynamical Systems with Poisson observations. Detailed Description # This file declares and partially defines the namespace for linear dynamical systems with Poisson observations ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)).\nSource code # //===-- ldsCtrlEst_h/lds_poisson.h - LDS with Poisson Output ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_POISSON_H #define LDSCTRLEST_LDS_POISSON_H #include \u0026#34;lds.h\u0026#34; namespace lds { namespace poisson { // TODO(mfbolus): Not sure if defining these as static here makes the most // sense. Is there a downside to letting multiple poisson System objects share a // common random number generator? static std::random_device rd; static std::mt19937 rng = std::mt19937( rd()); } // namespace poisson } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":70,"href":"/docs/api/files/lds__sctrl_8h/","title":"ldsCtrlEst_h/lds_sctrl.h","section":"Files","content":" ldsCtrlEst_h/lds_sctrl.h # SwitchedController type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::SwitchedController SwitchedController Type. Detailed Description # This file declares the type for switched control of a system approximated as multiple discrete Gaussian-output linear dynamical systems (lds::gaussian::SwitchedController).\nSource code # //===-- ldsCtrlEst_h/lds_sctrl.h - Switched Controller ----------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SCTRL_H #define LDSCTRLEST_LDS_SCTRL_H #include \u0026#34;lds_ctrl.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; #include \u0026#34;lds_uniform_vecs.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class SwitchedController : public Controller\u0026lt;System\u0026gt; { public: SwitchedController() = default; SwitchedController(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); SwitchedController(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type = 0); void Switch(size_t idx, bool do_force_switch = false); void set_Kc(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc) { Kc_list_ = Kc; Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc) { Kc_list_ = std::move(Kc); Kc_ = Kc_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_inty) { Kc_inty_list_ = Kc_inty; Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_inty(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_inty) { Kc_inty_list_ = std::move(Kc_inty); Kc_inty_ = Kc_inty_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(const UniformMatrixList\u0026lt;\u0026gt;\u0026amp; Kc_u) { Kc_u_list_ = Kc_u; Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_Kc_u(UniformMatrixList\u0026lt;\u0026gt;\u0026amp;\u0026amp; Kc_u) { Kc_u_list_ = std::move(Kc_u); Kc_u_ = Kc_u_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(const UniformVectorList\u0026amp; g) { g_design_list_ = g; g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; void set_g_design(UniformVectorList\u0026amp;\u0026amp; g) { g_design_list_ = std::move(g); g_design_ = g_design_list_.at(0); // set to first if (idx_ != 0) { Switch(idx_, true); } }; // make sure base class template methods available using lds::Controller\u0026lt;System\u0026gt;::Controller; using lds::Controller\u0026lt;System\u0026gt;::Control; using lds::Controller\u0026lt;System\u0026gt;::ControlOutputReference; using lds::Controller\u0026lt;System\u0026gt;::sys; using lds::Controller\u0026lt;System\u0026gt;::Kc; using lds::Controller\u0026lt;System\u0026gt;::Kc_inty; using lds::Controller\u0026lt;System\u0026gt;::Kc_u; using lds::Controller\u0026lt;System\u0026gt;::g_design; using lds::Controller\u0026lt;System\u0026gt;::u_ref; using lds::Controller\u0026lt;System\u0026gt;::x_ref; using lds::Controller\u0026lt;System\u0026gt;::y_ref; using lds::Controller\u0026lt;System\u0026gt;::control_type; using lds::Controller\u0026lt;System\u0026gt;::set_u_ref; using lds::Controller\u0026lt;System\u0026gt;::set_x_ref; using lds::Controller\u0026lt;System\u0026gt;::set_y_ref; using lds::Controller\u0026lt;System\u0026gt;::set_tau_awu; using lds::Controller\u0026lt;System\u0026gt;::set_control_type; using lds::Controller\u0026lt;System\u0026gt;::Reset; using lds::Controller\u0026lt;System\u0026gt;::Print; protected: std::vector\u0026lt;System\u0026gt; systems_; size_t n_sys_{}; size_t idx_{}; // controller gains could be different for each UniformMatrixList\u0026lt;\u0026gt; Kc_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_inty_list_; UniformMatrixList\u0026lt;\u0026gt; Kc_u_list_; // design-phase input gain could also be different UniformVectorList g_design_list_; // TODO(mfbolus): not sure why I need to do this. using Controller\u0026lt;System\u0026gt;::Kc_; using Controller\u0026lt;System\u0026gt;::Kc_inty_; using Controller\u0026lt;System\u0026gt;::Kc_u_; using Controller\u0026lt;System\u0026gt;::g_design_; using Controller\u0026lt;System\u0026gt;::sys_; // using Controller\u0026lt;System\u0026gt;::u_ref_; // using Controller\u0026lt;System\u0026gt;::x_ref_; // using Controller\u0026lt;System\u0026gt;::y_ref_; // using Controller\u0026lt;System\u0026gt;::control_type_; private: void InitVars(); using lds::Controller\u0026lt;System\u0026gt;::set_sys; // using Controller\u0026lt;System\u0026gt;::set_Kc; // using Controller\u0026lt;System\u0026gt;::set_Kc_inty; // using Controller\u0026lt;System\u0026gt;::set_Kc_u; // using Controller\u0026lt;System\u0026gt;::set_g_design; }; template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(systems.at(0), u_lb, u_ub, control_type), systems_(systems) { InitVars(); } template \u0026lt;typename System\u0026gt; inline SwitchedController\u0026lt;System\u0026gt;::SwitchedController( std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, data_t u_lb, data_t u_ub, size_t control_type) : Controller\u0026lt;System\u0026gt;(System(systems.at(0).n_u(), systems.at(0).n_x(), systems.at(0).n_y(), systems.at(0).dt()), u_lb, u_ub, control_type), systems_(std::move(systems)) { InitVars(); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::InitVars() { n_sys_ = systems_.size(); sys_ = systems_.at(0); Kc_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_)); Kc_inty_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); Kc_u_list_ = UniformMatrixList\u0026lt;\u0026gt;(std::vector\u0026lt;Matrix\u0026gt;(n_sys_, Kc_inty_)); g_design_list_ = UniformVectorList(std::vector\u0026lt;Vector\u0026gt;(n_sys_, g_design_)); } template \u0026lt;typename System\u0026gt; inline void SwitchedController\u0026lt;System\u0026gt;::Switch(size_t idx, bool do_force_switch) { if ((idx == idx_) \u0026amp;\u0026amp; !do_force_switch) { return; // already there. } // put old up and get new one out systems_.at(idx_) = std::move(sys_); sys_ = std::move(systems_.at(idx)); // set the state of this system to that of the previous system // TODO(mfbolus): This will only work as intended if state matrix is the same. // See example fudge in 0.4 branch src/lds_poisson_sctrl.cpp. sys_.set_m(systems_.at(idx_).m(), true); sys_.set_x(systems_.at(idx_).x()); // swap controller gains Kc_list_.Swap(Kc_, idx_); Kc_list_.Swap(Kc_, idx); if (control_type_ \u0026amp; kControlTypeIntY) { Kc_inty_list_.Swap(Kc_inty_, idx_); Kc_inty_list_.Swap(Kc_inty_, idx); } if (control_type_ \u0026amp; kControlTypeDeltaU) { Kc_u_list_.Swap(Kc_u_, idx_); Kc_u_list_.Swap(Kc_u_, idx); } g_design_list_.Swap(g_design_, idx_); g_design_list_.Swap(g_design_, idx); idx_ = idx; } // Switch } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":71,"href":"/docs/api/files/lds__sys_8h/","title":"ldsCtrlEst_h/lds_sys.h","section":"Files","content":" ldsCtrlEst_h/lds_sys.h # LDS base type. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::System Linear Dynamical System Type. Detailed Description # This file declares and partially defines the base type for linear dynamical systems ([lds::System](/docs/api/classes/classlds_1_1_system/)). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- ldsCtrlEst_h/lds_sys.h - LDS ----------------------------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_SYS_H #define LDSCTRLEST_LDS_SYS_H #include \u0026#34;lds.h\u0026#34; #include \u0026#34;lds_uniform_mats.h\u0026#34; namespace lds { class System { public: System() = default; System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); virtual ~System() {} void Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z); virtual const Vector\u0026amp; Simulate(const Vector\u0026amp; u_tm1) = 0; void f(const Vector\u0026amp; u, bool do_add_noise = false) { x_ = A_ * x_ + B_ * (g_ % u) + m_; if (do_add_noise) { x_ += arma::mvnrnd(Vector(n_x_).fill(0), Q_); } }; virtual void h() = 0; virtual Vector h_(Vector x) = 0; size_t n_u() const { return n_u_; }; size_t n_x() const { return n_x_; }; size_t n_y() const { return n_y_; }; data_t dt() const { return dt_; }; const Vector\u0026amp; x() const { return x_; }; const Matrix\u0026amp; P() const { return P_; }; const Vector\u0026amp; m() const { return m_; }; const Matrix\u0026amp; P_m() const { return P_m_; }; const Vector\u0026amp; cx() const { return cx_; }; const Vector\u0026amp; y() const { return y_; }; const Vector\u0026amp; x0() const { return x0_; }; const Vector\u0026amp; m0() const { return m0_; }; const Matrix\u0026amp; A() const { return A_; }; const Matrix\u0026amp; B() const { return B_; }; const Vector\u0026amp; g() const { return g_; }; const Matrix\u0026amp; C() const { return C_; }; const Vector\u0026amp; d() const { return d_; }; const Matrix\u0026amp; Ke() const { return Ke_; }; const Matrix\u0026amp; Ke_m() const { return Ke_m_; }; const Matrix\u0026amp; Q() { return Q_; }; const Matrix\u0026amp; Q_m() { return Q_m_; }; const Matrix\u0026amp; P0() { return P0_; }; const Matrix\u0026amp; P0_m() { return P0_m_; }; void set_A(const Matrix\u0026amp; A) { Reassign(A_, A); }; void set_B(const Matrix\u0026amp; B) { Reassign(B_, B); }; void set_m(const Vector\u0026amp; m, bool do_force_assign = false) { Reassign(m0_, m); if ((!do_adapt_m) || do_force_assign) { Reassign(m_, m); } }; void set_g(const Vector\u0026amp; g) { Reassign(g_, g); }; void set_Q(const Matrix\u0026amp; Q) { Reassign(Q_, Q); }; void set_Q_m(const Matrix\u0026amp; Q_m) { Reassign(Q_m_, Q_m); }; void set_x0(const Vector\u0026amp; x0) { Reassign(x0_, x0); }; void set_P0(const Matrix\u0026amp; P0) { Reassign(P0_, P0); }; void set_P0_m(const Matrix\u0026amp; P0_m) { Reassign(P0_m_, P0_m); }; void set_C(const Matrix\u0026amp; C) { Reassign(C_, C); }; void set_d(const Vector\u0026amp; d) { Reassign(d_, d); }; void set_x(const Vector\u0026amp; x) { Reassign(x_, x); h(); }; void Reset(); std::vector\u0026lt;UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt;\u0026gt; nstep_pred_block( UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; u, UniformMatrixList\u0026lt;kMatFreeDim2\u0026gt; z, size_t n_pred = 1); void Print(); // safe to leave this public and non-const bool do_adapt_m{}; protected: virtual void RecurseKe() = 0; void InitVars(data_t p0 = kDefaultP0, data_t q0 = kDefaultQ0); std::size_t n_x_{}; std::size_t n_u_{}; std::size_t n_y_{}; data_t dt_{}; // Signals: Vector x_; Matrix P_; Vector m_; Matrix P_m_; Vector cx_; Vector y_; Vector z_; // Parameters: Vector x0_; Matrix P0_; Vector m0_; Matrix P0_m_; Matrix A_; Matrix B_; Vector g_; Matrix Q_; Matrix Q_m_; Matrix C_; Vector d_; Matrix Ke_; Matrix Ke_m_; }; // System } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":72,"href":"/docs/api/files/lds__uniform__mats_8h/","title":"ldsCtrlEst_h/lds_uniform_mats.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_mats.h # List of uniformly sized matrices. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformMatrixList Detailed Description # This file provides a container for uniformly sized matrices. Users may specify one dimension to be free to vary in the list.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_mats.h - Uniform Matrices ------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_MATS_H #define LDSCTRLEST_LDS_UNIFORM_MATS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { template \u0026lt;MatrixListFreeDim D = kMatFreeDimNone\u0026gt; class UniformMatrixList : public std::vector\u0026lt;Matrix\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Matrix\u0026gt;::vector; // don\u0026#39;t allow push_back to be used since it doesn\u0026#39;t check dims using std::vector\u0026lt;Matrix\u0026gt;::push_back; public: using std::vector\u0026lt;Matrix\u0026gt;::operator=; using std::vector\u0026lt;Matrix\u0026gt;::operator[]; using std::vector\u0026lt;Matrix\u0026gt;::begin; using std::vector\u0026lt;Matrix\u0026gt;::end; using std::vector\u0026lt;Matrix\u0026gt;::size; using std::vector\u0026lt;Matrix\u0026gt;::at; UniformMatrixList() = default; explicit UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); explicit UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim = {0, 0}); UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; ~UniformMatrixList() = default; const std::array\u0026lt;size_t, 2\u0026gt;\u0026amp; dim(size_t n = 0) const { return dim_.at(n); } size_t size() { return std::vector\u0026lt;Matrix\u0026gt;::size(); }; const Matrix\u0026amp; at(size_t n) { return std::vector\u0026lt;Matrix\u0026gt;::at(n); }; void Swap(Matrix\u0026amp; that, size_t n); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that); UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; operator=(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept; void append(const Matrix\u0026amp; mat); private: void CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim); std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt; dim_; }; template \u0026lt;MatrixListFreeDim D\u0026gt; inline void UniformMatrixList\u0026lt;D\u0026gt;::Swap(Matrix\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.n_cols); } if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap // not moving, since it causes memory issues. // so this method isn\u0026#39;t a memory-saver as designed for now Matrix tmp = (*this)[n]; (*this)[n] = that; that = tmp; if (D == kMatFreeDim1) { this-\u0026gt;dim_[n][0] = (*this)[n].n_rows; } if (D == kMatFreeDim2) { this-\u0026gt;dim_[n][1] = (*this)[n].n_cols; } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::append(const Matrix\u0026amp; mat) { std::array\u0026lt;size_t, 2\u0026gt; dim({mat.n_rows, mat.n_cols}); CheckDimensions(dim); std::vector\u0026lt;Matrix\u0026gt;::push_back(mat); dim_.push_back(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) { // make sure dim_ vector is initialized if (dim_.empty()) { dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(that.size(), {0, 0}); } // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices\u0026#34;; throw std::runtime_error(ss.str()); } // if dimensions a not zero and do not match, skip move with error message. bool dims_nonzero = true; for (auto d : dim_) { if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; d[0] \u0026lt; 1) { dims_nonzero = false; break; } if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; d[1] \u0026lt; 1) { dims_nonzero = false; break; } } if (dims_nonzero) { bool does_match = true; if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); } if (!does_match) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrices of size \u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; dim_[k] = that.dim(k); } return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; inline UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; UniformMatrixList\u0026lt;D\u0026gt;::operator=( UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; matrices with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; matrices. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. bool dims_nonzero = true; for (auto d : dim_) { // if (!(D == kMatFreeDim1) \u0026amp;\u0026amp; (d[0] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // if (!(D == kMatFreeDim2) \u0026amp;\u0026amp; (d[1] \u0026lt; 1)) { // dims_nonzero = false; // break; // } // } // // if (dims_nonzero) { // bool does_match = true; // if (!(D == kMatFreeDim1)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][0] == that.at(0).n_rows); // } // // if (!(D == kMatFreeDim2)) { // does_match = does_match \u0026amp;\u0026amp; (dim_[0][1] == that.at(0).n_cols); // } // // if (!does_match) { // this-\u0026gt;at(0).print(\u0026#34;this[0] = \u0026#34;); // that.at(0).print(\u0026#34;that[0] = \u0026#34;); // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformMatrixList element of size (\u0026#34; \u0026lt;\u0026lt; // that.at(0).n_rows \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; that.at(0).n_cols \u0026lt;\u0026lt; \u0026#34;) for an // element of size (\u0026#34; \u0026lt;\u0026lt; dim_[0][0] \u0026lt;\u0026lt; \u0026#34;,\u0026#34; \u0026lt;\u0026lt; dim_[0][1] \u0026lt;\u0026lt; \u0026#34;). // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Matrix\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const std::vector\u0026lt;Matrix\u0026gt;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::vector\u0026lt;Matrix\u0026gt;\u0026amp;\u0026amp; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(std::move(mats)) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(std::initializer_list\u0026lt;Matrix\u0026gt; mats, std::array\u0026lt;size_t, 2\u0026gt; dim) : vector(mats) { CheckDimensions(dim); }; template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(const UniformMatrixList\u0026lt;D\u0026gt;\u0026amp; that) : vector(that) { (*this) = that; } template \u0026lt;MatrixListFreeDim D\u0026gt; UniformMatrixList\u0026lt;D\u0026gt;::UniformMatrixList(UniformMatrixList\u0026lt;D\u0026gt;\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { std::array\u0026lt;size_t, 2\u0026gt; dim_k({this-\u0026gt;at(k).n_rows, this-\u0026gt;at(k).n_cols}); dim_.push_back(dim_k); } } template \u0026lt;MatrixListFreeDim D\u0026gt; void UniformMatrixList\u0026lt;D\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 2\u0026gt; dim) { // change behavior based on free dim D if ((dim[0] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim1)) { dim[0] = this-\u0026gt;at(0).n_rows; } if ((dim[1] == 0) \u0026amp;\u0026amp; !(D == kMatFreeDim2)) { dim[1] = this-\u0026gt;at(0).n_cols; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Matrix\u0026amp; mat : *this) { if (!(D == kMatFreeDim1)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_rows == dim[0]); } if (!(D == kMatFreeDim2)) { does_match = does_match \u0026amp;\u0026amp; (mat.n_cols == dim[1]); } if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } dim_ = std::vector\u0026lt;std::array\u0026lt;size_t, 2\u0026gt;\u0026gt;(this-\u0026gt;size(), dim); for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { dim_[k][0] = (*this)[k].n_rows; dim_[k][1] = (*this)[k].n_cols; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":73,"href":"/docs/api/files/lds__uniform__systems_8h/","title":"ldsCtrlEst_h/lds_uniform_systems.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_systems.h # List of uniformly sized Systems. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformSystemList Detailed Description # This file provides a container for uniformly sized Systems.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_systems.h - Uniform Systems ----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #define LDSCTRLEST_LDS_UNIFORM_SYSTEMS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector // namespace #include \u0026#34;lds.h\u0026#34; // System type #include \u0026#34;lds_sys.h\u0026#34; namespace lds { template \u0026lt;typename System\u0026gt; class UniformSystemList : public std::vector\u0026lt;System\u0026gt; { static_assert(std::is_base_of\u0026lt;lds::System, System\u0026gt;::value, \u0026#34;System must be derived from lds::System type.\u0026#34;); private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;System\u0026gt;::vector; using std::vector\u0026lt;System\u0026gt;::operator=; using std::vector\u0026lt;System\u0026gt;::operator[]; using std::vector\u0026lt;System\u0026gt;::at; using std::vector\u0026lt;System\u0026gt;::begin; using std::vector\u0026lt;System\u0026gt;::end; using std::vector\u0026lt;System\u0026gt;::size; public: UniformSystemList() = default; explicit UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); explicit UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim = {0, 0, 0}); UniformSystemList(const UniformSystemList\u0026amp; that); UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept; ~UniformSystemList() = default; const std::array\u0026lt;size_t, 3\u0026gt;\u0026amp; dim() const { return dim_; } size_t size() { return std::vector\u0026lt;System\u0026gt;::size(); }; const System\u0026amp; at(size_t n) { return std::vector\u0026lt;System\u0026gt;::at(n); }; void Swap(System\u0026amp; that, size_t n); UniformSystemList\u0026amp; operator=(const UniformSystemList\u0026amp; that); UniformSystemList\u0026amp; operator=(UniformSystemList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim); std::array\u0026lt;size_t, 3\u0026gt; dim_{}; }; template \u0026lt;typename System\u0026gt; inline void UniformSystemList\u0026lt;System\u0026gt;::Swap(System\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformSystemList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = (dim_[0] == that.n_u()) \u0026amp;\u0026amp; (dim_[1] == that.n_x()) \u0026amp;\u0026amp; (dim_[2] == that.n_y()); if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformSystemList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap System tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( const UniformSystemList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_[0] + dim_[1] + dim_[2]) { std::array\u0026lt;size_t, 3\u0026gt; other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign systems of size \u0026#34; \u0026lt;\u0026lt; dim_[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2] \u0026lt;\u0026lt; \u0026#34; with systems of size \u0026#34; \u0026lt;\u0026lt; other_dim[0] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other_dim[1] \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; dim_[2]; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } template \u0026lt;typename System\u0026gt; inline UniformSystemList\u0026lt;System\u0026gt;\u0026amp; UniformSystemList\u0026lt;System\u0026gt;::operator=( UniformSystemList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // // if empty, assume a default constructed object and safe to move // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; systems with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; systems. Skipping.\\n\u0026#34;; // return (*this); // } // // // if dimensions a not zero and do not match, skip move with error // message. if (dim_[0] + dim_[1] + dim_[2]) { // bool does_match = (dim_[0] == that.at(0).n_u()) \u0026amp;\u0026amp; // (dim_[1] == that.at(0).n_x()) \u0026amp;\u0026amp; // (dim_[2] == that.at(0).n_y()); // if (!does_match) { // std::cerr // \u0026lt;\u0026lt; \u0026#34;Cannot move a UniformSystemList element for an element of \u0026#34; // \u0026#34;different size. Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;System\u0026gt;::operator=(std::move(that)); return (*this); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const std::vector\u0026lt;System\u0026gt;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(std::vector\u0026lt;System\u0026gt;\u0026amp;\u0026amp; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(std::move(systems)) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList( std::initializer_list\u0026lt;System\u0026gt; systems, std::array\u0026lt;size_t, 3\u0026gt; dim) : std::vector\u0026lt;System\u0026gt;(systems) { CheckDimensions(dim); }; template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(const UniformSystemList\u0026amp; that) : std::vector\u0026lt;System\u0026gt;(that) { (*this) = that; } template \u0026lt;typename System\u0026gt; UniformSystemList\u0026lt;System\u0026gt;::UniformSystemList(UniformSystemList\u0026amp;\u0026amp; that) noexcept : std::vector\u0026lt;System\u0026gt;(std::move(that)) { this-\u0026gt;dim_[0] = this-\u0026gt;at(0).n_u(); this-\u0026gt;dim_[1] = this-\u0026gt;at(0).n_x(); this-\u0026gt;dim_[2] = this-\u0026gt;at(0).n_y(); } template \u0026lt;typename System\u0026gt; void UniformSystemList\u0026lt;System\u0026gt;::CheckDimensions(std::array\u0026lt;size_t, 3\u0026gt; dim) { if (dim[0] + dim[1] + dim[2]) { dim_ = dim; } else { dim_[0] = this-\u0026gt;at(0).n_u(); dim_[1] = this-\u0026gt;at(0).n_x(); dim_[2] = this-\u0026gt;at(0).n_y(); } // make sure dimensiolaties are all uniform bool does_match(true); for (const System\u0026amp; sys : *this) { does_match = does_match \u0026amp;\u0026amp; (sys.n_u() == dim_[0]); does_match = does_match \u0026amp;\u0026amp; (sys.n_x() == dim_[1]); does_match = does_match \u0026amp;\u0026amp; (sys.n_y() == dim_[2]); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input systems are not uniform.\u0026#34;); } } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":74,"href":"/docs/api/files/lds__uniform__vecs_8h/","title":"ldsCtrlEst_h/lds_uniform_vecs.h","section":"Files","content":" ldsCtrlEst_h/lds_uniform_vecs.h # List of uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Classes # Name class lds::UniformVectorList Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.h - Uniform Vectors -------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_UNIFORM_VECS_H #define LDSCTRLEST_LDS_UNIFORM_VECS_H #include \u0026lt;array\u0026gt; // std::array #include \u0026lt;vector\u0026gt; // std::vector #include \u0026#34;lds.h\u0026#34; namespace lds { class UniformVectorList : public std::vector\u0026lt;Vector\u0026gt; { private: // TODO(mfbolus): would rather *uncomment* the below for sake of conversion // using std::vector\u0026lt;Vector\u0026gt;::vector; using std::vector\u0026lt;Vector\u0026gt;::operator=; using std::vector\u0026lt;Vector\u0026gt;::operator[]; using std::vector\u0026lt;Vector\u0026gt;::at; using std::vector\u0026lt;Vector\u0026gt;::begin; using std::vector\u0026lt;Vector\u0026gt;::end; using std::vector\u0026lt;Vector\u0026gt;::size; public: UniformVectorList() = default; explicit UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim = 0); explicit UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim = 0); UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim = 0); UniformVectorList(const UniformVectorList\u0026amp; that); UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept; ~UniformVectorList() = default; size_t dim() const { return dim_; } size_t size() { return std::vector\u0026lt;Vector\u0026gt;::size(); }; const Vector\u0026amp; at(size_t n) { return std::vector\u0026lt;Vector\u0026gt;::at(n); }; void Swap(Vector\u0026amp; that, size_t n); UniformVectorList\u0026amp; operator=(const UniformVectorList\u0026amp; that); UniformVectorList\u0026amp; operator=(UniformVectorList\u0026amp;\u0026amp; that) noexcept; private: void CheckDimensions(size_t dim); size_t dim_{}; }; inline void UniformVectorList::Swap(Vector\u0026amp; that, size_t n) { // make sure request in range if (n \u0026gt;= this-\u0026gt;size()) { std::cerr \u0026lt;\u0026lt; \u0026#34;Requested UniformMatrixList element out of bounds. Skipping.\\n\u0026#34;; return; } // check dim bool does_match = dim_ == that.n_elem; if (!does_match) { std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot swap a UniformMatrixList element for an element of \u0026#34; \u0026#34;different size. Skipping.\\n\u0026#34;; return; } // if checks pass, perform swap Vector tmp = std::move((*this)[n]); (*this)[n] = std::move(that); that = std::move(tmp); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( const UniformVectorList\u0026amp; that) { // check dimensions if (!this-\u0026gt;empty()) { if (this-\u0026gt;size() != that.size()) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors\u0026#34;; throw std::runtime_error(ss.str()); } if (dim_) { size_t other_dim(that.dim()); if (dim_ != other_dim) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ \u0026lt;\u0026lt; \u0026#34; with vectors of size \u0026#34; \u0026lt;\u0026lt; other_dim; throw std::runtime_error(ss.str()); } } } for (size_t k = 0; k \u0026lt; this-\u0026gt;size(); k++) { (*this)[k] = that[k]; } return (*this); } inline UniformVectorList\u0026amp; UniformVectorList::operator=( UniformVectorList\u0026amp;\u0026amp; that) noexcept { // // check dimensions // if (!this-\u0026gt;empty()) { // if (this-\u0026gt;size() != that.size()) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign \u0026#34; \u0026lt;\u0026lt; this-\u0026gt;size() \u0026lt;\u0026lt; \u0026#34; vectors with \u0026#34; // \u0026lt;\u0026lt; that.size() \u0026lt;\u0026lt; \u0026#34; vectors. Skipping.\\n\u0026#34;; // return (*this); // } // // if (dim_) { // size_t other_dim(that.dim()); // if (dim_ != other_dim) { // std::cerr \u0026lt;\u0026lt; \u0026#34;Cannot reassign vectors of size \u0026#34; \u0026lt;\u0026lt; dim_ // \u0026lt;\u0026lt; \u0026#34; with matrices of size \u0026#34; \u0026lt;\u0026lt; other_dim \u0026lt;\u0026lt; \u0026#34;. // Skipping.\\n\u0026#34;; // return (*this); // } // } // } dim_ = that.dim_; std::vector\u0026lt;Vector\u0026gt;::operator=(std::move(that)); return (*this); } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":75,"href":"/docs/api/files/lds_8h/","title":"ldsCtrlEst_h/lds.h","section":"Files","content":" ldsCtrlEst_h/lds.h # lds namespace More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file defines the lds namespace, which will be an umbrella for linear dynamical systems with Gaussian ([lds::gaussian](/docs/api/namespaces/namespacelds_1_1gaussian/)) or Poisson ([lds::poisson](/docs/api/namespaces/namespacelds_1_1poisson/)) observations.\nSource code # //===-- ldsCtrlEst_h/lds.h - Linear Dynmical System Namespace ---*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_LDS_H #define LDSCTRLEST_LDS_H // #ifndef LDSCTRLEST // #include \u0026lt;ldsCtrlEst\u0026gt; // #endif #include \u0026lt;armadillo\u0026gt; namespace lds { using data_t = double; // may change to float (but breaks mex functions) using Vector = arma::Col\u0026lt;data_t\u0026gt;; using Matrix = arma::Mat\u0026lt;data_t\u0026gt;; using Cube = arma::Cube\u0026lt;data_t\u0026gt;; using View = arma::subview\u0026lt;data_t\u0026gt;; namespace fill = arma::fill; static const std::size_t kControlTypeDeltaU = 0x1; static const std::size_t kControlTypeIntY = kControlTypeDeltaU \u0026lt;\u0026lt; 1; static const std::size_t kControlTypeAdaptM = kControlTypeDeltaU \u0026lt;\u0026lt; 2; static const data_t kInf = std::numeric_limits\u0026lt;data_t\u0026gt;::infinity(); static const data_t kPi = arma::datum::pi; static const data_t kDefaultP0 = 1e-6; static const data_t kDefaultQ0 = 1e-6; static const data_t kDefaultR0 = 1e-2; enum SSIDWt { kSSIDNone, kSSIDMOESP, kSSIDCVA }; enum MatrixListFreeDim { kMatFreeDimNone, kMatFreeDim1, kMatFreeDim2 }; // TODO(mfbolus): for SwitchedController, may want systems to have differing // numbers of states. Use this enum as template parameter? // enum SystemListFreeDim { // kSysFreeDimNone, // kSysFreeDimX ///\u0026lt; allow state dim (x) of systems in list to be hetero // }; // place hard limits on contents of vecors/mats void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub); void Limit(Vector\u0026amp; x, data_t lb, data_t ub); void Limit(Matrix\u0026amp; x, data_t lb, data_t ub); // in-place assign that errs if there are dimension mismatches: void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical = \u0026#34;Reassign\u0026#34;); // TODO(mfbolus): this is a fudge, but for some reason, cov mats often going // numerically asymm. void ForceSymPD(Matrix\u0026amp; X); void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min = 0); void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X); Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B); inline void Limit(std::vector\u0026lt;data_t\u0026gt;\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Vector\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Limit(Matrix\u0026amp; x, data_t lb, data_t ub) { for (data_t\u0026amp; el : x) { el = el \u0026lt; lb ? lb : el; el = el \u0026gt; ub ? ub : el; } } inline void Reassign(Vector\u0026amp; some, const Vector\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if (other.n_elem != some.n_elem) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign vector of size \u0026#34; \u0026lt;\u0026lt; some.n_elem \u0026lt;\u0026lt; \u0026#34; with vector of size \u0026#34; \u0026lt;\u0026lt; other.n_elem \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } inline void Reassign(Matrix\u0026amp; some, const Matrix\u0026amp; other, const std::string\u0026amp; parenthetical) { // check dimensions if ((other.n_rows != some.n_rows) || (other.n_cols != some.n_cols)) { std::ostringstream ss; ss \u0026lt;\u0026lt; \u0026#34;cannot reassign matrix of size \u0026#34; \u0026lt;\u0026lt; some.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; some.n_cols \u0026lt;\u0026lt; \u0026#34; with matrix of size \u0026#34; \u0026lt;\u0026lt; other.n_rows \u0026lt;\u0026lt; \u0026#34;x\u0026#34; \u0026lt;\u0026lt; other.n_cols \u0026lt;\u0026lt; \u0026#34;(\u0026#34; \u0026lt;\u0026lt; parenthetical \u0026lt;\u0026lt; \u0026#34;)\u0026#34;; throw std::runtime_error(ss.str()); } for (size_t k = 0; k \u0026lt; some.n_elem; k++) { some[k] = other[k]; } } } // namespace lds #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":76,"href":"/docs/api/files/mex__c__util_8h/","title":"ldsCtrlEst_h/mex_c_util.h","section":"Files","content":" ldsCtrlEst_h/mex_c_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C API) More\u0026hellip;\nNamespaces # Name armamexc arma/mex interface using Matlab C API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab/Octave\u0026rsquo;s C mex API.\nSource code # //===-- ldsCtrlEst_h/mex_c_util.h - Mex C API Utilities ---------*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXC_UTIL_H #define LDSCTRLEST_MEXC_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.h\u0026#34; // // If Matlab_FOUND, include matrix.h. // // (Octave does not need/have it.) // #ifdef Matlab_FOUND // #include \u0026#34;matrix.h\u0026#34; // #endif namespace armamexc { template \u0026lt;class T\u0026gt; inline auto m2T_scalar(const mxArray *matlab_scalar) -\u0026gt; T { if (mxGetData(matlab_scalar)) { return static_cast\u0026lt;T\u0026gt;(mxGetScalar(matlab_scalar)); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return 0; } template \u0026lt;class T\u0026gt; inline auto m2a_mat(const mxArray *matlab_mat, bool copy_aux_mem = false, bool strict = true) -\u0026gt; arma::Mat\u0026lt;T\u0026gt; { if (mxGetData(matlab_mat)) { const mwSize n_dim = mxGetNumberOfDimensions(matlab_mat); if (n_dim == 2) { return arma::Mat\u0026lt;T\u0026gt;(static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)), mxGetM(matlab_mat), mxGetN(matlab_mat), copy_aux_mem, strict); } mexErrMsgTxt(\u0026#34;Number of dimensions must be 2.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } mexErrMsgTxt(\u0026#34;No data available.\u0026#34;); return arma::Mat\u0026lt;T\u0026gt;(); } // TODO(mfbolus): make these templated. template \u0026lt;typename T\u0026gt; inline auto a2m_mat(arma::Mat\u0026lt;T\u0026gt; const \u0026amp;arma_mat) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_mat.n_rows, arma_mat.n_cols, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_mat.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_mat.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Mat.\u0026#34;); return nullptr; } template \u0026lt;typename T\u0026gt; inline auto a2m_vec(arma::Col\u0026lt;T\u0026gt; const \u0026amp;arma_vec) -\u0026gt; mxArray * { mxArray *matlab_mat = mxCreateNumericMatrix(arma_vec.n_elem, 1, mxDOUBLE_CLASS, mxREAL); if (matlab_mat) { auto *dst_pointer = static_cast\u0026lt;T *\u0026gt;(mxGetData(matlab_mat)); const auto *src_pointer = const_cast\u0026lt;T *\u0026gt;(arma_vec.memptr()); // TODO(mfbolus): I just want to MOVE the data, not copy. std::memcpy(dst_pointer, src_pointer, sizeof(T) * arma_vec.n_elem); return matlab_mat; } mexErrMsgTxt(\u0026#34;Failed to create matlab mat from arma::Col.\u0026#34;); return nullptr; } } // namespace armamexc #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":77,"href":"/docs/api/files/mex__cpp__util_8h/","title":"ldsCtrlEst_h/mex_cpp_util.h","section":"Files","content":" ldsCtrlEst_h/mex_cpp_util.h # arma \u0026lt;-\u0026gt; mex interoperability utilities (Matlab C++ API) More\u0026hellip;\nNamespaces # Name armamexcpp arma/mex interface using Matlab C++ API Detailed Description # This file defines utility functions for interoperability between armadillo and Matlab\u0026rsquo;s C++ mex API.\nSource code # //===-- ldsCtrlEst_h/mex_cpp_util.h - Mex C++ API Utilities -----*- C++ -*-===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #ifndef LDSCTRLEST_MEXCPP_UTIL_H #define LDSCTRLEST_MEXCPP_UTIL_H #include \u0026lt;ldsCtrlEst\u0026gt; #include \u0026#34;mex.hpp\u0026#34; #include \u0026#34;mexAdapter.hpp\u0026#34; namespace armamexcpp { template \u0026lt;class T\u0026gt; std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; m2a_cellmat(matlab::data::CellArray\u0026amp; matlab_cell) { size_t n_cells = matlab_cell.getNumberOfElements(); std::vector\u0026lt;arma::Mat\u0026lt;T\u0026gt;\u0026gt; arma_mat(n_cells, arma::Mat\u0026lt;T\u0026gt;(1, 1, arma::fill::zeros)); for (size_t k = 0; k \u0026lt; n_cells; k++) { matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = matlab_cell[k]; auto dims = matlab_mat.getDimensions(); arma_mat[k] = arma::Mat\u0026lt;T\u0026gt;(matlab_mat.release().get(), dims[0], dims[1]); } return arma_mat; }; template \u0026lt;class T\u0026gt; std::vector\u0026lt;T\u0026gt; m2s_vec(matlab::data::TypedArray\u0026lt;T\u0026gt;\u0026amp; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); T* ptr = matlab_array.release().get(); std::vector\u0026lt;T\u0026gt; vec(ptr, ptr + n_elem); return vec; }; template \u0026lt;class T\u0026gt; arma::Col\u0026lt;T\u0026gt; m2a_vec(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { size_t n_elem = matlab_array.getNumberOfElements(); // T* ptr = matlab_array.release().get(); // arma::Col\u0026lt;T\u0026gt; vec(ptr, n_elem); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? arma::Col\u0026lt;T\u0026gt; vec(n_elem, arma::fill::zeros); for (size_t k = 0; k \u0026lt; n_elem; k++) { vec[k] = matlab_array[k]; } return vec; }; template \u0026lt;class T\u0026gt; arma::Mat\u0026lt;T\u0026gt; m2a_mat(matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_array) { // ArrayDimensions == std::vector\u0026lt;size_t\u0026gt; auto dims = matlab_array.getDimensions(); // T* ptr = matlab_array.release().get(); // // mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false) // arma::Mat\u0026lt;T\u0026gt; mat(ptr, dims[0], dims[1]); //, false); // TODO(mfbolus): for some reason, using the above pointer at times leads to // getting garbage values. matlab array values may be stored in non-contiguous // memory? // // armadillo and matlab both use column-major ordering, so this should work: size_t n_elem = dims[0] * dims[1]; arma::Mat\u0026lt;T\u0026gt; mat(dims[0], dims[1], arma::fill::zeros); size_t k(0); for (auto m: matlab_array) { mat[k] = m; k++; } return mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_mat(const arma::Mat\u0026lt;T\u0026gt;\u0026amp; arma_mat, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {arma_mat.n_rows, arma_mat.n_cols}, arma_mat.memptr(), arma_mat.memptr() + arma_mat.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; a2m_vec(const arma::Col\u0026lt;T\u0026gt;\u0026amp; arma_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;({arma_vec.n_elem, 1}, arma_vec.memptr(), arma_vec.memptr() + arma_vec.n_elem); return matlab_mat; }; template \u0026lt;class T\u0026gt; matlab::data::TypedArray\u0026lt;T\u0026gt; s2m_vec(const std::vector\u0026lt;T\u0026gt;\u0026amp; std_vec, matlab::data::ArrayFactory\u0026amp; factory) { const matlab::data::TypedArray\u0026lt;T\u0026gt; matlab_mat = factory.createArray\u0026lt;T\u0026gt;( {std_vec.size(), 1}, std_vec.data(), std_vec.data() + std_vec.size()); return matlab_mat; }; } // namespace armamexcpp #endif Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":78,"href":"/docs/terminology/model/","title":"Models","section":"LDS C+E Documentation","content":" Model Definitions # This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form: \\[\\mathbf{x}_{t\u0026#43;1} = f\\left( \\mathbf{x}_{t}, \\mathbf{v}_{t} \\right) = \\mathbf{A} \\mathbf{x}_{t} \u0026#43; \\mathbf{B} \\mathbf{v}_{t} \u0026#43; \\mathbf{m}_{t} \u0026#43; \\mathbf{w}_{t}\\] \\[\\mathbf{y}_{t} = h\\left( \\mathbf{x}_{t} \\right)\\] t : time index x : system state v = g%u : input (e.g., in physical units used for model fit) u : control signal sent to actuator (e.g., in Volts) y : system output m : process disturbance w ~ N(0, Q) : process noise/disturbance A : state matrix B : input coupling matrix g : input gain (e.g., for converting to control signal actuator voltage) n.b., assumes this conversion is linear Q : process noise covariance % : element-wise multiplication LDS with Gaussian Observations # For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.\n\\[\\mathbf{y}_{t} = \\mathbf{C} \\mathbf{x}_{t} \u0026#43; \\mathbf{d}\\] \\[\\mathbf{z}_{t} \\sim \\mathcal{N}\\left(\\mathbf{y}_{t} , \\mathbf{R} \\right)\\] z : measurement C : output matrix d : output bias R : measurement noise covariance LDS with Poisson Observations # For linear dynamical systems whose outputs are assumed to be rates underlying measured count data derived from a Poisson distribution (Poisson LDS models), the output function takes the following form. Note an element-wise exponentiation is used to rectify the linear dynamics for the rate of the Poisson process.\n\\[y_{t}^{i} = \\exp \\left(\\mathbf{c}^i \\mathbf{x}_{t} \u0026#43; d^i\\right)\\] \\[z_{t}^i \\sim \\rm{Poisson} \\left(y_{t}^i \\right)\\] i : output index z : measurement (count data) c : i^th row of output matrix (C) d : output bias Model Predictive Control (MPC) # Model Predictive Control (MPC) is an advanced control strategy that utilizes a dynamic model of the system to predict and optimize future behavior over a specified time horizon. At each control step, MPC solves an optimization problem to determine the control inputs that minimize a cost function, which typically includes terms for tracking desired reference trajectories and penalizing excessive control efforts. This approach allows MPC to handle multivariable systems with constraints effectively, making it suitable for complex industrial applications.\nIn the context of linear systems, the optimization problem within MPC can be formulated as a quadratic program. This involves defining a quadratic cost function over the prediction horizon, which balances the trade-off between tracking performance and control effort. The solution to this quadratic program yields the optimal control inputs that drive the system towards the desired state while respecting operational constraints. Tools like the Operator Splitting Quadratic Program (OSQP) solver are often employed to efficiently solve these optimization problems in real-time.\n"},{"id":79,"href":"/docs/api/modules/","title":"Modules","section":"LDS C+E Documentation","content":" Modules # Control Mode Bit Masks provides fill types for constructing new armadillo vectors, matrices\nDefaults\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":80,"href":"/docs/api/namespaces/","title":"Namespaces","section":"LDS C+E Documentation","content":" Namespaces # armamexc arma/mex interface using Matlab C API\narmamexcpp arma/mex interface using Matlab C++ API\nlds::gaussian Linear Dynamical Systems with Gaussian observations.\nlds::poisson Linear Dynamical Systems with Poisson observations.\nstd\nUpdated on 5 March 2025 at 16:35:01 EST\n"},{"id":81,"href":"/docs/api/pages/","title":"Pages","section":"LDS C+E Documentation","content":" Pages # Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":82,"href":"/docs/tutorials/eg_plds_state_estimation/","title":"PLDS State Estimation","section":"LDS C+E Examples","content":" PLDS State Estimation Tutorial # This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (lds::poisson::System) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating a simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; // no. inputs size_t n_x = 1; // no. states size_t n_y = 1; // no. outputs auto n_t = static_cast\u0026lt;size_t\u0026gt;(30 / dt); // no time steps for simulation. When a system is initialized, rather than requiring all parameters be provided at construction, users may create a default system by setting only the dimensions and sample period.\n// construct ground truth system... lds::poisson::System system_true(n_u, n_x, n_y, dt); This default system is a random walk, where the state matrix is identity, the input matrix is zeros, and the top min(n_x, n_y) states are observed at the output. i.e., for this example, \\[x_{t\u0026#43;1} = x_t \u0026#43; w_t\\] \\[y_{t} = \\exp\\left(x_t\\right)\\] where \\( w_{t} \\sim \\mathcal{N}\\left( 0, Q \\right) \\) .\nNow, create non-default parameters for this model.\n// Model parameters Matrix a_true(n_x, n_x, arma::fill::eye); a_true[0] = exp(-dt / 0.075); Matrix b_true = Matrix(n_x, n_u).fill(1e-2); Vector m0_true = Vector(n_x, arma::fill::zeros).fill(-7e-2); // disturbance Vector x0_true = m0_true * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state Finally, assign the parameters using corresponding set-methods.\n// Assign params. system_true.set_A(a_true); system_true.set_B(b_true); system_true.set_x0(x0_true); system_true.set_m(m0_true); system_true.Reset(); Creating the estimator # Now, create the estimator. The system type includes filtering functionality for state estimation, so create another lds::poisson::System. As noted above, the only parameter mismatch in this simulation will be the process disturbance.\n// Construct system for estimation // e.g., will create a model with incorrect disturbance lds::poisson::System system_estimator(n_u, n_x, n_y, dt); // Can copy parameters from another system object system_estimator = system_true; // wrong disturbance Vector m0_est = m0_true * 2; system_estimator.set_m(m0_est); // set new initial conditions Vector x0_est = m0_est * arma::inv(Matrix(n_x, n_x, arma::fill::eye) - a_true); // initial state system_estimator.set_x0(x0_est); system_estimator.Reset(); // reset to initial condition. To ensure robust estimates, adaptively re-estimate the process disturbance.\n// turn on adaptive disturbance estimation system_estimator.do_adapt_m = true; // set adaptation rate by changing covariance of assumed process noise acting // on random-walk evolution of m Matrix q_m = Matrix(n_x, n_x, arma::fill::eye) * 1e-6; system_estimator.set_Q_m(q_m); Simulating estimation # In this demonstration, random inputs are presented to the system, measurements are taken, and filtering is carried out in a for-loop.\n// Simlate the true system. z.col(t) = system_true.Simulate(u.col(t - 1)); // Filter (predict -\u0026gt; update) system_estimator.Filter(u.col(t - 1), z.col(t)); Example simulation result # Below are example results for this simulation, including outputs, latent states, process disturbance, and the input. The online estimates of the output, state, and disturbance are given in purple.\nWith this parameterization, it takes the estimator approximately 5 seconds to minimize state error. The state and output error distributions for the period after 5 seconds is shown below.\n"},{"id":83,"href":"/docs/tutorials/eg_switched_plds_control/","title":"PLDS Switched Control","section":"LDS C+E Examples","content":" PLDS Switched Control Tutorial # This tutorial shows how to use this library to control a system with a switched PLDS controller (lds::poisson::SwitchedController). This type of controller is applicable in scenarios where a physical system is not accurately captured by a single LDS but has multiple discrete operating modes where the dynamics can be well-approximated as linear.\nIn the example that follows, another PLDS model (lds::poisson::System) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.\nThe full code for this can be found here.\nPreamble # In addition to including the main ldsCtrlEst header, this tutorial will use some shorthand.\n#include \u0026lt;ldsCtrlEst\u0026gt; using lds::Matrix; using lds::Vector; using lds::data_t; using std::cout; Note that lds::Matrix and lds::Vector are typedefs for arma::Mat\u0026lt;data_t\u0026gt; and arma::Col\u0026lt;data_t\u0026gt;, where the data type is double by default. May be changed to float in include/ldsCtrlEst_h/lds.h if there are memory constraints (e.g., large-scale MIMO control problems).\nCreating the simulated system # A first-order single-input/single-output system will be used for the purposes of this demonstration. The simulation will be run at 1 kHz for 30 seconds.\n// whether to do switched control bool do_switch_ctrl = true; // Make SISO system sampled at 1kHz data_t dt = 1e-3; size_t n_u = 1; size_t n_x = 1; size_t n_y = 1; // no time steps for simulation. auto n_t = static_cast\u0026lt;size_t\u0026gt;(30.0 / dt); The system\u0026rsquo;s input matrix (B) will be switched stochastically from one value (b1) to a less sensitive value (b2) according to the following probabilities.\n// for simulating switching size_t which_mode = 1; data_t pr_21 = 1e-3; // prob mode 1 -\u0026gt; 2 data_t pr_12 = pr_21; // prob mode 2 -\u0026gt; 1 Initially, the system will be in \u0026ldquo;mode\u0026rdquo; 1, where B = b1.\n// simulated system being controlled lds::poisson::System controlled_system(n_u, n_x, n_y, dt); // **Assume the system is not well characterized by one LDS, but is well // characterized by two LDS models with different input matrices.** data_t scale_sys_b = 2; Matrix a(n_x, n_x, arma::fill::eye); a[0] = 0.985; Matrix b1 = Matrix(n_x, n_u).fill(0.05); Vector d = Vector(n_y, arma::fill::zeros).fill(log(1 * dt)); controlled_system.set_A(a); controlled_system.set_B(b1); controlled_system.set_d(d); controlled_system.Reset(); // reset to initial conditions See the GLDS Control and PLDS State Estimation tutorials for more detail about creating System objects.\nCreating the controller # Now, create the controller. A switched-system controller (SwitchedController) essentially toggles between the parameters of its subsystems when the controller is told a switch has occured. The first thing the user needs to do is define these subsystems. In this example, there are two Poisson systems (sys1, sys2), which are the same save for their input gains.\nSimilar to a non-switched controller, constructing a SwitchedController requires these system models and upper/lower bounds on control. See the GLDS Control tutorial for more details. In the case of a SwitchedController, it needs a list of systems, using the std::vector container.\nMoreover, when assigning control-related signals such as the feedback controller gains, it is crucial that the list of gains optimized for each operating mode of the system have the same dimensionality. For this reason, this library provides UniformMatrixList and UniformVectorList containers that should be used when setting Kc, Kc_inty, g_design. These containers are std::vectors whose contents are uniformly sized.\nPutting this information together, here is how to create the controller and the list of controller gains optimized for each system operating mode.\n// create switched controller lds::poisson::SwitchedController switched_controller; lds::UniformMatrixList\u0026lt;\u0026gt; k_x; // feedback controller gains { // create switched controller sub-systems // system 1 lds::poisson::System sys1(controlled_system); // set process noise covariance Matrix q_controller = Matrix(n_x, n_x, arma::fill::eye) * 5e-3; sys1.set_Q(q_controller); // adaptively estimate process disturbance (m) // n.b. using arbitrary default value for process noise if enabled. sys1.do_adapt_m = true; // setting initial mode to target to avoid large error at onset: Vector x0_controller = arma::log(y_ref0) - d; sys1.set_x0(x0_controller); sys1.Reset(); // reset to initial conditions // system 2 lds::poisson::System sys2 = sys1; // set parameters sys2.set_B(b2); lds::UniformSystemList\u0026lt;lds::poisson::System\u0026gt; systems({sys1, sys2}); // controller gains for underlying systems: Matrix k_x1(n_u, n_x, arma::fill::ones); Matrix k_x2 = scale_sys_b * k_x1; // system2 is x-times less sensitive. k_x = lds::UniformMatrixList\u0026lt;\u0026gt;({k_x1, k_x2}); data_t u_lb = 0.0; data_t u_ub = 5.0; switched_controller = std::move( lds::poisson::SwitchedController(std::move(systems), u_lb, u_ub)); } Note that the above code block demonstrates how move semantics can be used for assignment/construction. Copy assignment/construction is of course also allowed.\nNow that the SwitchedController is instantiated, assign its parameters.\n// Control variables size_t control_type = 0; // no integral action, etc switched_controller.set_control_type(control_type); switched_controller.set_Kc(std::move(k_x)); switched_controller.set_y_ref(y_ref0); Simulating control # In this demonstration, we will use the ControlOutputReference method which allows users to simply set the reference output event rate (y_ref) and supply the current measurement z. It then calculates the solution for the state/input required to track that output at steady state. In this case, the goal is to regulate the output about a constant reference, so it is appropriate here. This method can also be used for time-varying references as long as this variation is slow compared to the dynamics of the system. Importantly, this method performs control in the linear state space (i.e., taking the logarithm of the reference output).\nThe control loop is carried out here in a simple for-loop, controlled system is simulated along with stochastic mode switches, a measurement taken, and the control signal updated.\n// Let the controlled system stochastically change gain // Assume another algorithm decodes this mode change and signals the // switched_controller Vector chance(1, arma::fill::randu); if (which_mode == 1) // mode1 { if (chance[0] \u0026lt; pr_21) { which_mode = 2; controlled_system.set_B(b2); if (do_switch_ctrl) { switched_controller.Switch(1); } } } else { // mode2 if (chance[0] \u0026lt; pr_12) { which_mode = 1; controlled_system.set_B(b1); if (do_switch_ctrl) { switched_controller.Switch(0); } } } // Simulate the true system. z.col(t) = controlled_system.Simulate(u.col(t - 1)); // perform control u.col(t) = switched_controller.ControlOutputReference(z.col(t)); Note that as the gain of the controlled system changes stochastically, the controller is informed of this change. In practice, a user must decode such changes in the system\u0026rsquo;s operating mode and call the Switch method accordingly. Such a decoder is not currently included in this library.\nExample simulation result # Below are example results for this simulation, including outputs, latent states, mode switches, and the control signal. The controller\u0026rsquo;s online estimates of the output and state are shown in purple.\nNote that every time the operating mode of the system changes (here, a gain changes), the controller immediately adjusts its inputs. In contrast, a non-switched controller with integral action would also compensate but do so in a comparitively sluggish fashion.\n"},{"id":84,"href":"/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/","title":"src","section":"Files","content":" src # Files # Name src/lds.cpp misc lds namespace functions src/lds_gaussian_sys.cpp GLDS base type. src/lds_poisson_sys.cpp PLDS base type. src/lds_sys.cpp LDS base type. src/lds_uniform_vecs.cpp Uniformly sized vectors. Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":85,"href":"/docs/api/files/lds__gaussian__sys_8cpp/","title":"src/lds_gaussian_sys.cpp","section":"Files","content":" src/lds_gaussian_sys.cpp # GLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Gaussian-output linear dynamical systems (lds::gaussian::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_gaussian_sys.cpp - GLDS ---------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_gaussian_sys.h\u0026gt; lds::gaussian::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0, data_t r0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { R_.zeros(n_y, n_y); R_.diag().fill(r0); do_recurse_Ke_=true; }; // recursively estimate Ke void lds::gaussian::System::RecurseKe() { if (!do_recurse_Ke_) { return; } // predict covariance P_ = A_ * P_ * A_.t() + Q_; // calc Kalman gain Ke_ = P_ * C_.t() * inv_sympd(C_ * P_ * C_.t() + R_); // update covariance // Reference: Ghahramani et Hinton (1996) P_ = P_ - Ke_ * C_ * P_; if (do_adapt_m) { P_m_ += Q_m_; // A_m = I (i.e., random walk) Ke_m_ = P_m_ * C_.t() * inv_sympd(C_ * P_m_ * C_.t() + R_); P_m_ = P_m_ - Ke_m_ * C_ * P_m_; } } // Simulate const lds::Vector\u0026amp; lds::gaussian::System::Simulate(const Vector\u0026amp; u_tm1){ f(u_tm1, true);//simulate dynamics with noise added h();//output z_ = y_ + arma::mvnrnd(Vector(n_y_).fill(0), R_);//measure return z_; } void lds::gaussian::System::Print() { lds::System::Print(); std::cout \u0026lt;\u0026lt; \u0026#34;R: \\n\u0026#34; \u0026lt;\u0026lt; R_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":86,"href":"/docs/api/files/lds__poisson__sys_8cpp/","title":"src/lds_poisson_sys.cpp","section":"Files","content":" src/lds_poisson_sys.cpp # PLDS base type. More\u0026hellip;\nDetailed Description # This file implements the type for state estimation (filtering) as well as simulation of Poisson-output linear dynamical systems (lds::poisson::sys_t). It inherits functionality from the underlying linear dynamical system (lds::sys_t).\nSource code # //===-- lds_poisson_sys.cpp - PLDS ----------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_poisson_sys.h\u0026gt; lds::poisson::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : lds::System(n_u, n_x, n_y, dt, p0, q0) { diag_y_ = diagmat(y_); pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(0); }; // Correct: Given measurement (z) and current input (u), update estimate of the // state, covar, output. // // see Eden et al. 2004 void lds::poisson::System::RecurseKe() { // predict covariance P_ = A_ * P_ * A_.t() + Q_; // update cov P_ = pinv(pinv(P_) + C_.t() * diag_y_ * C_); Ke_ = P_ * C_.t(); if (do_adapt_m) { P_m_ += Q_m_; // predict (A_m = I) P_m_ = pinv(pinv(P_m_) + C_.t() * diag_y_ * C_); // update Ke_m_ = P_m_ * C_.t(); } } // Simulate Measurement: z ~ Poisson(y) const lds::Vector\u0026amp; lds::poisson::System::Simulate(const Vector\u0026amp; u_tm1) { f(u_tm1, true); // simulate dynamics with noise added h(); // output z_.zeros(); for (std::size_t k = 0; k \u0026lt; n_y_; k++) { // construct a Poisson distribution object with mean y[k] pd_ = std::poisson_distribution\u0026lt;size_t\u0026gt;(y_[k]); // pull random sample from this distribution z_[k] = pd_(rng); } return z_; } // ******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":87,"href":"/docs/api/files/lds__sys_8cpp/","title":"src/lds_sys.cpp","section":"Files","content":" src/lds_sys.cpp # LDS base type. More\u0026hellip;\nDetailed Description # This file implements the base type for linear dynamical systems (lds::System). Note that this class defines the underlying linear dynamics, but does not have output functions.Gaussian- and Poisson-output variants will be built upon this class.\nSource code # //===-- lds_sys.cpp - LDS -------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_sys.h\u0026gt; #include \u0026lt;vector\u0026gt; lds::System::System(size_t n_u, size_t n_x, size_t n_y, data_t dt, data_t p0, data_t q0) : n_u_(n_u), n_x_(n_x), n_y_(n_y), dt_(dt) { InitVars(p0, q0); } void lds::System::InitVars(data_t p0, data_t q0) { // initial conditions. x0_ = Vector(n_x_, fill::zeros); // includes bias (nY) and g (nU) P0_ = p0 * Matrix(n_x_, n_x_, fill::eye); m0_ = x0_; P0_m_ = P0_; // signals x_ = x0_; P_ = P0_; m_ = m0_; P_m_ = P0_m_; y_ = Vector(n_y_, fill::zeros); cx_ = Vector(n_y_, fill::zeros); z_ = Vector(n_y_, fill::zeros); // By default, random walk where each state is independent // In this way, provides independent estimates of rate per channel of output. A_ = Matrix(n_x_, n_x_, fill::eye); B_ = Matrix(n_x_, n_u_, fill::zeros); g_ = Vector(n_u_, fill::ones); Q_ = q0 * Matrix(n_x_, n_x_, fill::eye); Q_m_ = Q_; C_ = Matrix(n_y_, n_x_, fill::eye); // each state will map to an output by d_ = Vector(n_y_, fill::zeros); Ke_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain. Ke_m_ = Matrix(n_x_, n_y_, fill::zeros); // estimator gain for m adaptation. do_adapt_m = false; } // Filter: Given measurement (`z`) and previous input (`u_tm1`), predict state // and update estimate of the state, covar, output using Kalman filter void lds::System::Filter(const Vector\u0026amp; u_tm1, const Vector\u0026amp; z_t) { // predict mean f(u_tm1); // dynamics h(); // output // recursively calculate esimator gains (or just keep existing values) // (also predicts+updates estimate covariance) RecurseKe(); // update x_ += Ke_ * (z_t - y_); if (do_adapt_m) { m_ += Ke_m_ * (z_t - y_); // adaptively estimating disturbance } // With new state, estimate output. h(); // --\u0026gt; posterior } void lds::System::Reset() { // reset to initial conditions x_ = x0_; // mean P_ = P0_; // cov of state estimate m_ = m0_; // process disturbance P_m_ = P0_m_; // cov of disturbance estimate h(); } std::vector\u0026lt;lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt;\u0026gt; lds::System::nstep_pred_block(lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; u, lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; z, size_t n_pred) { lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_filt; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; x_pred; lds::UniformMatrixList\u0026lt;lds::kMatFreeDim2\u0026gt; y_pred; for (size_t k = 0; k \u0026lt; u.size(); k++) { Reset(); size_t n_t = arma::size(u[k])[1]; Matrix x_filt_k(n_x_, n_t, fill::zeros); Matrix x_pred_k(n_x_, n_t - n_pred, fill::zeros); Matrix y_pred_k(n_y_, n_t - n_pred, fill::zeros); for (size_t t = 0; t \u0026lt; n_t - n_pred; t++) { Vector x_pred_ahead = x_; for (size_t t_u = t; t_u \u0026lt; t + n_pred; t_u++) { x_pred_ahead = A_ * x_pred_ahead + B_ * u[k].col(t_u); } x_pred_k.col(t) = x_pred_ahead; y_pred_k.col(t) = h_(x_pred_ahead); if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; // given previous measurment } for (size_t t = n_t - n_pred; t \u0026lt; n_t; t++) { if (t \u0026gt; 0) { Filter(u[k].col(t - 1), z[k].col(t)); } x_filt_k.col(t) = x_; } x_filt.append(x_filt_k); x_pred.append(x_pred_k); y_pred.append(y_pred_k); } return {x_filt, x_pred, y_pred}; } void lds::System::Print() { std::cout \u0026lt;\u0026lt; \u0026#34;\\n ********** SYSTEM ********** \\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;x: \\n\u0026#34; \u0026lt;\u0026lt; x_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;P: \\n\u0026#34; \u0026lt;\u0026lt; P_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;A: \\n\u0026#34; \u0026lt;\u0026lt; A_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;B: \\n\u0026#34; \u0026lt;\u0026lt; B_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;g: \\n\u0026#34; \u0026lt;\u0026lt; g_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;m: \\n\u0026#34; \u0026lt;\u0026lt; m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q: \\n\u0026#34; \u0026lt;\u0026lt; Q_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;Q_m: \\n\u0026#34; \u0026lt;\u0026lt; Q_m_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;d: \\n\u0026#34; \u0026lt;\u0026lt; d_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;C: \\n\u0026#34; \u0026lt;\u0026lt; C_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; std::cout \u0026lt;\u0026lt; \u0026#34;y: \\n\u0026#34; \u0026lt;\u0026lt; y_ \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; } //******************* SYS_T ******************* Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":88,"href":"/docs/api/files/lds__uniform__vecs_8cpp/","title":"src/lds_uniform_vecs.cpp","section":"Files","content":" src/lds_uniform_vecs.cpp # Uniformly sized vectors. More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file provides a container for uniformly sized vectors.\nSource code # //===-- ldsCtrlEst_h/lds_uniform_vecs.cpp - Uniform Matrices --------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // Limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds_uniform_vecs.h\u0026gt; namespace lds { UniformVectorList::UniformVectorList(const std::vector\u0026lt;Vector\u0026gt;\u0026amp; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); } UniformVectorList::UniformVectorList(std::vector\u0026lt;Vector\u0026gt;\u0026amp;\u0026amp; vecs, size_t dim) : vector(std::move(vecs)) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(std::initializer_list\u0026lt;Vector\u0026gt; vecs, size_t dim) : vector(vecs) { CheckDimensions(dim); }; UniformVectorList::UniformVectorList(const UniformVectorList\u0026amp; that) : vector(that) { (*this) = that; } UniformVectorList::UniformVectorList(UniformVectorList\u0026amp;\u0026amp; that) noexcept : vector(std::move(that)) { this-\u0026gt;dim_ = this-\u0026gt;at(0).n_elem; } void UniformVectorList::CheckDimensions(size_t dim) { if (dim) { dim_ = dim; } else { dim_ = this-\u0026gt;at(0).n_elem; } // make sure dimensiolaties are all uniform bool does_match(true); for (const Vector\u0026amp; vec : *this) { does_match = does_match \u0026amp;\u0026amp; (vec.n_elem == dim_); if (!does_match) { throw std::runtime_error( \u0026#34;Dimensionality of one or more input matrices are not uniform.\u0026#34;); } } } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":89,"href":"/docs/api/files/lds_8cpp/","title":"src/lds.cpp","section":"Files","content":" src/lds.cpp # misc lds namespace functions More\u0026hellip;\nNamespaces # Name lds Linear Dynamical Systems (LDS) namespace. Detailed Description # This file implements miscellaneous lds namespace functions not bound to a class.\nSource code # //===-- lds.cpp - LDS -----------------------------------------------------===// // // Copyright 2021 Michael Bolus // Copyright 2021 Georgia Institute of Technology // // Licensed under the Apache License, Version 2.0 (the \u0026#34;License\u0026#34;); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an \u0026#34;AS IS\u0026#34; BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// #include \u0026lt;ldsCtrlEst_h/lds.h\u0026gt; // insert any necessary function definitions here. namespace lds { void ForceSymPD(Matrix\u0026amp; X) { if (X.is_sympd() || !X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; // for eigenval decomp bool did_succeed(true); Vector d; Matrix u; // see first method (which may not be ideal): // https://nhigham.com/2021/02/16/diagonally-perturbing-a-symmetric-matrix-to-make-it-positive-definite/ size_t k(1); bool is_sympd = X.is_sympd(); Matrix id = Matrix(X.n_rows, X.n_cols, fill::eye); while (!is_sympd) { if (k \u0026gt; 100) { did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); data_t min_eig = arma::min(d); std::cerr \u0026lt;\u0026lt; \u0026#34;After multiple iterations, min eigen val = \u0026#34; \u0026lt;\u0026lt; min_eig \u0026lt;\u0026lt; \u0026#34;.\\n\u0026#34;; throw std::runtime_error( \u0026#34;Failed to make matrix symmetric positive definite.\u0026#34;); return; } // Limit(d, arma::eps(0), kInf); // force to be positive... // Matrix d_diag = arma::diagmat(d); // X = u * d_diag * u.t(); did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymPD failed.\u0026#34;); } data_t min_eig = arma::min(d); X += id * abs(min_eig) + arma::datum::eps; // make sure symm: X = (X + X.t()) / 2; // double check eigenvals positive after symmetrizing: arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); min_eig = arma::min(d); is_sympd = min_eig \u0026gt; 0; k++; } } void ForceSymMinEig(Matrix\u0026amp; X, data_t eig_min) { if (!X.is_square()) { return; } // make symmetric X = (X + X.t()) / 2; bool did_succeed(true); Vector d; Matrix u; did_succeed = arma::eig_sym(d, u, X, \u0026#34;std\u0026#34;); if (!did_succeed) { throw std::runtime_error(\u0026#34;ForceSymMinEig failed.\u0026#34;); } Limit(d, eig_min + arma::eps(eig_min), kInf); // enforce lower bound Matrix d_diag = arma::diagmat(d); X = u * d_diag * u.t(); // double check symmetric X = (X + X.t()) / 2; } void lq(Matrix\u0026amp; L, Matrix\u0026amp; Qt, const Matrix\u0026amp; X) { bool did_succeed(true); did_succeed = arma::qr_econ(Qt, L, X.t()); if (!did_succeed) { throw std::runtime_error(\u0026#34;LQ decomposition failed.\u0026#34;); } arma::inplace_trans(L); arma::inplace_trans(Qt); } Matrix calcCov(const Matrix\u0026amp; A, const Matrix\u0026amp; B) { // subtract out mean auto m_a = arma::mean(A, 1); Matrix a0 = A; a0.each_col() -= m_a; auto m_b = arma::mean(B, 1); Matrix b0 = B; b0.each_col() -= m_b; Matrix cov = a0 * b0.t() / a0.n_cols; return cov; } } // namespace lds Updated on 5 March 2025 at 16:35:01 EST\n"},{"id":90,"href":"/docs/api/namespaces/namespacestd/","title":"std","section":"Namespaces","content":" std # Updated on 5 March 2025 at 16:35:01 EST\n"}] \ No newline at end of file diff --git a/docs/en.search.min.0a4ff8fc05541e016283051ae0d54a28ca532d1ccd81528e387c7f29e19bc009.js b/docs/en.search.min.0a4ff8fc05541e016283051ae0d54a28ca532d1ccd81528e387c7f29e19bc009.js new file mode 100644 index 00000000..02cac6b1 --- /dev/null +++ b/docs/en.search.min.0a4ff8fc05541e016283051ae0d54a28ca532d1ccd81528e387c7f29e19bc009.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.d8a5f567aaddfd14291ea701eca6ccd7c962a2433ee501ee3ca9187ef3d617fd.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.0af7fedf957aa0ede799b0fc87d9d678bc4c46b70cb7a06f9bba5bd1e8bdc4cc.js b/docs/en.search.min.0af7fedf957aa0ede799b0fc87d9d678bc4c46b70cb7a06f9bba5bd1e8bdc4cc.js new file mode 100644 index 00000000..e315a736 --- /dev/null +++ b/docs/en.search.min.0af7fedf957aa0ede799b0fc87d9d678bc4c46b70cb7a06f9bba5bd1e8bdc4cc.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.d2e859c10d1d47f88862dc00415f6fddfe22120c22886c64132a9b3ce602e147.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.106502c57e06165042a7025a2f08e8475e5248c85493b0156ec12b9539dbcdbd.js b/docs/en.search.min.106502c57e06165042a7025a2f08e8475e5248c85493b0156ec12b9539dbcdbd.js new file mode 100644 index 00000000..c77fbdf7 --- /dev/null +++ b/docs/en.search.min.106502c57e06165042a7025a2f08e8475e5248c85493b0156ec12b9539dbcdbd.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/en.search-data.min.c719c889cec9be6cd5a2ff58d2119ba439cb5d872f0ffca15bdfacaa9767331b.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.456a135a860cf0205fd958556ea3161729eb3b69b7cddc6bf9bd73a6fcd98c77.js b/docs/en.search.min.456a135a860cf0205fd958556ea3161729eb3b69b7cddc6bf9bd73a6fcd98c77.js new file mode 100644 index 00000000..b80b7fee --- /dev/null +++ b/docs/en.search.min.456a135a860cf0205fd958556ea3161729eb3b69b7cddc6bf9bd73a6fcd98c77.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.51330e61c7b1fd13d76bf8145d7df78bb633baa004df0164d36937dcba61b94f.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js b/docs/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js new file mode 100644 index 00000000..9e79cab2 --- /dev/null +++ b/docs/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.9cfd320ab0b81a7a116ce6b7963a474b4ef48b8bab6a8c0001d3c629222792ea.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.618b0a14a873644d726c4cdf25c5fd0b4a171810e4d841275f7d1518ebf41745.js b/docs/en.search.min.618b0a14a873644d726c4cdf25c5fd0b4a171810e4d841275f7d1518ebf41745.js new file mode 100644 index 00000000..7bad8863 --- /dev/null +++ b/docs/en.search.min.618b0a14a873644d726c4cdf25c5fd0b4a171810e4d841275f7d1518ebf41745.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.88021e71daa56310a06a39db6e72993621d34c5a76c9ed5ecd29b1bf4ad2f8db.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.6bf4a945c37a68b0e35197d5055a3d63892d9f0912768ecfb7f2bcfc918bed5e.js b/docs/en.search.min.6bf4a945c37a68b0e35197d5055a3d63892d9f0912768ecfb7f2bcfc918bed5e.js new file mode 100644 index 00000000..a3407d57 --- /dev/null +++ b/docs/en.search.min.6bf4a945c37a68b0e35197d5055a3d63892d9f0912768ecfb7f2bcfc918bed5e.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.1bc81b8f6c065ee207cd6c9ffacb10cc8cbb02c0e2b14346c8eca1b0148e9277.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.82d18e8d935694d7ff0b8a23dfdbe00d24d52ff0df91c14896a97f26bf4313f9.js b/docs/en.search.min.82d18e8d935694d7ff0b8a23dfdbe00d24d52ff0df91c14896a97f26bf4313f9.js new file mode 100644 index 00000000..6234ecd5 --- /dev/null +++ b/docs/en.search.min.82d18e8d935694d7ff0b8a23dfdbe00d24d52ff0df91c14896a97f26bf4313f9.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.4c637a056ddfb2c8bcb41aabcefd2837a2544211e1d1e02e860af9cc2cf113f6.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.880d08e6f5b646cba945c77cc86a9a79febfc27f66dd68f22ef769e3813e6167.js b/docs/en.search.min.880d08e6f5b646cba945c77cc86a9a79febfc27f66dd68f22ef769e3813e6167.js new file mode 100644 index 00000000..32bc6235 --- /dev/null +++ b/docs/en.search.min.880d08e6f5b646cba945c77cc86a9a79febfc27f66dd68f22ef769e3813e6167.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.d8f6979e36e91aaee0955534c9a6ee87be5533f8cf09b0d7dfd21dc847b28bc5.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.a7b5cd643b42a37fe645bb6e7f78d7f64342b4171dcaf7a72ee318c51b9f18a2.js b/docs/en.search.min.a7b5cd643b42a37fe645bb6e7f78d7f64342b4171dcaf7a72ee318c51b9f18a2.js new file mode 100644 index 00000000..521002bd --- /dev/null +++ b/docs/en.search.min.a7b5cd643b42a37fe645bb6e7f78d7f64342b4171dcaf7a72ee318c51b9f18a2.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.916c0a771f703a05af3ec0233f9223552556f3ef4854450f4c4b69af5e5495e2.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.ac7993fe8d4a4c7aff8278e4035b25b24413de23ebefd815043d3b47a3f83f39.js b/docs/en.search.min.ac7993fe8d4a4c7aff8278e4035b25b24413de23ebefd815043d3b47a3f83f39.js new file mode 100644 index 00000000..6723c933 --- /dev/null +++ b/docs/en.search.min.ac7993fe8d4a4c7aff8278e4035b25b24413de23ebefd815043d3b47a3f83f39.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.65c34f625f9950a602a0226433da4c37549866f3f72e93c93939e71f342009dc.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.af624a2a488cdbe2acbfe043f4c3e2771bd1f2346c649deb3ce623f66ae220fa.js b/docs/en.search.min.af624a2a488cdbe2acbfe043f4c3e2771bd1f2346c649deb3ce623f66ae220fa.js new file mode 100644 index 00000000..140ffe67 --- /dev/null +++ b/docs/en.search.min.af624a2a488cdbe2acbfe043f4c3e2771bd1f2346c649deb3ce623f66ae220fa.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.e015cd687a9e9fd3fc4e08fb324842e12bc2202a08e95f5d633e3dddc2361d47.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.b8d7078e7074dff396e335edaa9e744cc1aa5c958d392f9370da657373b84c58.js b/docs/en.search.min.b8d7078e7074dff396e335edaa9e744cc1aa5c958d392f9370da657373b84c58.js new file mode 100644 index 00000000..280173d4 --- /dev/null +++ b/docs/en.search.min.b8d7078e7074dff396e335edaa9e744cc1aa5c958d392f9370da657373b84c58.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/en.search-data.min.3daf9b62119cf749584e6093a5ab5ecb6b0c3b3fcee13be147e897573018facc.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.ba0bee9096d7214e3ef986b48d32240230f8eaaeb7255908e5f80d952ea7d851.js b/docs/en.search.min.ba0bee9096d7214e3ef986b48d32240230f8eaaeb7255908e5f80d952ea7d851.js new file mode 100644 index 00000000..b0c66b85 --- /dev/null +++ b/docs/en.search.min.ba0bee9096d7214e3ef986b48d32240230f8eaaeb7255908e5f80d952ea7d851.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/en.search-data.min.30eff4c2c98b0267a65ac83cdd908a3d059084312c7d6ca2681cf60eebe832c2.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.dd282e74f117abd7cc99a5962c5ee2c1cc8c438004bb94172707abc3cd5a577d.js b/docs/en.search.min.dd282e74f117abd7cc99a5962c5ee2c1cc8c438004bb94172707abc3cd5a577d.js new file mode 100644 index 00000000..5cea7529 --- /dev/null +++ b/docs/en.search.min.dd282e74f117abd7cc99a5962c5ee2c1cc8c438004bb94172707abc3cd5a577d.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/en.search-data.min.f9279ffce4d2a4ee361f219216511c0e77ad58c4ba1b2be8f44059d8123bc4be.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/en.search.min.fd38584a97146b6a0437fa7aec2f6a76ff7b9bb9de7284c85b2214750c1f608b.js b/docs/en.search.min.fd38584a97146b6a0437fa7aec2f6a76ff7b9bb9de7284c85b2214750c1f608b.js new file mode 100644 index 00000000..7684fe4a --- /dev/null +++ b/docs/en.search.min.fd38584a97146b6a0437fa7aec2f6a76ff7b9bb9de7284c85b2214750c1f608b.js @@ -0,0 +1 @@ +"use strict";(function(){const o="/lds-ctrl-est/en.search-data.min.f0cd534ef163f541ebcbce10f51eba1a283a32b46261720b6440d5fe719c1f2b.json",i=Object.assign({cache:!0},{doc:{id:"id",field:["title","content"],store:["title","href","section"]}}),e=document.querySelector("#book-search-input"),t=document.querySelector("#book-search-results");if(!e)return;e.addEventListener("focus",n),e.addEventListener("keyup",s),document.addEventListener("keypress",a);function a(t){if(e===document.activeElement)return;const n=String.fromCharCode(t.charCode);if(!r(n))return;e.focus(),t.preventDefault()}function r(t){const n=e.getAttribute("data-hotkeys")||"";return n.indexOf(t)>=0}function n(){e.removeEventListener("focus",n),e.required=!0,fetch(o).then(e=>e.json()).then(e=>{window.bookSearchIndex=FlexSearch.create("balance",i),window.bookSearchIndex.add(e)}).then(()=>e.required=!1).then(s)}function s(){for(;t.firstChild;)t.removeChild(t.firstChild);if(!e.value)return;const n=window.bookSearchIndex.search(e.value,10);n.forEach(function(e){const n=c("<li><a href></a><small></small></li>"),s=n.querySelector("a"),o=n.querySelector("small");s.href=e.href,s.textContent=e.title,o.textContent=e.section,t.appendChild(n)})}function c(e){const t=document.createElement("div");return t.innerHTML=e,t.firstChild}})() \ No newline at end of file diff --git a/docs/favicon.png b/docs/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..59c7c2a213cc1de508282f86d457b16426bf9c62 GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC6Hgb%kP61PR}Fa?7&w?6|IB7% zUbBR!+>`asl_*Ar3-i?!3>X>I|1>Z#GR(7(Q*Z$4VZODzZo-MJr)7X_22WQ%mvv4F FO#mg08}a}E literal 0 HcmV?d00001 diff --git a/docs/favicon.svg b/docs/favicon.svg new file mode 100644 index 00000000..a3c696de --- /dev/null +++ b/docs/favicon.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M3 18h12v-2H3v2zM3 6v2h18V6H3zm0 7h18v-2H3v2z"/></svg> \ No newline at end of file diff --git a/docs/flexsearch.min.js b/docs/flexsearch.min.js new file mode 100644 index 00000000..984d8c6e --- /dev/null +++ b/docs/flexsearch.min.js @@ -0,0 +1,42 @@ +/* + FlexSearch v0.6.30 + Copyright 2019 Nextapps GmbH + Author: Thomas Wilkerling + Released under the Apache 2.0 Licence + https://github.com/nextapps-de/flexsearch +*/ +'use strict';(function(K,R,w){let L;(L=w.define)&&L.amd?L([],function(){return R}):(L=w.modules)?L[K.toLowerCase()]=R:"object"===typeof exports?module.exports=R:w[K]=R})("FlexSearch",function ma(K){function w(a,c){const b=c?c.id:a&&a.id;this.id=b||0===b?b:na++;this.init(a,c);fa(this,"index",function(){return this.a?Object.keys(this.a.index[this.a.keys[0]].c):Object.keys(this.c)});fa(this,"length",function(){return this.index.length})}function L(a,c,b,d){this.u!==this.g&&(this.o=this.o.concat(b),this.u++, +d&&this.o.length>=d&&(this.u=this.g),this.u===this.g&&(this.cache&&this.j.set(c,this.o),this.F&&this.F(this.o)));return this}function S(a){const c=B();for(const b in a)if(a.hasOwnProperty(b)){const d=a[b];F(d)?c[b]=d.slice(0):G(d)?c[b]=S(d):c[b]=d}return c}function W(a,c){const b=a.length,d=O(c),e=[];for(let f=0,h=0;f<b;f++){const g=a[f];if(d&&c(g)||!d&&!c[g])e[h++]=g}return e}function P(a,c,b,d,e,f,h,g,k,l){b=ha(b,h?0:e,g,f,c,k,l);let p;g&&(g=b.page,p=b.next,b=b.result);if(h)c=this.where(h,null, +e,b);else{c=b;b=this.l;e=c.length;f=Array(e);for(h=0;h<e;h++)f[h]=b[c[h]];c=f}b=c;d&&(O(d)||(M=d.split(":"),1<M.length?d=oa:(M=M[0],d=pa)),b.sort(d));b=T(g,p,b);this.cache&&this.j.set(a,b);return b}function fa(a,c,b){Object.defineProperty(a,c,{get:b})}function r(a){return new RegExp(a,"g")}function Q(a,c){for(let b=0;b<c.length;b+=2)a=a.replace(c[b],c[b+1]);return a}function V(a,c,b,d,e,f,h,g){if(c[b])return c[b];e=e?(g-(h||g/1.5))*f+(h||g/1.5)*e:f;c[b]=e;e>=h&&(a=a[g-(e+.5>>0)],a=a[b]||(a[b]=[]), +a[a.length]=d);return e}function ba(a,c){if(a){const b=Object.keys(a);for(let d=0,e=b.length;d<e;d++){const f=b[d],h=a[f];if(h)for(let g=0,k=h.length;g<k;g++)if(h[g]===c){1===k?delete a[f]:h.splice(g,1);break}else G(h[g])&&ba(h[g],c)}}}function ca(a){let c="",b="";var d="";for(let e=0;e<a.length;e++){const f=a[e];if(f!==b)if(e&&"h"===f){if(d="a"===d||"e"===d||"i"===d||"o"===d||"u"===d||"y"===d,("a"===b||"e"===b||"i"===b||"o"===b||"u"===b||"y"===b)&&d||" "===b)c+=f}else c+=f;d=e===a.length-1?"":a[e+ +1];b=f}return c}function qa(a,c){a=a.length-c.length;return 0>a?1:a?-1:0}function pa(a,c){a=a[M];c=c[M];return a<c?-1:a>c?1:0}function oa(a,c){const b=M.length;for(let d=0;d<b;d++)a=a[M[d]],c=c[M[d]];return a<c?-1:a>c?1:0}function T(a,c,b){return a?{page:a,next:c?""+c:null,result:b}:b}function ha(a,c,b,d,e,f,h){let g,k=[];if(!0===b){b="0";var l=""}else l=b&&b.split(":");const p=a.length;if(1<p){const y=B(),t=[];let v,x;var n=0,m;let I;var u=!0;let D,E=0,N,da,X,ea;l&&(2===l.length?(X=l,l=!1):l=ea= +parseInt(l[0],10));if(h){for(v=B();n<p;n++)if("not"===e[n])for(x=a[n],I=x.length,m=0;m<I;m++)v["@"+x[m]]=1;else da=n+1;if(C(da))return T(b,g,k);n=0}else N=J(e)&&e;let Y;for(;n<p;n++){const ra=n===(da||p)-1;if(!N||!n)if((m=N||e&&e[n])&&"and"!==m)if("or"===m)Y=!1;else continue;else Y=f=!0;x=a[n];if(I=x.length){if(u)if(D){var q=D.length;for(m=0;m<q;m++){u=D[m];var A="@"+u;h&&v[A]||(y[A]=1,f||(k[E++]=u))}D=null;u=!1}else{D=x;continue}A=!1;for(m=0;m<I;m++){q=x[m];var z="@"+q;const Z=f?y[z]||0:n;if(!(!Z&& +!d||h&&v[z]||!f&&y[z]))if(Z===n){if(ra){if(!ea||--ea<E)if(k[E++]=q,c&&E===c)return T(b,E+(l||0),k)}else y[z]=n+1;A=!0}else d&&(z=t[Z]||(t[Z]=[]),z[z.length]=q)}if(Y&&!A&&!d)break}else if(Y&&!d)return T(b,g,x)}if(D)if(n=D.length,h)for(m=l?parseInt(l,10):0;m<n;m++)a=D[m],v["@"+a]||(k[E++]=a);else k=D;if(d)for(E=k.length,X?(n=parseInt(X[0],10)+1,m=parseInt(X[1],10)+1):(n=t.length,m=0);n--;)if(q=t[n]){for(I=q.length;m<I;m++)if(d=q[m],!h||!v["@"+d])if(k[E++]=d,c&&E===c)return T(b,n+":"+m,k);m=0}}else!p|| +e&&"not"===e[0]||(k=a[0],l&&(l=parseInt(l[0],10)));c&&(h=k.length,l&&l>h&&(l=0),l=l||0,g=l+c,g<h?k=k.slice(l,g):(g=0,l&&(k=k.slice(l))));return T(b,g,k)}function J(a){return"string"===typeof a}function F(a){return a.constructor===Array}function O(a){return"function"===typeof a}function G(a){return"object"===typeof a}function C(a){return"undefined"===typeof a}function ia(a){const c=Array(a);for(let b=0;b<a;b++)c[b]=B();return c}function B(){return Object.create(null)}function sa(){let a,c;self.onmessage= +function(b){if(b=b.data)if(b.search){const d=c.search(b.content,b.threshold?{limit:b.limit,threshold:b.threshold,where:b.where}:b.limit);self.postMessage({id:a,content:b.content,limit:b.limit,result:d})}else b.add?c.add(b.id,b.content):b.update?c.update(b.id,b.content):b.remove?c.remove(b.id):b.clear?c.clear():b.info?(b=c.info(),b.worker=a,console.log(b)):b.register&&(a=b.id,b.options.cache=!1,b.options.async=!1,b.options.worker=!1,c=(new Function(b.register.substring(b.register.indexOf("{")+1,b.register.lastIndexOf("}"))))(), +c=new c(b.options))}}function ta(a,c,b,d){a=K("flexsearch","id"+a,sa,function(f){(f=f.data)&&f.result&&d(f.id,f.content,f.result,f.limit,f.where,f.cursor,f.suggest)},c);const e=ma.toString();b.id=c;a.postMessage({register:e,options:b,id:c});return a}const H={encode:"icase",f:"forward",split:/\W+/,cache:!1,async:!1,g:!1,D:!1,a:!1,b:9,threshold:0,depth:0},ja={memory:{encode:"extra",f:"strict",threshold:0,b:1},speed:{encode:"icase",f:"strict",threshold:1,b:3,depth:2},match:{encode:"extra",f:"full",threshold:1, +b:3},score:{encode:"extra",f:"strict",threshold:1,b:9,depth:4},balance:{encode:"balance",f:"strict",threshold:0,b:3,depth:3},fast:{encode:"icase",f:"strict",threshold:8,b:9,depth:1}},aa=[];let na=0;const ka={},la={};w.create=function(a,c){return new w(a,c)};w.registerMatcher=function(a){for(const c in a)a.hasOwnProperty(c)&&aa.push(r(c),a[c]);return this};w.registerEncoder=function(a,c){U[a]=c.bind(U);return this};w.registerLanguage=function(a,c){ka[a]=c.filter;la[a]=c.stemmer;return this};w.encode= +function(a,c){return U[a](c)};w.prototype.init=function(a,c){this.v=[];if(c){var b=c.preset;a=c}else a||(a=H),b=a.preset;c={};J(a)?(c=ja[a],a={}):b&&(c=ja[b]);if(b=a.worker)if("undefined"===typeof Worker)a.worker=!1,this.m=null;else{var d=parseInt(b,10)||4;this.C=-1;this.u=0;this.o=[];this.F=null;this.m=Array(d);for(var e=0;e<d;e++)this.m[e]=ta(this.id,e,a,L.bind(this))}this.f=a.tokenize||c.f||this.f||H.f;this.split=C(b=a.split)?this.split||H.split:J(b)?r(b):b;this.D=a.rtl||this.D||H.D;this.async= +"undefined"===typeof Promise||C(b=a.async)?this.async||H.async:b;this.g=C(b=a.worker)?this.g||H.g:b;this.threshold=C(b=a.threshold)?c.threshold||this.threshold||H.threshold:b;this.b=C(b=a.resolution)?b=c.b||this.b||H.b:b;b<=this.threshold&&(this.b=this.threshold+1);this.depth="strict"!==this.f||C(b=a.depth)?c.depth||this.depth||H.depth:b;this.w=(b=C(b=a.encode)?c.encode||H.encode:b)&&U[b]&&U[b].bind(U)||(O(b)?b:this.w||!1);(b=a.matcher)&&this.addMatcher(b);if(b=(c=a.lang)||a.filter){J(b)&&(b=ka[b]); +if(F(b)){d=this.w;e=B();for(var f=0;f<b.length;f++){var h=d?d(b[f]):b[f];e[h]=1}b=e}this.filter=b}if(b=c||a.stemmer){var g;c=J(b)?la[b]:b;d=this.w;e=[];for(g in c)c.hasOwnProperty(g)&&(f=d?d(g):g,e.push(r(f+"($|\\W)"),d?d(c[g]):c[g]));this.stemmer=g=e}this.a=e=(b=a.doc)?S(b):this.a||H.a;this.i=ia(this.b-(this.threshold||0));this.h=B();this.c=B();if(e){this.l=B();a.doc=null;g=e.index={};c=e.keys=[];d=e.field;f=e.tag;h=e.store;F(e.id)||(e.id=e.id.split(":"));if(h){var k=B();if(J(h))k[h]=1;else if(F(h))for(let l= +0;l<h.length;l++)k[h[l]]=1;else G(h)&&(k=h);e.store=k}if(f){this.G=B();h=B();if(d)if(J(d))h[d]=a;else if(F(d))for(k=0;k<d.length;k++)h[d[k]]=a;else G(d)&&(h=d);F(f)||(e.tag=f=[f]);for(d=0;d<f.length;d++)this.G[f[d]]=B();this.I=f;d=h}if(d){let l;F(d)||(G(d)?(l=d,e.field=d=Object.keys(d)):e.field=d=[d]);for(e=0;e<d.length;e++)f=d[e],F(f)||(l&&(a=l[f]),c[e]=f,d[e]=f.split(":")),g[f]=new w(a)}a.doc=b}this.B=!0;this.j=(this.cache=b=C(b=a.cache)?this.cache||H.cache:b)?new ua(b):!1;return this};w.prototype.encode= +function(a){a&&(aa.length&&(a=Q(a,aa)),this.v.length&&(a=Q(a,this.v)),this.w&&(a=this.w(a)),this.stemmer&&(a=Q(a,this.stemmer)));return a};w.prototype.addMatcher=function(a){const c=this.v;for(const b in a)a.hasOwnProperty(b)&&c.push(r(b),a[b]);return this};w.prototype.add=function(a,c,b,d,e){if(this.a&&G(a))return this.A("add",a,c);if(c&&J(c)&&(a||0===a)){var f="@"+a;if(this.c[f]&&!d)return this.update(a,c);if(this.g)return++this.C>=this.m.length&&(this.C=0),this.m[this.C].postMessage({add:!0,id:a, +content:c}),this.c[f]=""+this.C,b&&b(),this;if(!e){if(this.async&&"function"!==typeof importScripts){let t=this;f=new Promise(function(v){setTimeout(function(){t.add(a,c,null,d,!0);t=null;v()})});if(b)f.then(b);else return f;return this}if(b)return this.add(a,c,null,d,!0),b(),this}c=this.encode(c);if(!c.length)return this;b=this.f;e=O(b)?b(c):c.split(this.split);this.filter&&(e=W(e,this.filter));const n=B();n._ctx=B();const m=e.length,u=this.threshold,q=this.depth,A=this.b,z=this.i,y=this.D;for(let t= +0;t<m;t++){var h=e[t];if(h){var g=h.length,k=(y?t+1:m-t)/m,l="";switch(b){case "reverse":case "both":for(var p=g;--p;)l=h[p]+l,V(z,n,l,a,y?1:(g-p)/g,k,u,A-1);l="";case "forward":for(p=0;p<g;p++)l+=h[p],V(z,n,l,a,y?(p+1)/g:1,k,u,A-1);break;case "full":for(p=0;p<g;p++){const v=(y?p+1:g-p)/g;for(let x=g;x>p;x--)l=h.substring(p,x),V(z,n,l,a,v,k,u,A-1)}break;default:if(g=V(z,n,h,a,1,k,u,A-1),q&&1<m&&g>=u)for(g=n._ctx[h]||(n._ctx[h]=B()),h=this.h[h]||(this.h[h]=ia(A-(u||0))),k=t-q,l=t+q+1,0>k&&(k=0),l> +m&&(l=m);k<l;k++)k!==t&&V(h,g,e[k],a,0,A-(k<t?t-k:k-t),u,A-1)}}}this.c[f]=1;this.B=!1}return this};w.prototype.A=function(a,c,b){if(F(c)){var d=c.length;if(d--){for(var e=0;e<d;e++)this.A(a,c[e]);return this.A(a,c[d],b)}}else{var f=this.a.index,h=this.a.keys,g=this.a.tag;e=this.a.store;var k;var l=this.a.id;d=c;for(var p=0;p<l.length;p++)d=d[l[p]];if("remove"===a&&(delete this.l[d],l=h.length,l--)){for(c=0;c<l;c++)f[h[c]].remove(d);return f[h[l]].remove(d,b)}if(g){for(k=0;k<g.length;k++){var n=g[k]; +var m=c;l=n.split(":");for(p=0;p<l.length;p++)m=m[l[p]];m="@"+m}k=this.G[n];k=k[m]||(k[m]=[])}l=this.a.field;for(let u=0,q=l.length;u<q;u++){n=l[u];g=c;for(m=0;m<n.length;m++)g=g[n[m]];n=f[h[u]];m="add"===a?n.add:n.update;u===q-1?m.call(n,d,g,b):m.call(n,d,g)}if(e){b=Object.keys(e);a=B();for(f=0;f<b.length;f++)if(h=b[f],e[h]){h=h.split(":");let u,q;for(l=0;l<h.length;l++)g=h[l],u=(u||c)[g],q=(q||a)[g]=u}c=a}k&&(k[k.length]=c);this.l[d]=c}return this};w.prototype.update=function(a,c,b){if(this.a&& +G(a))return this.A("update",a,c);this.c["@"+a]&&J(c)&&(this.remove(a),this.add(a,c,b,!0));return this};w.prototype.remove=function(a,c,b){if(this.a&&G(a))return this.A("remove",a,c);var d="@"+a;if(this.c[d]){if(this.g)return this.m[this.c[d]].postMessage({remove:!0,id:a}),delete this.c[d],c&&c(),this;if(!b){if(this.async&&"function"!==typeof importScripts){let e=this;d=new Promise(function(f){setTimeout(function(){e.remove(a,null,!0);e=null;f()})});if(c)d.then(c);else return d;return this}if(c)return this.remove(a, +null,!0),c(),this}for(c=0;c<this.b-(this.threshold||0);c++)ba(this.i[c],a);this.depth&&ba(this.h,a);delete this.c[d];this.B=!1}return this};let M;w.prototype.search=function(a,c,b,d){if(G(c)){if(F(c))for(var e=0;e<c.length;e++)c[e].query=a;else c.query=a;a=c;c=1E3}else c&&O(c)?(b=c,c=1E3):c||0===c||(c=1E3);if(this.g){this.F=b;this.u=0;this.o=[];for(var f=0;f<this.g;f++)this.m[f].postMessage({search:!0,limit:c,content:a})}else{var h=[],g=a;if(G(a)&&!F(a)){b||(b=a.callback)&&(g.callback=null);var k= +a.sort;var l=a.page;c=a.limit;f=a.threshold;var p=a.suggest;a=a.query}if(this.a){f=this.a.index;const y=g.where;var n=g.bool||"or",m=g.field;let t=n;let v,x;if(m)F(m)||(m=[m]);else if(F(g)){var u=g;m=[];t=[];for(var q=0;q<g.length;q++)d=g[q],e=d.bool||n,m[q]=d.field,t[q]=e,"not"===e?v=!0:"and"===e&&(x=!0)}else m=this.a.keys;n=m.length;for(q=0;q<n;q++)u&&(g=u[q]),l&&!J(g)&&(g.page=null,g.limit=0),h[q]=f[m[q]].search(g,0);if(b)return b(P.call(this,a,t,h,k,c,p,y,l,x,v));if(this.async){const I=this;return new Promise(function(D){Promise.all(h).then(function(E){D(P.call(I, +a,t,E,k,c,p,y,l,x,v))})})}return P.call(this,a,t,h,k,c,p,y,l,x,v)}f||(f=this.threshold||0);if(!d){if(this.async&&"function"!==typeof importScripts){let y=this;f=new Promise(function(t){setTimeout(function(){t(y.search(g,c,null,!0));y=null})});if(b)f.then(b);else return f;return this}if(b)return b(this.search(g,c,null,!0)),this}if(!a||!J(a))return h;g=a;if(this.cache)if(this.B){if(b=this.j.get(a))return b}else this.j.clear(),this.B=!0;g=this.encode(g);if(!g.length)return h;b=this.f;b=O(b)?b(g):g.split(this.split); +this.filter&&(b=W(b,this.filter));u=b.length;d=!0;e=[];var A=B(),z=0;1<u&&(this.depth&&"strict"===this.f?n=!0:b.sort(qa));if(!n||(q=this.h)){const y=this.b;for(;z<u;z++){let t=b[z];if(t){if(n){if(!m)if(q[t])m=t,A[t]=1;else if(!p)return h;if(p&&z===u-1&&!e.length)n=!1,t=m||t,A[t]=0;else if(!m)continue}if(!A[t]){const v=[];let x=!1,I=0;const D=n?q[m]:this.i;if(D){let E;for(let N=0;N<y-f;N++)if(E=D[N]&&D[N][t])v[I++]=E,x=!0}if(x)m=t,e[e.length]=1<I?v.concat.apply([],v):v[0];else if(!p){d=!1;break}A[t]= +1}}}}else d=!1;d&&(h=ha(e,c,l,p));this.cache&&this.j.set(a,h);return h}};w.prototype.find=function(a,c){return this.where(a,c,1)[0]||null};w.prototype.where=function(a,c,b,d){const e=this.l,f=[];let h=0;let g;var k;let l;if(G(a)){b||(b=c);var p=Object.keys(a);var n=p.length;g=!1;if(1===n&&"id"===p[0])return[e[a.id]];if((k=this.I)&&!d)for(var m=0;m<k.length;m++){var u=k[m],q=a[u];if(!C(q)){l=this.G[u]["@"+q];if(0===--n)return l;p.splice(p.indexOf(u),1);delete a[u];break}}k=Array(n);for(m=0;m<n;m++)k[m]= +p[m].split(":")}else{if(O(a)){c=d||Object.keys(e);b=c.length;for(p=0;p<b;p++)n=e[c[p]],a(n)&&(f[h++]=n);return f}if(C(c))return[e[a]];if("id"===a)return[e[c]];p=[a];n=1;k=[a.split(":")];g=!0}d=l||d||Object.keys(e);m=d.length;for(u=0;u<m;u++){q=l?d[u]:e[d[u]];let A=!0;for(let z=0;z<n;z++){g||(c=a[p[z]]);const y=k[z],t=y.length;let v=q;if(1<t)for(let x=0;x<t;x++)v=v[y[x]];else v=v[y[0]];if(v!==c){A=!1;break}}if(A&&(f[h++]=q,b&&h===b))break}return f};w.prototype.info=function(){if(this.g)for(let a=0;a< +this.g;a++)this.m[a].postMessage({info:!0,id:this.id});else return{id:this.id,items:this.length,cache:this.cache&&this.cache.s?this.cache.s.length:!1,matcher:aa.length+(this.v?this.v.length:0),worker:this.g,threshold:this.threshold,depth:this.depth,resolution:this.b,contextual:this.depth&&"strict"===this.f}};w.prototype.clear=function(){return this.destroy().init()};w.prototype.destroy=function(){this.cache&&(this.j.clear(),this.j=null);this.i=this.h=this.c=null;if(this.a){const a=this.a.keys;for(let c= +0;c<a.length;c++)this.a.index[a[c]].destroy();this.a=this.l=null}return this};w.prototype.export=function(a){const c=!a||C(a.serialize)||a.serialize;if(this.a){const d=!a||C(a.doc)||a.doc;var b=!a||C(a.index)||a.index;a=[];let e=0;if(b)for(b=this.a.keys;e<b.length;e++){const f=this.a.index[b[e]];a[e]=[f.i,f.h,Object.keys(f.c)]}d&&(a[e]=this.l)}else a=[this.i,this.h,Object.keys(this.c)];c&&(a=JSON.stringify(a));return a};w.prototype.import=function(a,c){if(!c||C(c.serialize)||c.serialize)a=JSON.parse(a); +const b=B();if(this.a){var d=!c||C(c.doc)||c.doc,e=0;if(!c||C(c.index)||c.index){c=this.a.keys;const h=c.length;for(var f=a[0][2];e<f.length;e++)b[f[e]]=1;for(e=0;e<h;e++){f=this.a.index[c[e]];const g=a[e];g&&(f.i=g[0],f.h=g[1],f.c=b)}}d&&(this.l=G(d)?d:a[e])}else{d=a[2];for(e=0;e<d.length;e++)b[d[e]]=1;this.i=a[0];this.h=a[1];this.c=b}};const va=function(){const a=r("\\s+"),c=r("[^a-z0-9 ]"),b=[r("[-/]")," ",c,"",a," "];return function(d){return ca(Q(d.toLowerCase(),b))}}(),U={icase:function(a){return a.toLowerCase()}, +simple:function(){const a=r("\\s+"),c=r("[^a-z0-9 ]"),b=r("[-/]"),d=r("[\u00e0\u00e1\u00e2\u00e3\u00e4\u00e5]"),e=r("[\u00e8\u00e9\u00ea\u00eb]"),f=r("[\u00ec\u00ed\u00ee\u00ef]"),h=r("[\u00f2\u00f3\u00f4\u00f5\u00f6\u0151]"),g=r("[\u00f9\u00fa\u00fb\u00fc\u0171]"),k=r("[\u00fd\u0177\u00ff]"),l=r("\u00f1"),p=r("[\u00e7c]"),n=r("\u00df"),m=r(" & "),u=[d,"a",e,"e",f,"i",h,"o",g,"u",k,"y",l,"n",p,"k",n,"s",m," and ",b," ",c,"",a," "];return function(q){q=Q(q.toLowerCase(),u);return" "===q?"":q}}(),advanced:function(){const a= +r("ae"),c=r("ai"),b=r("ay"),d=r("ey"),e=r("oe"),f=r("ue"),h=r("ie"),g=r("sz"),k=r("zs"),l=r("ck"),p=r("cc"),n=r("sh"),m=r("th"),u=r("dt"),q=r("ph"),A=r("pf"),z=r("ou"),y=r("uo"),t=[a,"a",c,"ei",b,"ei",d,"ei",e,"o",f,"u",h,"i",g,"s",k,"s",n,"s",l,"k",p,"k",m,"t",u,"t",q,"f",A,"f",z,"o",y,"u"];return function(v,x){if(!v)return v;v=this.simple(v);2<v.length&&(v=Q(v,t));x||1<v.length&&(v=ca(v));return v}}(),extra:function(){const a=r("p"),c=r("z"),b=r("[cgq]"),d=r("n"),e=r("d"),f=r("[vw]"),h=r("[aeiouy]"), +g=[a,"b",c,"s",b,"k",d,"m",e,"t",f,"f",h,""];return function(k){if(!k)return k;k=this.advanced(k,!0);if(1<k.length){k=k.split(" ");for(let l=0;l<k.length;l++){const p=k[l];1<p.length&&(k[l]=p[0]+Q(p.substring(1),g))}k=k.join(" ");k=ca(k)}return k}}(),balance:va},ua=function(){function a(c){this.clear();this.H=!0!==c&&c}a.prototype.clear=function(){this.cache=B();this.count=B();this.index=B();this.s=[]};a.prototype.set=function(c,b){if(this.H&&C(this.cache[c])){let d=this.s.length;if(d===this.H){d--; +const e=this.s[d];delete this.cache[e];delete this.count[e];delete this.index[e]}this.index[c]=d;this.s[d]=c;this.count[c]=-1;this.cache[c]=b;this.get(c)}else this.cache[c]=b};a.prototype.get=function(c){const b=this.cache[c];if(this.H&&b){var d=++this.count[c];const f=this.index;let h=f[c];if(0<h){const g=this.s;for(var e=h;this.count[g[--h]]<=d&&-1!==h;);h++;if(h!==e){for(d=e;d>h;d--)e=g[d-1],g[d]=e,f[e]=d;g[h]=c;f[c]=h}}}return b};return a}();return w}(function(){const K={},R="undefined"!==typeof Blob&& +"undefined"!==typeof URL&&URL.createObjectURL;return function(w,L,S,W,P){S=R?URL.createObjectURL(new Blob(["("+S.toString()+")()"],{type:"text/javascript"})):w+".min.js";w+="-"+L;K[w]||(K[w]=[]);K[w][P]=new Worker(S);K[w][P].onmessage=W;return K[w][P]}}()),this); diff --git a/docs/fonts/roboto-mono-v13-latin-regular.woff b/docs/fonts/roboto-mono-v13-latin-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..f319fbfa46a9c546ad2b4f68e2b6f9267cdfc5d8 GIT binary patch literal 15160 zcmYkjV{j(j_ceUQw(W^+8xz~MZQHgvNhY~s+nCr+Cbq3f^5*{ipAYY;UbTC#+WV-g ztNQHj<)I)U0RRDfH(o9P;(u0s_S^p-^Pl(sZ;~o%A^-r0*f+=i4UQCyU`z!SMwV~R z_uE(dhN0_>lB<fUu<AFr`0ew5L+aRZlEK8z$PoYlMgQi#zAXpW4d0!Gk;^v+miQe5 z{r`Y$Ve4i7&1C`rEDr#HENbbr7m=lzk?HqvvEMNq{{e(O7t!*Y{JtIf_KChh4qFdG zV`=B=`OO`CkHPef0kv^?Fk1(c?>Mgi#%=kJgKc?*u`}}g9+!LaI}iUG#DFgl9(yA@ zvv2O?JBIOlexU*(2H}nlF0SAEg8dzX`Cr_Z9l*@d+3Y))_q%@p)PJyqj1BW#H!(Cd zG~6*}GG;O|baJ|fF-nxKp&K_+{K+UiWv)2(lWxpJ(a6vU5d8IpvjYIUg~9NAC;yjc z#67_k6qFVe<c0}k1_LYM3pMZglQ=R2fXqd%3<?q^kn#en2aUYGy1t&?>Fay{Vw9ZU z2ExPR|CC*ag^#`ez`?=RtkUERTw(?VI5Z%9{I6ex(IALGMQq5v_fHWqF|sDjLejnx zcHG~)XMt$URCKVwj^`2xi;bGt;?@(ztUE<Uo>#$;8<2S>2!;RUr%i%YX=(w)=N`FR z{33Q&o9#AQ{_hHY8<xL(Vy)U3bR@%cdSP-inHLmz;yp-~3-)w)UHWRMD9lFekcxlh zFEhO;Tjj5}FrEopz0vk0^TifGGKjbyP}rSGBsqYs9w>I`eZTs+7Lb;f_fO1?Z3XWg zBkqi@Jh#-QDIFq9MH(pIop2o|+Wn$bbSFtVxFmN7J<$5un$n{n?diDRe|VzmO3s6i z5f?2MRx&x)s}xRUa<&rh->&&CatViEJl#$O$*l<SI4g_CSc_e{#397Y53eeNCil1^ z;kBeTt0tc2I<I75hA=P7IfbA$<xl^xIL~yPrlbo~nz}e)DEW}S0HdcnJ8JaNl%j_S zmCGiEF>0DJLG5*ruEAXb&pCii7MDJwi5AED7kojMV^}psmU9E1b&6fa$Ayk<#utuG zSyO3(u4M>ThCX9k+NQc;9T%^<@h@bUZNrqg&PAOX)W&7~0z>De+E5R=IY;8!_uhX$ zc_3e303^@#Djz6AG|8vu>6!z3;B(EA^%79c&W*EuV@HIUVHMK2gP9_<Pia1IR^$X+ zs+1fX6;*cpaK0v>!lV7K&vgd4&Bs&?e3IykM?I~2r-S?7_o*Dt+E^5HD^cWS{U@&E z1auN;WKR&puHZnSK!iB`Qod2DiJ{EnM~!~SGySI*%4)+wD_6fG0dP@<K6k&mJkWY# z67buzf1iQd3h8)1OOhVu1b6!X&BC}E*Nf3jV;vnHA0D22@-g1)q<;8;bgj5Br&&Xg z*We+(;Idic2QI6g4d_-L;u6z_$4SsSX3NG5F3zxiivO5DB^~Q`Mt(>mB9gCa$^l9< z|7=gvM8^Gl?YiE7F#21#{5nSB&6NkVP$952NVn)BHk3!r8bJua!tw~Ej`WT|4tY3y zZ%OFLKSjmo+jB>^?_V2XuC!-nzwa$Ij8E#Bz}-cd9lgY=WJwm8<Z7Krtcx#iMIEvK z1739O0a|z!mQotGcn)sj=n^T+f;trzu(ML-c+$5?acnYYe&g$$wzgM?8x>?|xcDXa zWl78=gsk4W)WOsKBEpt@w|<FtekR}I*L`WKx0UDK$%#?H2RC|wJ>*X@q#vs3K#GJ^ zkg%KHuRCBkiC^4>x4Azmw2@5frZ$1V1tK-z`<44rgm|_m0|i{+O1VcTMFEHu{TjaB zTJ}b6yIAJMJ7ai=gO6YFxMTvl?Jy~h-Kw#F%Bf8#<M~BVKyFIyzw#ZF;XA9q-#(mr zcATR5@VdbISvga>dz69l`+@p8-lZ&~QNf{-7=+T3)`{<y(|7S*@Mf`|oTum*h_&Vc z0z!sPII`Ux&z`GZ9h;P)MGSe85KY8L9Hie22$(&Y$T)~fWcxy;E2W&%ncFkjAYOe5 zsrH@uCH5T~yz7%3(R&qF@e>TN^S>0^j9ZckIpZYwTO00XH@PjWC*usnhDR%4^4fp> z0suPaxUk{lA*SC~z(YhOO17-o;)lYCYN1E}As!#Sy)D>TY$%kUU%PYp0W#_5!Bed` zQw7t-Wk(0S9!|ryt5%06UoG;5c~|$dpyrm4gBG&%aYHPKyLn_7k%ha&v|<ZD2o}9; zDITd#j1mfZdaf6#m!lWhJ^+i3%&eR!eG^NLe3Bkue71<vxRJwSFQmWSR%OB-=l-M& ziXNNPvyBrL(8br(X>MZ9w{fvNSW@9v$4g8j7=4A-njU?c+<Z{nK;3`_hWJW=H(~33 zSRIuU->X!bJWnu|B$yqU=)0;MweFu}6kWc6Z9UPiwqUlH4c`7Tdk&gHTg8&EgHYr> zIY#O~j=S-~;peq#4*UTV^0g1JeK~Z=4cG>e=AQYmhApZ_(T?LVUB%{fp}8j=niOl@ z2HTLd+A^*iKJkDC(7utTzrM*Z8vjn<_y1s5e@IbR|Mv!j&Xw^Hs(LH(N09=vm7gj6 z@*E!w`DLNXP!&5op(grf_z0GzJBJB>Rlw_Ez_j{9;}4)o?C#0Xar=Rw{lZI8cf@Hg zJS=J`uwO_A8E{DJf&&kBdy&_G>iUj0RepBfyW<Q2%J7VJc1~pFU;KIyhgU;?Gt!$+ zV?YpiCdq^U%ZUqH^U`m;lLi}`G!C&d2_F_*gM^CpoI}X8EfP|?aw0pns=Mr%NFl=} zt>3#4r!7;KgPkG;3yNw1e0C@!OR96rR2x_FT!;&qtJ`!eONcmj!081B7yL(X8(EgF zDN|zLh+DrZiVfD#QYhOOmZKTeADzPPJh|{$1}NutV|6Ouy7FT~s?8e3KUS6)je|uI z-tg22v-UlR^fkJlpa``w#F%Ot!UP`MOtV9dYV7x&RoMJZrH{8GBLwyyR0mHNmbSiC zZZhX3Yj|N{U#mAYIX%yx%Y7Y=*tl?%jmTjuudQz@VWKg+#s|c5F`~O}ihH!k+e^p> zB*CorqyPc9dWWO&h%1P*6_Cfrap92u&D2V7`^>Ypx93Hg#<g7*opi~$r%6_d6KAjP ziRKpf*AoBUf{~67rZWKKX5FtJ+@bs6c0ns51p1)$@`L_ityx!E9Ayc28`!c*$bYj1 z>dMEbjwn7Wm2&7cUs#q?tG>?mYAh;&o$JXJtaeuz@*3z269<Wm?L(HX=v~1r8ftOR z?)fsM=2Ox?2(9koI&@L$8X9COl#xS?el7)*6`96uT&@ewekF1$((Y$2p4Di!=~S!d z>>>|C&V-6E7p%O`21D?6=WFB46wggZ%(IK=wk{CDHfl#&5^HVL20(N@;pYV09Z&`s z-=BK*q8TTKs?x068@AJg?rJh1R=qT0TeBg{*w|#?K%>@;C@G@F3Al{zSs2&zSiZzY zJtA5VpEy=27;fjVAqwWmvU8LsXG9n=HYa9G)n$NM>UA}3k%7d+%7@hU{_G~jRI2TP zn3_00)mDd}FC)49DFwH7FOnfmdIRf7S1{CIP|)Sv-gYuSk-@+)M%&}+-NofF#s%cU zZU0326V%oK`p*9k(Ig=7`ZV}@jkUXHfFUS@+tC%{vH3cfvf}7E1YOEM!Wu-u9|=#C zmL)!xuzE~aG$6DGN=LHH!cVw`-4v7~Pg2i|s4GgZuP^7iXj`OLu$}Z?MK$Cy<N-+H zM{FYqBP7xm1ipS^{UGY<4E-TJ5^Y>tV7S%ze(>1&oL_kS>J-^ouoU}Z>E))+HRsoL zmz#ApHOHPrr7=}@S)nP*w0|&x>fMM5z<WnE$b=75m?rWjN3H^_xGHLaVs!K5?b-nK zrO2)Q>{q7cw?SjtwWHFM9bSr16fGCi`Telkgvr_iZMJuyzVbZ^2JMm734pW@%u!+$ z>Qp&f1ROaZ`P^zIm_SK7WY5&MRqs++ZL<dQQtz$jW{kx6^mEcw+O&wi_T1vf8sD;y z;*8Ed3k`@}*@fZv*g0uxj5`{jjh$TG%r6A#AJZ6ja>k>Ua;8!A^QFkqKcuDG{Zl&T zcCDAJw|w(;fL3m6XR7H;y-nzcQ9SWO!S_wxC7MGnm9saLe<Y%kfJ;D(gO2domRHgh z-;`Pv>hwS4$&nzwE67QaW+FXR8y1)4E8<iMSn(-X!ezHlQ+1%+CO@*71+=hmwpQ80 z@z`)fjH*~y6ZXv=nQ>~kwpwqk$zcVPfOxzL5~Nm+D625V9yV@Fb&bo)_d-mHc8jMS zo0mR*he=m&;hYk5G6uB{Cp9;1txIb;sWkLM*;$>^k<Fn`77ztb47Vb=4vrMBsRn-$ zVm%~6`XwPDiJq`>b8arr4LoVEqh(JJUZRh@JaFMXp4jL|5xf_vwC|VR-qzH{mb|2n zE-j1s^FH4~iqrW)0=me4Vk}L^zkVRi`)<O${tm}JMZpTLBLO>@>VG)(Z6zhoTt2(d zj=th~Eec3`J{bt_v{m;I7(omh#2WQxP+OU_ud008+gum-?q+tW<}zeKfaPrUpwtPm zAAL*zrv=mp0_|+7s(pNN%$9)c>{F{N<MioD3Pyv!qu3T&E}3#u$h_3WWM-Jw^*+&~ z1DP{uy*ql`5pv7Cow{2DG4;$bC-_-R8Y`|niLr}%5a?E4`Qy|Ewc{jI-N}11^9-O% z!qH;zo`p_5l=A2>SW|S&V?oj7Q#&gUj<r<?^9u%pOvB|5JMueeLV{G_Xv67Y%m0XB zUrV7rf-99@lVL)D$<Plfh|{PjV8B)>Z!`?eRv6Y9i%2FEmnfAu-M%t3myL-hlwOT! zj+5i3G?klI1Ti^#b|rd+)yk7&s)i3L#Ma$3E2Bx4PUDzGDWWf`=<?B_{>-e;f2y$a zg5HOlYxMGNdCNix6KpJ*k^*4L$wGHq@9Qd<21%6W-?VS>@}ib7G!yX#GrWQKzi1aI z)_`->TqaoX%r;ICQZsshfib|&YCpQIszANFSt?<)fAaqL#XPxGSsRkJsDA-+mgE7} zFO)bz&pvi|q|6Y3A&@sF=myIRoBA{^-8rcY*`28z57G1PR|#?+eyvW@RNUvW?O$mB z1jP%cUKhG}ew>lfei-enUolCd6wV$(3Hmwv{z-g)JE%|jo}F&l3>yf_;hve6P;5!H zBiNjo2B7gFH*Os@W=q~z?BTsj)F}qJ;jDXqRu%C4EMJU5Sg8q0sPJ_q@XX`s!RWk! z0bLfkqvNQvGh$%jbowJIZ=jINGkg`2%HQMx(af3~2j?6|%c^_Wv&qW}#kB3Yk87w9 zdkiD(WuLb`z3V)}ERb<NZ<VWy=&@P2GKny7C}vAXy9|A>v^;`BK<oQKUljoiwI`35 zpj}b$hig2*XfQKpfNJ^W;ETt~o`BoLLww*Qi<(st%LurgML=K`jc0#&n|ViSBfGWu z0j|$JcUhfTzGT_>hM{vfAO5RU!4SezlTL-2J-eUhw+>cD{*AXDU?p1QO{K|w3&u{J z_d-0}Q;^lBD5!U868&)cm>0NLk0qfYNR*(aHoW(sMyJmXQcz`YjZ1+ej%#PD4aTUb z%=d)B^=iJd5@06F2YGBHDe3p2;SsI#1OXZ)MqF1bz6r~?@8tRO{)ugAG+OwtsubYv z=rGD*5MK-s8QQAFQ;?6$bFiFrazp0-JKO(v07eYmnX}Wb)<b7oe_g}aQnA{)Cuo9t zS_?-lIXu@=1ZE^mys7}6bE`zt?)j-}r|ft$?TYy)vGp39S6cMTp>>lY5ju3JQ$S$7 zf1<{roW6yoVE%Kj?IBHY|3ZxK{m(cB9;X_i17*$dffynL$U!tN5Uu7icI#H5lex<C zDmhws$baGdXt3k54e-OCf1#W7luw&3*YV)V#X*BKQeN{Ih<9K}hI~$cO8BJP{{cdO z2I41~UWFEBfZMuWilL&%8tg+s-`~*~^cs}u_FL<-N!DM#>73l>bCjxU=sK7Fid|la z(L%afJ`sL-og0LgyG!5x;k`<ZZkfKq`!1D|z>Wf}YOCwImgUNYe*K_?!o#XuRBqO0 zFDGSF0J6)91^26j@Q()fTblI+{3>_SEJ*^3+a#IY5vge6R_z`>tZ~$&vpH8D{KZVX zXBmbpDyK=8ZgY5tuHQMgOZBW-T{5<aZ1e!f6{>j=lFScH!P>YFJ-rIg<NAj{oyB6u z_n|TxQNP3=Bvl~7L?y2|hCBt8d!~GOXz8d)xdLLeLfAd`%~av2y?=|Nv%QemK(!@K zZPNT0&TRe(d%Z0};+}i8JRCzJby7R--rO}fU>wkLRbhkQxz2?A*i4-!?L99z>okgE z=}sv)Kr7~)hPqOFD&b+uVdu$%{V-)!de7sy8KJqGH5XydndzD3oiS6=^8{=APo5^M zowyoqUI51GHSuZr1T6AdutG6^>kBx#Osk5u%wLVX{ekU$Qc=z#qq!@4-|lj9D0^RT za$=@kd>DI(u#Fl(SK|L<=NO3lf>;N7-dX|TxYK!}RmXV0F^ObVrHG%_x2&cLW&>p_ zcU;`irof75&ol8L0<n6UhA9&Up!i$nf9|G4)PwLB$T(2ikXb)TuX1nm8?tw-sc+!T z>PNd5p7-V3s85(u+IVnLgeC=BM)^rDDedGCy=*4*jy9-!Q77f|0Q_R!YLsZE`$p23 z!L|pqFq0_}eks?xoFzNG6vQRSL8VhoI`ZKmD}kBt6*`DWQ6xr!h6F>6u2Ve=YJw!` zKCQZdiWbfcdJg@IB1^quLJpj!dW1i*`dT?9e%NoK=8*V2qU9A%_NA48wiKS;`$1Px zQ4_znL8lYohsjH34jW$FP&5y0Y_2OFu}V0GmE+H~{vhQZu&kMoIW5AJE#nwy!t~KG zUo2bWgaAkP?MG?cD8$H640ABL#F6qGd69$h47B7e+2=^jWq6P)1Ufd<7AdNSm*TW0 zz7<C>f#jNtrE|x6-M={HH=;Wg;|OynM`A0Y{sF%jP`zCJmz?J<nfE<ww>c(ub@g*z z7ZAbw{>>28C6HjnH5nvttUTzx;Gf?a?4m<G+tw1Y=Un{iXTpz}J1vWHW%F)v*s*2P zCR3{PwxQ)Q6rdtREWp9`?kXmALg=aLC7lN@of<6)Du=WaEuXo<0V^XOyz_+1b&$_| z+PXH8_;XSQ^Nk`(^hs^%w2!pI;|GcoUcsQu!;a2f+ayXZ6~K4WlJwVbiAQ2~xX_us zqzMr-n?kV$5Ed#&w<oKFkraIW?od6o5&UEPypkJZy&;>3L=wPR<s+wJ6yLQCZMY;M zP`~I&&s<nJ+asZOJU<UfvQ&zsxUJJs!K2P^cMn#D|9UFPusJ(VXTud4)}i7q6s13^ zv#uwj6m7FcKT=SX5(4e))CTj^b6h6$L?ieK6?jm3yF6cAZZ+eSAX@|_Cz|k_#p@VJ z$WbF$n8^aCp&=~j*0fr>SF6d+O$d#)Hut~IHWzUeLT9RVojA~8+q?dzOz_=Hsig5S zq5!)(^4?n)3+2nhMBsk}50anmgvE{6Kb6)@%Pm$McV2n+mfG99sGYU%a${9in)hcN zFFf!U#Ih^y6)v=s=(o<9-}m{>_4oWPQ7Teg=;%d_m(g?gH;oI-P~)^32+g82Fj=7& zxF6fz+3Xc7L1JHkY2<eG{#eGKOK9(j3pQ?VI667LylTWBW0Ey_$b}Byvkog>!^j0L zot+!cHG=sm3>RDJ0&}oJUjZ3kiEBdel6y_^tm_0p+|rGNZ|N?6pWNC6@Qh7c{$ILz z>4s^~gb&_U|B#;EpeEB(RkbZV=wjdr!|WaVH2htSOe+SJXb-c=Yo@YT>%+Xe`wLeX zW5vT^LdL*5g|Q%x7it%+j>AOw6?<Xw`~|8ZpvucM&3`!L1g4{7F#~Qa+H6=wMIq_> z>5XF=koq!(ZZ;p+o887tZ*J3QXCQI-x$4<gWs;SsH`EBc>*<>nfeW~fABLphtjq5D z5JtK8tNU;|ts(J^6>b6c)!9qxY99l|_L;sHiO^vJ>^xs0qT~?%io4Esu`71Ys}muI zJ%b-<@|lXW!~k3Igu*Xl!9hz9I|?wq2Ls=;gNPFpS+*96R4>$d)#A}Q%2h^EObHdx zFLBx^e&Om4%j&cVblHwhZ-}4T@^`PZQ_9@!x-~+o=IJVe!U=Y3lFNH<+A4)IC39I$ zq6G$rN4H_Ri10e%@)Yo{6}=2$y1!=mSII7HzK+#V>2vIAF$Y3XGl*DrNI)_I=53f0 zLicFt&C3;skg(A?YE;cB#!7F;hb89yi<>gBhMMgO6Bv_jx^b6haf{j(iPMtJbOj1@ z%#p=Aj7T*~l-(esBE^2u(<tBl!szI;#Te49uztQ%75DYmvcjQOS|5+z&5i}7c%yTN zIaB6VhC00W=U}HA)6=jhgZx;i5Wc%t4xvcMNYjMgmn!yd9<zlif7KO+C~^||*MC(O zhY19ORDz??o3duskH!eVcy^YNe(_amw$lKiPSGedXz=J1YO`i$<MWOyThJjTAy(wW zo<1EfV%sXZ)1qlcl!7qhGPok#bB=BnsZScjV%*0Y=i<|5VerQBfsq~&)7uF^b_1!I zu;18RcW#|Ja$Ud3(b-AcdZhX#vvx@xr3s4n%Ql|IGlZQ3QZ8?6zkY*bIj)p*-HtF} zs^>QC`}`nMqdtMre8enMo!gVTBJyq?$Z=z2IeL%Gx!1`JW?ypfsgsJ&gAoXBm@4CU z%5Iy>;~X3-L#`YmzpaBpFoZA^0uVV|9-lJ9x5p`8{8h-Z_^jmWW)Azo|F)d0<ZyJ1 zGk3zlS2H(+aD^GBENAJOXCD?fx|^1kiegMig1Kt?b@D~xPS33cmSyI<q<2A^XhKLT zVp?!FIT$2HdH2?p_zFmnyKm0grR_@a&PG`GcvgB`;mYc@=={2OeNM%dRg8a)KuY+~ zmyqjd*s4P&yEwFZQZt{*AJzQKs{Ino3k3Z`3$%jFsD$K)-+Rer>Cpi)9;c=8pD^LR zb?*w38KFeABR0n#z*3OMKSzA<bJ?%!sSmhSu*u6?Wd+W-Sm&Gn0xxYJFgnM|NKa3M zO)5g4R<W;7C$dbU{w3YswPx}g+364>j3s`x+?{jW%(+xn|9XcyMrOiLO^GrQTP8+^ zva?JQq_i=n>fZ74fY^7$WvEPH2jc`hQ>c$efdH&exH}@aWl|=~<L^53znzqTk_g3q zlAB#4&7I3~LSgO`n=GnD5n5fdnStL8a5ZB(r?%GilW87gskvF|QwR;>$zNTL#ILrO zLeRTd@{D{PGm8k~tLf2%y4O^0j&Z0~VJMhoVs~BtL~iPrjMr9=Y**l57&6fJ3GzFd z&0{{aKn!*kL>ftVM#JO1w+NF1*~v73h~X-Hz)5GQLw4{NP!P7LMn4qA$|G3%6YI0E zfnd_K&|`HJBa(m6(lTn|&loZ>(G!94B(7s=PNK)x=G)iquS0vN)k2{e&=BhLO~oAm zjzDhN9xf}dKbLg@Mb+LIRt-OQy~~wXU!<1?m6MnX>p3daJV|jHle?6UN>!VA=rlIO z@oOZ0DC{`m>k!{52yOfl*_`CME1Q{#awwbIKQv#~Qc#o;;(SD3m7|aRtFjd^d5xq2 z{%`aR*eY3(Ai8D!q9jZP6M~WTj;!sC{cp!n_@^4n>l-Fb!{MVTi&}PXT%Gna9f$+N znq4z0mA4;=(CP095N^B(o+J@WQj>zIhDZmr?5|O4(m&00oN>1Cd3nnP3eEjSt!pil zGzFrOQVgTSY)=uoan{XwSUb4n$xJoQHxOsVpd2sRNo;nRW_C+4KM6;PNspk`TQ%*T zY<xj_2aKbjVJBAU+>HozDCir<s?$J0kh(K&?%MFrX1uC=<04BWIeVKAbj{3^8TPR$ zNJX=h$LQ&o?ynhAFfr2lCRG-zDk%0fjtG##_E>Np;D^MrMABl#rj@RH(s0>x@(>$m zs}+CTwAxmCtI1Ada7`X5u?hcXpGrqe7bvUajN!)TI<kta3j5F)lGdOS4xB8I|2mR^ ze)(vR$7@tA+F^~)VS*B_I{kR4HTKaKjc2$oSSY08(Lm*y?><-)W6{fy)z#AOq(H`- zoj^BPe0o!wH?7b7leUy2e+p7S8=csJ>Haso|MG9s3rdZa)7QXYeR(<*aK2(urerW9 zWeieRvc@6y!Y1KUFtO*e6*j{~l&u>>eT(;_bbj8`wUl+UB5}OXRzbM4DX^mCtjwji zW784b`?Wn>1L}?PN1)|}G+nlon2E;7?`<%pdn#dU_)sS|)<++6JRK@bSi<X5ZbH}* z#ErNEkzR*DrgeuSy8EFuKH@)W-*psTNx89&BRVZ+=&P~eU6Pn}%B;-9m&N7X=-qw) zf&h_({Ruq&OD40iW8S4~Op68Qlegk7+iYdhU2P1@eaT15`procnG^V<kG0%VS$LNr z%lu_V<e!<j6hhMj-AZwZD;zv{OfDefa-&;4-YWSy^EvHhw?cxNt%`UlKbJ%hSNe8g z3B3J)`VLr8!)o$=%@XJPI-zRjDy{8Kjf1<*76r-571Ss04t6K2I#)o$;dp*XtbE7> z<}zg{)iZLf=0CO-OZ#&2Cb~3gj?&Q>!=r6?1I5NM!_FR(#<R9H{kPr`<zvP;R;Qb` z^_eDVY5TOb0a5W+)3WWFOJ1X=Z|H4qN$GuQn_onfw2dBqZB*CkzK8PI&h+x0P(M>F zNO7PqtOQn@NB3dC$0vyNXPaabY}BOP9J-8q)Jr;HX+&EdPebf@@#P&>uYKCe{VViq zLC!(-tmD)x7OFG5&thkY=w9}KVQGZVV0tcBx88W{cY*2~7q`>O=kSOrUTW44%BB3O z4<SV8;{1Q#&1c$cH&=cqhr4$CpgIkzFxCv?BrV#|uyKJvIRqrCG;aCIOf4Tf+d1T& zY#ckcZ!xU&^h7;ywDS6PR@!+3S9J95N(yeVmY4M|njhj<mKhGdJ~=6VX1LG^=ygN< zq2Us|Hty~B9B7(w+<2%1NTeiJx$lwc8G1vZdg7@n)?y-SU*gvSWopT4=lXRGwoNcF zDh5;%u+Y)5(4ryL3IF_-fp{GrJ~<l<%)=#t|0Pn9jdt!mdmhG}Fq>s`_O@GKwE-{2 z^fgNoBqu%DFuQqL){VP1wNCv{(t^kd)&E><QjpC7^rDbs;GX^F1!|ENaZ$MY4#YFp z8*EeD4$31a`_Y24@Y<BY&9!(6XC=Jxyr@)@?NMggUmdys6rPj>CQ)muB#x1cIg4@Z zOtXi)Z;~r9MmGM_I3eEFz=UZ{*2B)gpuM%(P2q60<q<Pytj&32@1#FIK7#ey({|Zy zY01V&Zw-N!dui#sieu&i<GDKK%I1;_f7^NcN9TrF=l$PKR7u_q#36}my|cUbeueW^ zpUvI6jmGXC|IQ5+keI6t6!d@AX9Xvm#H82*M!q{ZX~GY8>;jWZs;YgAHzZG6Ka;)t zP~v!WZ@EA>KI7KI{3Di}OR6&{4Yi{(ISa+nild3Eyd#1!P6mFmK9Q`RNR#U#cF03N z7s!32lm>e5Ns!i6dwBErP}I8uS@Zav+rd@Ps)9lD*4Gr0^BswA{>*lv6**#L*6qA; z%WIaZRmRe|RsU5PJH(7~7<WmHr9wZXnGm9CYq!5*Xx%t>=eo|)`%9c6-GpnV({52S zrklX43|Snp?kj4G{tDTgA6A!L#y#qincc3kmkzJ^lo@mlg{J8rq;6JVL7dB^;_|Ea z+>jU<3{4`gWeWE~nFhU0Icc$tmMeF_Awsl>$*;J4OuBubq=5lGyhYaK%5^qZ#tb!1 zd+q9enmq^4(P!K+6RAa{j{w;dJP^bhe=FR#U@ZFG5m!FbL9Eqp=>XSG&yXOXW#3e; z3iC%A$jihc5i`l3^2UE+4D$g$SC%+97E8~UBQ~^YX)LDlvqc^gTf*6+v6I>f!N(+z zNC|CE7tvoZ*yF`_{80Zgo5<{@aQ|@!4^n_F|NO%d?E5x`o*M-}>=bGhbBEILl`xC) zAgP&sMRf4=g;Fioq+Yb`HexI<fds-8EzQLF1Sa-GB)+}#Tbjhdkp~amxm3BvGE~F4 z9TqL3+RZr(Rsh%-UVUo2lUrstTwg&!hb7`u35{otq*XJPBpQJ@r_dA8QI9lNJFfun z4ff0ISA5<by!pxBk>bW!)_WW3cKx_Amsf`(q`i}($F1PL<q7&ZG+)$tZ%FxorW@)t zw?)|xcHu|Mq2els1z+c?$#~!O0to@lv`r)k-xAE*Kez*T0XD&*MA>ismLqY1M2E<e zR&HE*<48c&yDJtVsO!WNLFbgIWSI9ipzgkCooCQD!Gn40D5uKzfzr#kY)bAjkg8a4 ztR=__`l$sB$zsEkUp6P>x5wObQo+1kB#zzR0!N+<COcdt!ZY9BKkQC!Ju&<16&kr# zt^VLd!7Ds6nFz$@8Z`|}kB_?dfBv+TdC#PbNrZ!r3rz9G(~13u{Y3gME6B=Q-M&UW zyWM5$d1C$*lgGVMH)?e-$7h<jYlv(0;uASHKr$X2cNj-}AlF@-5O|y3{d?hL01SRD zZfGDdAS{Bd6(@?OJ@u~z$41dl%bzpOf>pn<FD+%}^2d@&9cdMNozrT{S)Qb3{$1PA zY`ir>#4!G#Q=8{a16x|kkW81K=NlqHvRmNuJsUrWafos(6Apx0K4L=#6aJD$ssPSu zZK#Rl$&%Lm_#+Q@s)cgH%BPA&@OqqVHnXUXM5RHU-4zuxk>l^^u-4&m{3MtiiDaox z{ccmqNKato$tCFraer(leCKd6`&|Jp&<1n=Nc_kdBw8Yv?o24-fv$L1P$gwVVmRY- zypa?H?YwWn|MdLW{ReanPgzg+;9(p%&;nZ^9{9?v$Oa>)$6zr2WwKfSEH=kcAq$;e ztJPR6MtAlb4T0a%Mf-ZY``tfc>y@}U06orU6R*T#MbC+=wn`AIePTbpq(oDP&#mQh zvxo5lOy@b@&W}xtpEm+ZFjpDgoRjx?%dy6p3G+}Lq-+k7daf@-K0*$-kg(XCa7vEa zxzf{UpvkF2N8{FJN{yD4*qKLKQT6dgwVox-zwX^)u9<M&CjeC8ti9X3fLltEfM>aU z+Qi^q=^6yPuhos&^i!BvJCSImL~P#-!|&h2&Vw*Ht0LVIF9B@H`C%n?Dv_;<+Mz3Y zFuO)L%rhTq643-87otBBGZ}0AR$H{L6Ac8I5;PPn4SMI-lXTg>=C}vWw*L6d+c$`| zgRBlat*oa}0qyxgLm*ekTQBHqU0Ww8+cUXJi>__m=Uo4ZxIL8h$^buK<RBn!2Wj95 z8>{)lV5H+@{@TbIkjuyC#bCo*OZ=69VNU<+6<O;0KRTYc45XSWFoZ%U0yl`o>Jt6J zni{ZX;%<~CC9GX6%fC{3p!9eZ6HV-d;tcgib@?V;h~nv;o9v(y?dDTisUh#6Yt^J6 z4@ct9OTeAlhbw9yHlEi^der)UxF3JPCNwcGtiU_#dou;J(#Pype|V<n3dlVc@IaJu zMRW!E4Ok1oTKb43f($^3tx{&~3lQ;eaet2B1?=2^-a)#}_-pS?{K=sUUXogX1pG3Q zxm(MqpBvjc=}t?_LU0I<d+*OP_!krSjFkOmR)^RbZ<VzaD2&-D@QT|3Rt09Eph7f< zo}#j$8F;&j%)x(PG5dFvwVdN{P0*i6;Nx+T<3eGr9CQbwSK>W?GWWZ3_|HF|0HiP4 z7mjz#JHvncz%7AQYsf*);$t-uVI+|<OS%oqD)`CcAfP1%`XAEz?5i$b%?c6)YKV6S zW@!Y(y>K33Kw<2J(>^3;hG;-0tv!w>UNQ@Y8$V(#BuRyT_F|w<TW$kO_~Kv?PUe)Q zq4*K7%0;foh|H=rWn@=T#S-PGL2}HM)x4_I5Nw#Ha*@@1=n!g{2q~gj=u`pf-~`?6 zP4`r`V>(w=P=?lCU6vYd_ooMVp|mx-cT|6V7S>5=$l}?*6Uo{+n^p8j&Ig+N*kw9P zLa;<o@0uIY4}=t5k*_rB;$|NP{2;r@=}kyel3%&I5`X<0<rsE`>`3A6IF@v5`<*rE zZ_!2)h5916Clixy?%OsGttq-~niR@ev0XcQESnU7$`<BoYU%>(ojY^m<1}y9!i;`P zOZpn`Z#3S=);>OVTQ)!meV~j-FqRWz!c54OYFzsUIMCGkWdis-qkjItk&GYZK77TL z%#o+Q<kj@$%a<=dx_|r*#V~}1@EG;70A{jnYkPKOZ~gCRb(Iqruft+#<@;Ec$PhV$ zdEzI7Oo>|LK@DS1mUh5Fg{&WiWg_Lo6S5zIzF>1=QL6dqZKvn6ay$l?%~@^WPps8w zxAsmJvycXgxbj0h?YnEsiz|EEK=^>h`g$M`r2%m`s&t|LB`gSp%Gi$|eJ&dW*U6=> zm$MO!&T|YC*-9o_1|Tw;xJqK)>IXwQIikW9KF0^!)r505KzkA4qH(p==zKNpe8%MV z9OPw7(2SX;1nkS7tnL3;<hl9&5<~;tBVx0OdZA_;_yHM$4*Hox6+~gKyRCASSH6-X z>;1v!;FUngxlOTmuL~O%G7|zL-y>S&c>6-S4D^4QG#z+^^X<_Gv**|k*Dsi#I))4L z?a`@lyn6xdFc-d_0NFNsl>6exN`wXn5lXZLVIaOMLSK0fxVijLfo|JSAW+pySAzPt z3Szy7h~0v(i&%-N>yMpVenij#ZD#TrzYFb~5WH3&;{~BlG@Z*;R-%xEn6K|CNIYwF za$fDDO&Os6k>k*EZB;H%-0P1xYkjsI5dM6)R9J|MUmD&&_uk(p?-+enGLyGB_FjHH zN60?%FYiRl66ZsTzrnF=8G>AYJ;ca8bsFBt<xYCx%E+Dz8!*X#v5d@_vp`tnF+d+1 z`__bVSVg2?@ly~+mg{i}qD7)q#Dq0#WwFJ<gQuG>o>&%p(+)QTCRXyuhHc%Lkn>C_ zICT+7tgX=B`lG7D8N@(QQ`dGrb}W`cT->nA#lXu;Q=T$A<E#GtU8e(Sv_D8)FKur1 zFs7zSn-4L^PbZ=xsqWeHK&)iAG5ZZy7R=2Df<$-QHeK0HL=Pq*;bUACsA!2n>Lf+) z^z#>@QbLh5<^2G2w1VHz`dCjaU18&UUjP~kn_$A%>)6L8sGR?mU%#u0r>}oppkE_J z`bMG~BQ2pqm-BI1E1r(e{VGPq!8=rw8y8`Q)v$jvPo@5A+sv_X<C#jm<lF8DQEdc7 z=qvzL*UU%>#XQBM+<ImGqJMxdUrhISSrWrIIfwBmav=Q8=)25r#W+iiPA?E&ISCzo zXqFtP9gT&BWmqiwb#-;6$KMwEHdaBlsH!kiJWwoBY8XpKPeo^2sSx0}-P7yQ;=Z)9 z=xgWV4V)mL*-Q1B+93;J#E9eXEcOB}XfYhP(6dmFp+xZlEAxjoJh%w{cKKFTNJ*zg zh0&B2$K}K5p(iv$PT%n+<}Gw;?A#h`=X=SV-8WLq{M$(7>z6yTePC`B<>IJfkBTh& zYkm35tK%A8_eZwZK~2;<m3#_qss802n8A;guu`d}t6cm96L(}P#yO!?4`gW(vSHSi zS{TnBe`L@&9NKh!8xAs?`LWl`LOoNV>?Lllb7XO-2iYuX$qrJgi#>}{-EvY`U!u)` z_2Mdq*VbkjA!B`X-<+##^)oBEq>8qc6PU__XQYd+@5{4;<fT{RV?h}{MOv0`f*bwt z7YsNLN==QPHB^b4(Wq6iSozSAWbXR0PguKk>H2qtKwpR|Z$P=buL)b#MJ*TAD~son zrPNeJ%_yrWwfqoe%gQB9w~&geBn9Qq`>bmh*d;n=j>BYirzi6*``L_*LF6!Ml;%ZP zv);w+y5+^c*&B#9w>bNEn+K<hjJ4h+RMTf$(0?mf`p1rcc@+GtSl_r_)YLF2Mx(8? zu~uhz;!jU;iHO6v)s{9E6)iru-fn#kjDLb_Kw+R}<k}LCX~i!(Yubml4x{KAV^omw zsDN47x|F5o$Z|Yj`I^M(7D3?4!!RtTix%`-wAL!z#ZqUfswrw3tDk%YP+H(*s-q6w zZc#dh>DECrkdQ|VqeU*^RdjY1bS`cEufb6Bdw6*C0yydF64Mp1W)i#_-qkIRkc7Ao z%;I1m2d}A#*;uq9Njl$n*a)7ByTY9rv!mWA>Yhn{;&ZB8!PJcSQ5VR(5~RkqD*a8n z^KL>%0{L_-6qUL~$^~N(VoW106l;~Qp^4l))ZC1@!yWIo<h#n~`y27XWnZ?5rW^TA z`oojI7(=!2B2!wBG?cyHyq&2D$YA?xj&tH^hXLq+vD}O`(%s?y?xeP}b6l{mc1<5( zU$AjGvU<p{^}OlKSEX<*sKM8@HzUv;!TdQ!t1*z>>ylG>pCI`55>{EskZH7A6KS5i zSxu$fVf}w|_>K82V|WeMgE~5u>J$9&hn1MeMtLP!N>5PaEc>J)Y`Cv9>3n)5K~W&^ zd%ZCQQij)}(Og&mO*nx_lCBcbK1?_1IuRl*+$4`_iVC;%7>+CiPv@lgd|zS?O*WRk z!galm$^8*=>FNk#FqI+1#C15;<*=Ftr$SacvEHixWFF#UB(Ji{dd+dnwz+F{M!i&J z!!*dWUY6K;VA6;VLo31El6y0_S4~Q&>f^;Cq|P;Co<>fEf6ZrS+r(WpzV^2#so>2- z5#Nd^Kji!9<(Y4b=)FH|`%tAdR}I6`5xlS4N+Iu7boR0KTA3Urnf9Af)5D`Bg+C&f zQah|lV7a)p2d%xN7sR1{6M={H_xh%}^5xqpJyAWt<rj-Zz#QEU{weI`_eG7Td;!`x z1}IAwsj#4_o{KA_+!DG0uS3jl%%%B<(%$jf($|sU$}|*gx?pfgATJ^(6NM(=HAv5g z?$A_9SGPo<?5NtD4D`|vBGTPxi!io+!Pp<6$}^-GvvuwhU=NQ{?a#zwuQRDWa{@zK zCP>E&(R1F?{8+{Oi_+4T6ixnBAXIbriP?=P+RwVysj%Rw-Uf;kb&o~ZADPNC_!P!L zJ<vQw3t}6qqz$83Bi>Sr5JxO<XttZot{JPiBwfhZ_p@~<CL8NeX~?IE@+DR7vDiB2 z_)55x#N*aQa2I0}CK9M9N)n}*9vL>n%G6X*=^u(dKBclK%c#u-I{KS4zCK#g*13gJ zKW<y;^}b9smBC?G2d%`l6C38&6O&QHH%8dzIU=8eYr*odYC0{LOM4a1{PGnH&qbNN zbChRn5vk^wkT;4?GQ)59@u!P+P)6|DW2#b+MqxD@gWU-K-7sE}VnTcpC&76(6TexL zn0Mr7N2GWv>>$yJVCx+Tm^}V_7OT#)!z{xbkPlh<dK|h2rq^l?z6QK05j<5)?FyJp zyc8r+aJ*X#&Ahu-#RwR**rr~59#kz}U_zSE>d5mV%Os4HBF2%4g9Pu9rZn;+f2#R) zsF5a}8zCXx{<fDllaDb?(uDAm$b$6K2xUH!tI7}*^9$7vfN{`<extVC@i+G9xBnSC zk0<H)($-r~a+Wv<RUB~p5vfztz`}Wy7NU*&=jh(xb0+TsH${D6+8nrKE!6$Hx_NEg zs;SJ#877rT**3X!-c(8yJgY!#1n-i)QqfgP&7^fDe1Y2Lk_HHlo<4>OWZ|bZMT813 zrU*i$OU1K7X2}Y>JwJMQX=b_KbR7i4NQ{6KD2awQI)s)Hj^bgTD$VKt5jB*^VDeKM zRWP?~_Q7?7Rz*40_qTX&c)rQDX-1*#gKXW87Xj2_i&^QHxH;}Wl1afe;zVP9QFg-D z>R7uL3I#0!YH$;#X`*wc$2j%OQ*2G*`dE2?|D=7Sgc-~<Q7xFsPq7xFzG>!^1UBi# zn9V!3z0Fu+;29<iE)^ie6Vw0Gas^TH`=QHUFOUMOf)YXzo1=24Q9&APt{#q#lHviS zOINeqG2e^iVs)`E>+#hURIZ@V_dyVZC9;wQEs(Fgy3i)oNz)^<C#$DlXGyx<By2j0 zZ+2l41mh`fC=#{6U1T!6FMc;}k;Cs&Q7Knjax>TXUDmV6>E21l;#dD`mAr6e3~zE2 zJIvpDc#K8$%O=H4%}zVuhL6>K8ry%~ry6MUE18`IC(^SOeg`Fi{Bxx}<QYMnu4zQ% z0CxC2r>?7A2|Q=Bq@CR90{5y*6J)7>fRlBxx8QWd75ex26<W+BPtp(SpXN<e)v;I` z^jV^tQ<B-Nbf?bkta9)(8j75WXpX@(2(JjQ7kL5qu;|fHEY~yYWmuIl*~jgb5}C1Y z7qSz-kCu<L^Ckc~=vGgxK2K~H`&@Ij3~b)w0@xq}rdQEJEW2MMMPDUKLN5X6B3~em zsMLes+Lv$X7XaYMWW#2F#P<k*`%e}I01VxrF#KoNR|SFpAU>8LC83SKT^Io9f9(J1 zs7wuaq~3hKbySBJM9TqowpT(Zs(sLabOea~Z=ID~BC9Jl0A2hV$Xn`TXHxrrTBc6b zdOIDJr&TTEe`o~`B|4}8TBif1g4-XV{CvY%dPh-52s7NKJ$9Z>@f3XK0&rS0s`9rG zSot}}0z{t17%-+g*1o{g49C4FOzja?zUme7c;*PAF7mYIWjkgNjze(!t|fZ8Ion4S zqHwLh(2&gOhq8tw0aX(Q{<qmF@aDbFs>0Aw>!l40qLXLUG=6)mT6D(k(vt?i$RVtY zMr@25yERxJ6dc$$cB1kf|GxS4$1E(r5?z5SN!E<8)W`_3H$**h91;s=jvJY0rU^5o zXs&@AJ=+J3Q040M`!B-Y#}T#;sP?!9A8$H@g{s#vx`EE9XAa`~txyhll3NuDP{aHo zPjp5F2qEf1;Rn)Du!Ep}Ie&Ss)AxflZ3>|SX62S+Kd*+eyi#nxx6DoRKNXm}kK%vk zsCVVD;OcC*V;uR1pi0`>c&=G~15Hi_{RD@_dn3^%&X&xk2mcaZ9RC8J4W9*HZ8r$x zQ~XoUGsZK*W8S0BNB0NbzgAFkMRG;fx*v|bncQ4lp6o-R>*47U4B`0^VUEj=`;N6Y zr8g;3BY&#?EdS<Qr&}k(jBJj5jKADtacOL$^mXcq*}-6eRT$db<FCkO)9uJo<~`KR z9oiT@9_l4mB-JoB-ov;f#-LC_R7GVM5}hMwBdQkGnNxBl`bah7&e7GZA@)dl9~(R% zlh$NTtCE&H40IOvD)LrSnkrM*cn78|rJWHzR(VVMRkt^Jt>fAy=*w-F3bHlevd1x- z&vzNqw<2n<IC<ydVU4Ca`E8<m4Efza^Fc87+1$AN%kqR)a7|7>AQ%5BuAG5Smhp%G zF4`ANP|8pk@(;prA?ae6#4M(FpCL9>qaluMD1jsPbibS@=(UyKda8S2_bC%k(sU{9 ziMe;Aeq)l|2F3;TQ;(nmZ`B~T`x)x=kl;3#L9#5%3tq2Bz_)Pk|LG7t`|6T}V~g^F zFeXI-=8zHqj#^M2&-J(@c6Tr#c(GAzb9pp#^Y3?$)$FV_tg)ZIu+Kp}lw*ASVYl=j zX>9YLfzq1;;34g?x`0#!O9|n%NNe;oIn9xh2Aq#@Z!@@cgqU&l2i)m2pp(@fq18U^ z@M33(SN--k`re}WCrVr5XzV+wJ|sq8sUoz6%pvq6sf?z!_Ixr}D9Pv&G|5~tk@7`x z6vJ4RR2wNWak<nd^5>8Pbe-5vGub+?-Hb@K`#2R0am^|v4n`5jL9#U!RaxTO8hcG3 z;)i3li*@%TUT|IqPOORYI;^|nK#Qn%tt!EY0Chme`RH$(w!?V;TaTWlcMSwT48N4B zCMR!We0i^XCofUPZjxokc~m3z)~<1qr@A0|{Qcm2?6T4)gzmS9)%`l)ohD0I{|9R_ mqlx(c_R<^>@}&h&Efk#uf&c)}FoDbi0Bs;&VRhhffd3B}UpW#0 literal 0 HcmV?d00001 diff --git a/docs/fonts/roboto-mono-v13-latin-regular.woff2 b/docs/fonts/roboto-mono-v13-latin-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ed384d22fd9f566df41a3a37de9f614adc7f630b GIT binary patch literal 12312 zcmV+zFz3&APew8T0RR9105BK;4gdfE09Tv<0581&0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/roboto-v27-latin-700.woff b/docs/fonts/roboto-v27-latin-700.woff new file mode 100644 index 0000000000000000000000000000000000000000..a5d98fc6202f5cf5fd8b556ca834e8e9dbaafac1 GIT binary patch literal 20396 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 literal 0 HcmV?d00001 diff --git a/docs/fonts/roboto-v27-latin-700.woff2 b/docs/fonts/roboto-v27-latin-700.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..01d05fa509b7f91526cabe90c9bafa130e4c118a GIT binary patch literal 15828 zcmV;_JuAX@Pew8T0RR9106o+I5&!@I0FAH!06lR40RR9100000000000000000000 z0000QWE+|u9EDy6U;u+42uKNoJP`~Ef!iE`!Z-_r3IGy<5CJv<Bm;;L1Rw>1eg_~7 zf+-saeiiK4qk!`OK_YwJBZ^?-00<(VI*O7_I=$lmZwGXUtYxOa{8dzmQcxhgqwYbY zSbSR3!}OxQi~-+Nxlb=Qqm9iw6_iP|Z9QY{h%&1+21U&k9`&QtFC(nQt*K^99FF1W z`VR|BKJ>s@D8t7djw0;oD`NaLfnAH*-nPC*9Y=GDAEB{NatR{4;J>D^d-p^|GBbZE z1{_PFn1LB_J3K$P-rq*_U`XT!+K7WOVpMD+Y79rl#()teh~x;V5|j!R7-@qDtzuy$ ziV`hg-C_KttKQe$uhpnlOKQPrK$_9g&CYEf(5``DUAe0O!+)W9&+*5|fa?q}0#<1F z96594c%M4I83}=z-9>PhyAV@()pffo(zhVn=7%i7G?IUZaM4@}ZHPq`lK_Y6?05B1 z1-E$!%mTs$BDzgj7+Jsnwf$v7B8tsI_6GSu7MnpWY4yZ;DSw%5-*Vj#VYe?DGfqpg zmX#3qlWTiv7Ah7xN5s2L9!Mk-N7Nb#$z^j)65OmGzVG)#9|RFqgrSnKt*#ZjXYTy? zPuKdhk-?W5r2+f|CRY9Y|F>n9{#(CGX!<5D14bQWGA`lCDc|_ltCFgI{jP2vt6QUi zl0l_5G#Pp%gCv8dM_#4~I0NcdFSQpDgM=c?DcU^qnaF1&Z;*!0_WxAX^?y=SH6L~7 zzGWH@l;pZO+X8)({t!UWGC;W%(!Ea69VkEIAiXCdrL-}9bD2`=uy~l0F;nI&d!`}& z*qfDy45{rcA`;^4{k!}7Wlgkoug*MoEoLZUK!k`WYTe`aJY*aH#*6wXCQ<`db+-^& z4KTt!{@`B@hTkCuAm%(l5+s16N(D)m4#LI;$&mwcUOq^x4ltvBfk0q@tpMAB00Sfe z1lR(W0mQlITRQ=nd!~_0AoIY*fDj<_Xg4|x$Up#WM@t^LFER}90$2fp+6w&jvaZ&c zzi_A)>+@kB8p9%Y3m|qY;c;mQ$4(^`GI1~nVI5(SQVLNLRYi;5M5aV}ZtebQmd?e< zXmxaMv9j65K~lL(q~SI&yxaZ?MI#~)z%W=h;~*pm+yn?hIpm0=jydiWEF2AZnzRw< z(r3UBnI&s>95|wIM&rtj2QQVXRI4HIqsEMzFloxP8TUQ#(5yMnz3|d2?=4!gY{jN6 z+rHYd>!)A-^S^x%?ZzMwh|_hLJ_+whfS}@qh1?dzRQsWfsD`DTZa41(V(Ul%8CIZx z+S|D$F0FBGg~z7y5ntFAE&{dISVqP%fk{kZ+WOE3^Z1BQSiooNOY0V~gk`K?3)}dL z9qd|rt@(!U_+kBQ#jh&Ft&!%t`v44$u++T-7wzT|`a-u2`n~ra3K3yeZTfip;eCu3 zqA|`5bL)xFn34*UnkjZy<<tN{h&W@S+3ErxD1ZnAv95((XkWxlwJkaduki+N@$Tf3 zxFHF+g!N*GtsntpSb<6wRKAHTAypBoQDc2+#Uhrlj1}uYNdIHMH>9d60*GxOZ6L!6 zROI6e7O{k7tn^2)B#uU%8&R7v4@wKREM=>x?Sik^!S2bqAj=?z3%H0{@*5l2zy>xX z@b2U&#Hx@$hE+_7g<9HAC&i6`d`EpDEMf`ESh2QZ*~VAwVE4_3Q?47WZEnqj6KaWZ z-}v}3jAdJ4yatk*sS4w7f$bUL9x<*g6R~rz&<rtMA6l2B9<KDS$8JPUcU|fkoBD2( zXQN115+bL6E!b0l0NN)4Ia2EJ?KnU8#hts9To-1q^jth(tyQ1BUqiX6$9?NNoP4(J zvSWn(Rb?x@-gTd)%#Ij-1>J7_`l%n995l7rEVL~=r<MQ?W#=7y{o7idzUvdm>)k0j z@8O+y)jZ18>GZ5l*M(wq-TmBWsZVn*whHTgr7qloc(~8K(r?}C7>%@1JyBv!w9r>V zw|JFk(t!$2#K5+`D1}`clpU=ufP}2SG2x(pACV1mUporOsCnILFXi-7QQ0Kq&2{x! zFKg*_ICGyKpU9tP{hzJes0}t(Pg4D^dh83tDkC3?l%$~M^VPQRM2h_%_HL}+JtbZD zwDs`tZZa~|3ox^la9b^M^@BB)RKEq|o{!dYV0N>6*?fn-CElH8Q=2_+9y&)UFal$% z)ReHhnwEttf}Ue2e%hA?^m@J%sbhEMcO9z^74sO-gE(#oU`qi(3K+4$aEBa&ph5#5 zZ3aQTIU;Ff2szft@*Fr(;4GB5CM$F2>7d-?BPv~VRP9@<YSfBTHtnMNX`dSyO$WFn zm*ldLl@Kt8g|s6=)KLLBCb*~yey4;0tdM{c5Dg(tTL{q+{0KsZu8^W9ko1Kh10lj( zaI+9Rh`|95$MFIYJ3^p9z&Qmx-i||oPeO?C5PUp^uw2b^ax6=_04;lhEK9lwA*)u% z8cl|bnFcfS=83v*7MD4%Kwf)?-}^0D&<CG!`;vUXq7}S0n-R9`f}bx1pZ_wFY;sX9 zS{5ftLV}nOcSv(X8Ri58jH-}?6EJuIqZJ4<IF6t5@dF&iMYsS*5_d5f#ELY;8i#X4 z8IB48IV}Xa1Y8W>K3>kNg?_Lp!{m{G$!~^~t02KC7v<tyl#7?e-Gi0@&aGi=av101 z0^pfh0rAM;636cOfs6BoqCX<tdpC#CFxE&8!G&i4A5}bO-E5A=Jjc;V?{mJR@xdmS z*;yY#yWH4WTy1AlzShwy<B)KXL3|(ymw>P3{xBp~hGE$A=D+vx@K9jzU-&EnW{=W? ze1L6}JN5Q{G%O|*1V0+?#{^-6C=x_JiRaHeTj&%*azLkb&4&V}OK=bmq;M_{I3I_< z?FjrN9A_(Dm#-%_4iEbg39@1nxB+b~fskk;7!1QrSUWgNHd?{t8j2I;c!#jprtK*+ zmUjNUz$g$L>8{`Y`0HN)_{xArkRb;KMw~q0ONQ|4I+A{}4v-*8inN}Rml=p4vePg% zRS!OrrXma>BX#0~1fpHe053QIg_}qq76!7aS8j?14fpve1E6|6{JM088+GI8(W_6t zqE|uYZ60(+lxWvncf(D$+y(>j{dH%3G;ZSp`W=k}#=gS2`F(-YJXL?5UIq>YTmsk+ z;DRgchyV>N{sjI4_s`V<(4hAM)EUizh6e8msdw^_1c4ZD{?F<{M!4_-wwA2kRh@=S zcx1`8Nef;l?{NN{UpvaVPz-7?Lm0YYHEf21;pT8QypqoJwoYw|X8!;C=l2hQ0j*wx zPF?Pr^w_emR~U|T$L>fWirw=fm0t1aM_*vN=RhMq$k0}UTqDfzDSzO8g&%X|4<zm5 zImr^mn-mYK^FQaGA4;Jsx<D=?*8}7O_?G{w;b~KidiHCE-+!8Puf6fsJMU%4^ufH3 zK3VV?Th^~1c8l3^?D^)qAAZX9%W<ok{bkGh3a%PAC&@{GGj9SF=))M;8cdx*z^}mb zON69_ihLb;@+)jJJ%fPH!R`&J-pU>!UzIQN|JN{aPqFh+M#*j%UbQt!K9W(oyCmE7 z)l0S;Uaf5Z)eTMy*}=YUVTUShrVGV=!JhY6+V|z?65hdEeff8?t1p#kOXE_coVmo_ zILb4UO0?^T?`%GapTT!;c+=RyTe;49-$eDLMxyR|c$NNYlGR_y<=pp4MiWPE6FhR+ zhFk_QpKQ4)X}ouc_fkFu@3UXTPNdz-`*JpFM|rLCtJnMyfve-CCzS2KYLD;N<lg<( zpO<du?(^@9HV-iwRl7Gdxnf46>-U}0yUR_bfWh$|yZhl`;PYft5Bq*8&^=!gUay^B zhYkDZUZOfSS%=q;f{>7}C(_;JOK<lQ-p}5MS4LwPJtMV^{L)G+%V~Rj)KkR@_!nD$ z3wAyS)DZyj8#wWK{Sw4iIs!0_&>sSZpO#!=oFGgr0ZK95fx%4{HIkkQY{Q)(94wVt zD_rUtz~E*v`-e}B5|=vEL<mNi3)Tx7Y{8njCIX@-h5vCmR_t$zhVuoUxYSu(D#ME+ zvl=a?PIOlc4oxZ=YwA=WikZG+CCj8-T5a{(VxA_M&Ye}2%hZv~MQW9nC<S6~yj^Cx zmes=KXi}Zxs&Llp%(#O}{m+%_8MZa5&9Lwd;)&H}?y`kWGFK}!+G4wprO{5LZl*l5 ztx`^Iw?-~4T3bo!HMVwTrLvl4>96F3z%i9rV{?c{I77r%IJ0kX5ws|^thDehckTuD zW8T`?n6&*JoK(g%Q*lNiAv9+x=Q??!`|bQC(%lxJs-&ok2_Q3*rA;p)PFd~a;=lj} zESDftMsorQ1(9yoK$!B?xR1y<r(9$djLCo*$wkC&9j6bBiOlBt)0RyuqXS!{*fRmT zu{OmnFw(9xy!sg)-DS9GmcZZ-PU`sasR~nl6qsB%uq%yTsY4qjVqe>}Zs9d$gQYw$ z+zYx*QSX~hLpSS|l_NjQWy){l$hRdrCzmSd2Sae2tTn`fcZFT7n(j5dCmTJ}1?~9y zdV-oPckrCJzKY{D_mas;l5Kk6vt2=IJc%{`5rnU|Hy=}nSO7WFtyosE@*WDq8dT~_ zkTjg>l~t=#eZGLgeGHTnt@O%&E~fe<XwS)GkSJe;W62e~p3V9TzGjGZ?3&vpy@^Q9 za?VACBk6WvVE5C}_}WReZh8_Q?KwH)%#a<fVy&RsqO<N4Vt2e~|2q_-j0}UX>)dUF zl(*H)8Rvp@U`JIB);c){UvKAZ_Bi^<`D0)2f7Pg)`IXS(b=W%6C)oTvh#$<izM1lF zmAYg#l~>iKFl+Wg8tENJjeQ37M4(!T^N(C`BOh~LBUh7`0Q(2jX3qnFL|pQ-B2Jp^ zTycD;at(}hN(EU3dAtgTmhKMso;zTa=(F^?)Tx5+DP`oYixcC;xDsjSp~hgWE>C6O zkW2<sU+Ua2rrD_@5zAT8yQe8BPJ~g(r2|O11T;+wlKsr@g6v(&%*w@QRU6&=Ry!Zj z)n)CxZIHA1L`!?~v0Y92vRm4uDZMG5-vYTI%M!2WV)BvmpDn3k|9xtuORQo!Q+!9C zUc;0HkJZ+;91Y}TXU-<m1?llZ--qjPRyG0CQ~+Cg8c`qREg`PcL~PTviZ!N@@;`~q z6`Hl3#M+v7mJrU;qDPVD;Fg50&^*#C(~{86=<~pdP;T9NY*P6eQU4z?Ifa1fFx7Pq zdTP486jXmwm7DRKD-5-!tUzV??lzN?*=irHaK%K4P%NWqsK4PBg8(WLZVuGOf-Tsf z(zu>7-ToI7Ub}mhg7gMTQIC}@tI-dfS|q_n=D8Ze@7&BRH`k6H&mt6O2HSF;1!fRV z=PAnYlrUG2WtKDJ;W`Xp+t--nv-r8cm$xzHIpGk_>6yuqJ%yM`H%O$J`kvbPoQ*%j z1$)fOvV9?CworQG%WjSUDkMXZCD0Cwsz)f?b81IhPZbg$cjs=%OJPFU*hZUe!ZD{p z5w*sebyCug@l<^}<4*>Ob(2+`NUXtyV;fTkGdy$hCQts2z|w%F84?S#$RoTmF+OtA z2bKcnhW!pZN<{&zA5M4{LNS%hK23u=liVFHP_x550I!(~qR1p*hWldu=`EqK0f~$P z9`>}h0kSF9PW&VSy~9ZqW{F{%61vK-#i7~~AVI<LG#C|16g#R>QpJ+=6#rwiHqT$Y zus@Z-aaJc2lrgUn2mjw)pIyz`WD4@v9oJej>m<)E7)Ed>${Md1`>8sn`D1@q{=4@+ znq`*B3^g0Q5n7@U*Whp-oCNmjZ{R_soIuhiw}Q2>hn<<cde)(mI_h`$i6-<`pm$O; zWyjZIi7V(oO*obAzMGwse84Ojly}9pQH?4%{$~d|>Hw71Di)cU?xNuZvFLaZ2w>-V zM63oXD6~3>ShV@F$oby?apJb3rXhPy=R*HBlDy5FYzrdtH1<X9uy>|$j}-e6MG46n z(QYcap_|6RAEWd*$*Np!%KCoumNgW}hCfrAt??)rj?G}GM}s4QW)UOjIHMaARVz;5 zBx-YKQnY3FTz15AKdd81^3Vz7^L(phnH5>RwOM*0m_yF??>Kxgn9k8knK$1{&0e^M znS^|(`BiqQS0}naIXA{B<Aci8UG~oAp;u`yFKT&N(|+1`p2*kX^%u`rYYPZ%Ra0Kd z*QD=JBY~5{f1#VA_Q(0e%Uh>cpbd3~tNB5gas%e<;?y`l&T#4MM#fh0jbh&^&yk*w zFBr&b!m(hND?xHhqM6hDu)-><)i)_<*vfSIb%GVxWDn`**`6@%)4FkgdpaL7Iy4ZP zREj1I8d-g%Ll{u28CW(f_oK*J{OmDQM<~{RRsEI!z6J^;D6Uh}Ax|_~qCg{Z$R$)< z#oErr*PB-C8u+YvSVk&7_WD#Fbe$E&i!DpC$g-?DIDK{CFPELhvuQfGepcS5#fN8R z$99ktFa)BVS2ML9e?_PKC8sZiDYUsu*~631V>f!d;@`<j0tg7D9gBg$TUspzQvVX! ziWN&LoLX*Rm=pcUvs<mtQ;^J(7tr3Ki9|m#MH?~YgKRyOX5;0p$o5T9a;l-^MR^Hf z#@9AWX3$jfte&rlopFtKHa#ySM||gG+Yjk|-|{}2xWhJhrP^-(>i=$Z;_F8P9P-G9 zJmB_aLtkOaI~sh=g=8Al)I^PtNrN(I&U(m}*dx`Ahi1%j4tM^v%wHHGUbrURK3|2@ zWLLU)NOgl7yT`6gzwP>eQvP`LZ))1rd_-<h->X&gIp^r={o3J@JuBEC-?!uC%U;nJ z^{+Wnlix-$@xHMXM;CWT>(g#qUKgC`46SUp%g>(O5Sy%<cyQq3kB68zRx*PY;2z2K zt%}{*nqS!2oKMQ?Y|1a_YR((x(laBmjL8O;dd6gemMO_tG5u6lcXM7rS7RZGeWkgu zsIw(!7_LRM(L9r7t!GMNnUV~lZ+TWK(UEypSr%CVtSc44M$Y6a%H<bxgH4Y|io3cq z!tMW8CPd}^kW1@+{hMS-t*1YE1z%W21iR}fSlQC7f=-7qAykaj=?H7}78RmDnM^RS zw2KzShsu>+J2EGkXt$VfY11HAU#q4#q-j)D@l?ZjX}DHZmfo61Vk!JB(p!5WwPDXN zS68blpV4#&hQXg`oGgxb+zL(jiqEKly+a<L+|!{E`ZbeK?qeuF)iZ=aL)sI*TGoJa zpLA^q`h?#pIv2ePRmI?9WT9klFe_}8jZ6}t_>bSfBnS)rq*(tyZWSkXHs9QxYC&|T znwz;fk%(?i%F{_NG3=8sQKpfus{a}8Z0+m74hpLM)_u3H4LgXI9`J3Z2YO$GwfgpY zUkzb+v_O|h7>p~8vF`d(kw9LWc=6qv?tY2r;}hoQ%VLKqWhTE}PAdKM>*Y8zd_Jqp z(mm2ms+3k8Mh|tP1^c*D!d=Oj$TR)#0y3Am-re(0X!BJ$b0WSw{+`V0>KpOJI~d0^ zL9Rj3OxHJGFBI!G9&A}{(Z%Re1qTl84Iey=%QJzYE%WbFK4W)Gm9+ykqaUwBEBUb> z-k+(=_{2yXfhQ{Px-$J~zVy7*q=a-<(DxgmTKM5Dxl!2GxMX)!OwZ7~$cKV6iyzMw zEXf!O1aI~-Y$x7<)mIh~q${-Ih{hm~$dZ`2sA5mE1H#I9VKrs5iMcfd&N$;FgWlwh zrK>0QCfwusIKlU6Bq6acSj`<9+{C!~^WF2gUvH2&bFa_{5BE@(w?{~Xw<jZ1{w>ud z(9yxgk9OLH-b|yXU?VM9VU;7xyq^k_w&pYPislOwg+ONrlU`>fWC3=*#v>bsEc}Tj zai!r6o~OfW<KviB-rU?Yy3Ujo{z>SDN|ur^@?A^bNWRqN$Ze+CjD%)H==$~<2$Pg> z9m+Fp>95!I*Y3DzNppqll%S#Amrv(bAKao^Rd~ADTU(L%ee~qiO<kP}T%DJ;U&8+d zd51bm@&8-ejXEch70vw4H<Z+UM@@@1r-8|w=t6(vHFaY?TTP0dd*TbOPNX{_7C&-w zv@@80MZ`xpO_wro&{*Nng~?egoi}Q3uvS1(0QGqwd+X;u{Y77)VQLhAx@oqx+G(ns z{jpcfV^PVY$-1d3!hR<VF^e1tr2`y8i~@5IO-ut^oWFdcGh-p4jXsCMWm7s5p9;ME z3GpT5K#W*_m#JBp*s~7{@E*TN=cK0<cEyXxHEkgvWfV_#d0J{Hc6snOPDpZ3g+Gs- zoLf=$0{RrTQSiF@e0DNlhKUdP-Mgw+I}Gld-fz`C)j0(CqVGH?=G<6>p4)u5_N7PH zU?I+k-MsS{aB*h$8r_U&<Lwk9ii&LO-^-4-pFw-{b$jn4jQ%@v&DA>4Gru<_2IB2E z;mSW`)(@hRvZLZJ)GdMqkHyZ__6{O(U-Z3xlp_{A#3HkZnpJGcwkkFwU*21JmFFCl zujkLq%}3=gLu)TpLtFxUoC9J)v1DRRTfO32P?p{w`5^+q#+V)pu<rbNcBe!39vwrs za3uZFl4j^!z{NRPAm1fbravo{p(o#2(0Q?oBN{w(Fnwr<=v_NldiU-LYsZCaFDp%D zx~~n$cGj4HC#09v7BXGTMt1MPi&Nmgrp>C#rZuoYoNxMGUD>b!7D$}{BKiA`cJr^e z%y;=O4*r01?+V5xg$IY6O%H=mc8>S1JxEuHcHOx%)Q<l}(&<0CD87B*dNU>ulU#gS zo-&a}AUFz}py9l`5s8rjLD4Y@JAPUY$<#PkEq_<+&&<!U8P|UW)m+?Tu^*<Z_@51n z3`=mgCKh<*9{Xp#`uXx5`4_wGs53>+^H1)eL*kqRt()%*-BG!7eXsG94Hsv4`}hI) zzY_|!KBF>Hn4OmJ2hKOGUlT0eOte)T;OHopb8NKjpk_fc!4u+mj;ipp*+!`j-oz7v zPW&PW{=eQw`)(q?yR<#&eeWUp=jwYH=?rk897#v37Lw#Y*CW-C`lCr!8~*xXhsrsL z5PGy{cGW^DbGQI-;|k_EfM<tu5O|y;hq+H4MS)c<o|`YsbSpd}hiL4aS~~vXX;ZU! zL<bxf^8bcpl$~ZaQFvX61<jn`==QqD*Yd9Zz4jGk8dzDoP{@wv`WVlsx@WE#Q)^R$ z&%uRbwxVKFMj_gDH<_=6%7Lbpd4lNz3h`cPk`v4pj6?u#fH~q9-8Au0i5HD%4yp*s z#j&nvM4Zn%kt1##h@G?dI}MRUKxoY11OKEIfx`&Y|9r<*?tfc?hD9KXU$A39U`dNY zTR^dm@xea}C&p_7V~#3;BH$8=3}Ig$UQ$8-sG>Inur*m%@T|-ye%`39<H~z-O=rPU zZrO*wl2B}q5Iyf4v&|zuzt+g@eIS2&yfY(8IHW`p^70Fg9SUriYj7WYJat>o83miy z)@ZtL`sLiqzyPy@|81O_?)6O&2;i0|NO`gvdMWOcr;g>h?vVKEsB>o`2!5=l(XZIw zXx`G8)}Rb+-$@<c)Zo~Tbg9y@$eb>3MAZPM)5DJDaZcL@!hj;cSk@~Ml+-1Soheq9 zrzyrJG)f7LVkjd{i0QjC(Pw3fT+}|2em+h@ZBvEwrOE1NPf(C$Ket*?w20znZtQ#Q zvDW7GN}vdo<R!llc;h4U#-7+TM>Id<TzN3tr_}3CbaG2mO9V@p9#1mGB~dfr4~7sg zSw`SZiYgkt+BOYM-NN*{+c|oi(F@wg5S)w;Hze+_<W#>E7Z6#5$p0NsnUgpHL-SNe zH8SZj^*xm_Eg_7kW^>c0sQ(vKMw<O2{$halMv#vle70li{SgG3tCG<an5`akOM{X9 zh-G~E-@@TUS73%1>o4GXqx8p*)i|<v0{veNv%xP@lRk_O$nXzs2vylT(RQ*~J!}j^ z5amg6E~~~~Bwjy`57vv4Ott@6v~_o6_QyyK#=y>=O13k`VO<F?kCSD-P#%^28Jy03 z>K)haD+afwJGfAs3_Rq@&G>K3-H_9JW{kCX>m==p<x(fs&eife{Rm~G-su{ggzk=G zZgo_bY6kpOA|RdM-czqsCFTg?>NTip8;|7Vb0kI02)hs3MeX!&qGVq8c=r=;{vQJ- zc$}!w5zV`lVTu;-rqx9?DX^-^dQDl4g?R60^M*!ZZ#j<kYIF=uvR_}z?B<H-Y8vNv z-PW4l6$^4ukUw+sS?{AR_{7|ezR!K?TJ=`4Ho4H5rwhHVy>6f95UUlqlWs!YfP1JL zg&D8J!`J5)22T&3UVZ{!snpi0?^Wwt0M}l^C%PW>K6%aK;pKbMnj(Gk#&W4;vUW?Y z1%c>i`vSVz|Ea?EoNi;asjem68rPA$14Dhqp#~0_x>57osO;mmsP3@tzV(n$(cP!j za%)qoH|3uxE=at2g1N2uaq3f}-1YZRBNC$e1v&%<lz}=-dti|h&X6lNQJ<8*qrBrz z6m3A%G;UOLFU<T{eKfc8@vb7(*~P)c*~goA0lyJ#iVUeup1FVNY)X1wAv@czX8ZxO zzOcmUl3!FEIdZQ$y0BEblvXfmXGVN#a`VtdJ2EjgWM(KjJWNS*h1j@|sXI~OEJ*C$ zbd~&)=5ad4({H-bQRptTgo=cEmwJ?X_cxPi-H3BSvIyBJ#1DiNdF0bz1C1SUvs-Lp z7gBhB9}ODkJkPPBEDeoQ9GL#sLtV=m{SEF}fj6#5J=y*#@@X1#`V1o^EY8M^97c}t zxfkkD^(4~rn4h_$W#W<dCJw3|2ClX-N7#~hD&B^qZ(v0Zwz5pJwtRxqv?mdCZOM2I z^h-s-{Haq_SA)^*x&DZ*p}E?otIg#Xz1<vX?*44RN{k6$ySup9`?h4XmdlA=ZmL5z z)hIeuD<WHs#5yV!9guA^H(e|zQX1L)TVdwG9aqx$@Ca%0mP@(Y-RfX#hosoFGxV^w z_|8DwxoBgOI`*nia92upR!w21o{zM>SL#v9!S-y16`<;>(O7z-u~Ue|E2gs4SJaQ& z0eOEg|9A1m)uGZ+y0SAnnZ?qd#fCL=n^queM(VANO>8b@W?On1YFJnh+(CW`zJ;f^ ztyh}o3n#LnAuP1EeyR3iD6{FJ2ru5u0<U3a#UNS*lZa}^DP5!;67vt!ZpZHQAF*+S z_qw-ne>F2#0i4HDB%Qp}J5S9<U%r%8R-Y1QL5vD<E{*hZ6BUnejH4;%s2%T;e;C+) zCcXG_Y)Bkit^8;qeK5JK;7Ug1tdc2KL(fdvJps6vinRh=ar}CY%org&**?~<Tq}4V zLlELJrO~nji7MAIz<cA6%WJ>H`{%`TzQ0SyfyX&<{<^svwZNU4>)~WUa&?xbad(u< zl!OhQYvpcbIUF}putX>nU`47Q9K3zBHYOfr%;WD<KXgxXc%+q7ckAAz+B>67T?k+I zP<va~5a4a^;~r{f>&^_sBQK<7>NnMvY7^ziDwnElTwNf03^cs+;_37CncH<$Ro?D$ zhkYQLvf8TrkT^#dfm9*up;l{(+9A%b4QFBB9~&DYISd=fMN3jA(I{G7`%}}=c1jbg zsa@pKSo|rxHpTJTs&8|exl_^Yf}OE@!8)g)w|1~Q*MXdPD|4ZDoI*&^y^3WGeIx0G zJQ3ZtE*?>NDEQj4xvEEH;|5ru$^>u(mKwlT*trMn8S%Ys#I7O+cpUUz|9nhp%vf?< zIONifv3d7%b76RC;tG?|X>&-=_Owk!W?9eT*h_~?igEZaKDY!s3dvB_%-O-XJg&k! zwBOEx=4e)jm~*xHDE>3I#L(=4&DopJdm1kNys``0b*$~FHjbVa8fdX{#O=q=qC(Gx zPeOA$+$)zZ@<-(+#Mq=MI;&XUCTYDe)L_l2MXC7SY4!wR6n$-b6E*c_S7xLjGg;2l zIp72Sfb?H3kY#x_Lv*vEv}iFo<zx2pcmCPz5G!nDG;1iqCZf_iidcHsb$;_yPx@u1 zb;Ol7rjDf%k!7i@xB}mxg#KDrGj(0W5~4}b%#}hSx;vVix;R=|ou;gzX^*|~a}wYr zXI{_{_;j+&;=cnbYhL!!#L|YqXS2m<owTDiaFqTDMpW}qW#mDJe?lIm$J7U#?(Uvf z^ke-|0;PH-<-W0TV*U^hn)~xDuMB*^Fg}?5D6kDfz{tlKf!J15LKmu}iXYal-&uha zFXtq-qN4X2+zi(Z*-d0`dCwrhjc_4aMbmX~70+qCRpEHJA>se!-0J1?WtkN(pcmhv zjoJbcR$5XJH52WiVtbRM^V%qG^?X@Q*;Dc3a5PU1tC7iwsPDZH)x@AjG!Zz>Ha-y| zjBptrcb{Ud=oP`G3q==-7LZ3!?K(U$C!JGcJ>1ixY1DI3UOs1IA{|v~{cBZJyL`Kj zYX*FaNU!obTpy(MPF5LGZQ~11tMO8>jnjE6t&CPR5nWQsvZZFh&bKXAcm~g51eJH( zu54Ig$5M@}-xJI<mS3aISy<`77H9!9--oEjW(E7fnXuYdwG+VG<IO)39ZfBT6~Fu_ z&Ol!NuNB#Ug453iGgA^F{}VxJNep^MVu+-Tz;?k+$xM`F=1nBBuSWE5y)N1=L|$h8 zl;vIGLuoA_><}d-r8RUX1?8PYxeOk48J_))s*WV}8NGts*OTC;rzt4!dMLWd%Ajqk z?2_zC4KsGbyhvf*J}C;d(cx#q%N0^KdQ!U!y0e*;VTMXhUK?Iw11HIkj5>7x$GcBi zcL~R~eB?rq1~Yvs&*z>8W+@SrGBwQ6R6fhGHuCv0P}2D=IzhLVXXYv$6@m9%$@5t> z<=^i(``!@K{+1WtKhXX$P$DRebYA2DhSp}D6}iy201r*&voi1KH`W@n9d-8}ee<Cm zePr}?hwxg9eAta`nbKNOUR0M0CEph-J)v_>!^>UQ)kxLNdfIj7@{DC4LdxKHt$svk z1Gzkn%%6WR)9k@l*SD6I*R?<;oi$Az#U)KwF5Ks>VEDn=<4&4J>H)AE67u=Bf7_|L zAahz9t?pr+s{q@7Yc_1QaqhXLa}vJtXQRD1d(Au}C;d!fN7eb%z6{8J7H%F89D%#4 zuYP<wQn~3+dr1ma*u~5!BsQZUA^PgY><d>KVP*P{_tsWwZrEWDCq|z%)>rcj?rE@W zcx4sV-g*vKXBZi#m7<%%Y>Y21YK>>6rV&^=tl&#={IxMRZaFZ59FFE;Pvwb)kWW*| zHm9jY?^I22_KD}y(&Kwt(~Fv0L6z>Scdu4On;0JQcq7V1LoS;bx(dNuD<m3)LZXn; zD3r9}b74%2uo7l$qGfFySD)5@tv~MP1sj;AzAY~9y|MPj>{3&f@zx&|HrJnm;EtE{ zx*4n`<LfmcB(#CU#1=s2693B+9Rb_wHn<^o0S;03OR%{zd|_ktE$T->{h>OS4^c%M zHw;C*W5$Ut;B~}zM``K9sKw)IBBkTwuEj}f;HJ8%Mhy!se*o)m8-cj+N`dzq*&C}| zMU}$~HdeLGepQ1$A~sux5*r`c93=;EVLK4-H0Z+HOpe)O_L_ZWzcEmk++_X><X}0# z!3Vwqun|!+hd5;860;#PPQmUzW{ll^fjh?)nHU<(qcglxunLFQDCbA4gfX@({MaRC zUJM<$`<QWl{{k74#JUbrC$%4ZVZ+_WAnaeTXt*%G*oErO@|6(hC6_mG{HclK_e~t1 zGjV(=r9(CDKU0TynL4~J)G7JBjK0qVp?N9N0Mk69g+1A%;J9l4qSOB|`!X&8F4qTp z%4<(VZVWP;O3wkH+g2<f-?QLwqBPD8&$B!m-45nYNFR2iTc{*=LF;2FM+c3oLW)<s z#-woj1jV>0K{2jQP>g#+7ZP4+x^U}+OL!han#P@>hbos!dC1vOQjZDX+`NiQa7ix3 zq?s|9ow(eXJSFZ3(yhxyWZHVde&CzW2BUw{`-SEE7%O)=o6_quzK#E7pY?eWRZ{k3 zjR2q@@5`Kk{#J8%0@GIg1y$ix&1obwocd`RVU<*JO$Q+5PAp6Syk_~{G2<9~)WqH9 zAVz-l_I3EE64A+A^$;xL(Y^y0vR+R)HjTXKqEmY<8)ezUe@e7^Odt@y=w4xCKN{i- z9-wcXHD64e$^W}k$a!<U_IpekP*=}7FZDvp=YjDxV&x<8-rUj+d=j0NkIzEo&qN<E z<2WxHtvYLhr?d~h=+Sz-I@qvaK>ZPBj|cYz_UujgBj|M#lg;(~QMO3i<DoDCkV{>b zj{)Q3y@QRBc+2Q=32#SD@3;0iI#Ap8h6x@I(+R+y{a4HhzBfcBcFKZpE-A<XyI6+# zdZ4T`^KIB!XXMUEXg?pQi+YUFvci$la;-a>gRV?~c+0PcMJcP)yz{bKpVK>dWq<H0 z7LC)U?cK*mzzEo-Af<fh5`LEuTwW;sB(^<~ZbZG@lLu;GmQ)OjWHJFZ#Gs*j8I!+0 zeqa3S^tX952_nIs+J|gn5vp|y+oHte(z|wc-+owRedz4K4q5oLZv)+vT694-KC!gg z3;3^PoG=Jy7|t!dk|r}xZla}1V9Zj%3MO@UnAJW&w|KB}Q5>J_uSsK`8W7f4N6>Yb z7fiRL6S-d79?gLm^yy<_*ShxFn>f<;*NF+o4PEE%>6J9N>pr>0?y_}FoRZsdw2K%C z>|=i)F)z#0=;)l9d~)X9v<Ca$$?C^mh7))GYG~Z>2D5K={V2$^s2Ogj2x>^7?p#Nu z;#HMSqUbu$@zO^bCkB0V_w-WUCjk(ITkec8xjo%a6vI&(h3M%>*Et}^wrP?BaYD*4 zMM|=>qql&JDoKP*vQ*lq&}Hy6K9EGiG#ThxW<w4LEdhmzt|xshlJ3}ihNdag2NM}G z8Yp@#f+J+NzsNKA8Xp7xNntb#yP(W;*Uyr7)6QdV&HEW}+c|<ZT9V}n@_zM*w2)(6 zGq6!y*}`l%!?{0qP?D^F2#N7AaxTLV?R|xW@_2@9dnfomK@q3q^GRji!eXfP-AFwY z)nV??&-)*Nd1}K|QkW5a1WA+uM4`0Ye3B`;$i&|WNtH&pu^twY3=?$d$UT)E5+t$$ zqJJTiTnwIDQ0qfwJ!5iRzZetE-s&nf`4GVlh5;O87?g%Au4FZXkRZts_oM}h6NC-$ z1_iHfYK8~fCk{g)Ftdrm@>ms*K92FR<A=5>;+f;^Zne7+1nag><Bq4CK>t8E1m%E4 zFgC7yu4*r3I#^)QrB+!Gz*^((I}7jfCMe62GC^kOgDg@BLzhmX&(4b|Hsw|u`U6n~ zS?j3};K<vEMS#EsbJFNXay_>{r(xifMio*!T311zJbU>woRvnDqtepMJ^3BmoC=Z1 z3wbv$OLViS(|`vV+<3>0=*1h<61LuUri^5qeJ?rbwl~>FV^T=Q6%I&I(v48pmLl7J z0osU8m(Q0^Cdal;Hjr;@Vm#AqkK2>U=At=A-((@!%x8_`m2UE5%|i#lMY!78<U(e3 zj=<RhnPQ2?ZY*_R*y!pU7xp?eyGfj<-g;XPjjUKA%aYE6X=!$3(xCO-CdQfhcEg~c zfSM*if^v!6BXO;v#`o~JB8ZQwq)r%konxFPooK7D>!qX!4w=t}m%Y{hXdmO5etRAU z&U*s-K68BE^Mb`{tcAr*>E5O#vk0|avs!1~QKd;8mqSh1GzcxxON5UI2)djPI;Dw# z)Fn|HWwz89GzC+Hn55CWz@Epj4umwkZd4zGqhPKP0g?bVMWd)GE}PIk+sNcKAs`rd zf0%OWxz?bRpxiHD;5nE{wO9ZRn4Ys)m}-oSF*8j|kRa(|8B?!aL_@mlwJBXSNbJpN z2UBgpdzeI4Hr)><c{>Rl>m}GoObh9dui}LPr1d-NH&7gHCM38jtj5{uG?|`Kk>wST zGJfnp@&8Kp4UAWilX!PPFxrZGk}N5RS!go(LV~OWnPbTkK-lc^E_fOxjh$@Absgqm zU8y&UI@x}s&enQ`qA3O}U^6*O2FV*z3H6M^DR?M;PivXz0toygC1BFek4F`^huVvg zjdt9@DAm!q*m&TP+M+{qu*^z?Xf(yn1QGP4VL~1ZH9pYEWqdc64qhZ946ti<0CaaJ zY@W9XY)2@7q*xy@P4bK1$%P?M<30aqVF)5ZK@?DW3grW%T$Lynvyn<nP(zPwt(G68 zNpfoix{>75EG@XrS<c}mTd+9;2H-p{V_9YnWJ*n`LwPoZ*q;;$mLJ@{<je_lBf(kd zvtC%ugK?DHvo&x<bdt`gi_xnCQ-uS%@8rBA+*Sgflo!$%^yEq|FA&at@ub+9mx$i# z=R*q_B+6zhX^a$p{L+Pqy~B-p?csIol%~LBIKejEGPB^ulV%s*Hf~(3bJQvxu<iCp zsO@Nuk7$Cep*eI2pqzBnqS;n2*nvPW_lZydpL)>Q$E%zqy-3zf04oKAG-x6?tsoGA zcij2`qO$~Q7CTm(-lL!)IV|~&j1HUysF+EdLxIA)3?8p`3rj3b`HC^&$M8muM5^Go za`OgkF+4c}f*u$aX?i`@LP2VvST4}Cm1{Cn&^Y@6=eFFatIe{nVwvE)SO|Qyx)9su z(ifs!qAhDSlR~pHV?HRildfok;7E<7xFLZq?n_kBhc}x{+F+rhE0fm{^Mmz_)Scx( z(s_q8vIkko6dBK{YNBre^TNn84Z&%#irIqkl7QM|Li*%U`*DPHnyf`IU%1#oFkAJ3 zleG$c(PPT4qF3PM2-U;1xLFW<oIw~eL#DcMgc*b(J*A$bOCp>@uSOm^0a>C^O^?R5 zQiv%-btlQjB9BZg%_s4UqD0B9<UBH^7&OQ;GEt4pO%1tPtmnlbxOW$mij1(Sq|~Sx z_)fA3?Z#;|uZ|Zy83XD9)q(}7aYQ16E1P8u=^#3^17czm)ky-DO$_o&Y>f!7zstH} zQ7qWJsp2zMw9c|Vf}Y2u@h?hFg4$1Dt2-CnL|sI9VNtH(i3Q#5l7Z(8lP@Mt_YdF& z{NRx^ryVpC_SUnF8zKbp7v&VU=c1wn`R72CnF3BPU{D4`R4gByqEYhV?ZQ*6&aM$% zN2gO7jj<uN@hy=O!Y!pdJuOe<R&|F=*+;fKS+#p7N7U`>%ucq_##3*$tI|j7+eWZf zx`?QZ)xPq*%ScU^;jPetyYjBZTfr@7+`hF_1$shcbS@guJEA>3-VEAz$y94Ck10ru zt>)IDJBo2fwX5`+C^2xL&w=v`WD4PuD6FEY{iw3$VDu1x*RS+~(r^ri2>qN$`=lyw z*Q%ZC$L^&v=z2<4*CSK0PEVAKs#Ggnq;=|2>2&v%(M8jZ3{2V{Kg57xV4;AUtA*R| zycxZGZ|%YI*d@5S=a`A_9=u;bBLT?CCA>icCAjf<)lhj<@<ts_4~L)<>vHrrI(BOl z&Wey$FtO5vFbaOawTQ;+Bw0pr=1F8VABXHn$Y_L(tNk@hhf{2@=|Y-b90}xiF%byO z6XcToD#Lm6QevOI`@KAv-Wb=&JHy0Ewn?Z^DI-nZGpcA@jMhQS$8kzp(<`d&PKSw6 zb1(piVb%Hnqz{Wm@9VDK>aV)hkeXP}vxnJt*iYFX>f5?r_u>9$%bEVY_20pD(sG1B z$skupV(2tZuQm#Eoe`m=Cg?GfcnSjYjblRlDwq~%CFFoGNIYt1NEp1=9zkPi1sx?| z3z<6=4l9seL9I^pA~BVc%k$FjB_F7>(MKgUUf3*W8*$Ohi~@VV_p|B(+B@0^%NiM0 z$wbzSxBYs-uJ;W1M4mYFJjoa#d|WVnp3pUOZIMu|RzIPvr|h^^U68o~SkOi~c@v)y z=)Fo_nL3*@tvpxJ`IeZm0s6G&lYE6H(ruu~6@D(1z8(i(q6l^f%7TEQ+*9DMsr+hD zc`m1Wjdr^&LypdfDLWXRdyH*nMD%4VMJkp<@KJuzU37K2e7<{A_6z6R&w~z&YH3R$ zNjp!MY~;cg!c;$A+BUNeL#KEOr~g9qr~8+MbAC#=`(lkc$^)1Ou51Y{rcZ#<aowJF z22ey3?@yqV^C;F1Dd)Dney#L_M9e|Mmlv}UOqZ!L<B=|)dHomrxjo`~4o>r}aCV|G zB<`;PLI;?+Ny^drjsvp6NcI>7J+K8-x=0gU(#n>*!R!Mv{_Gv857`;pUNb>t9qW4z zfRsA4PTPB(Cr?h%V(430I#zK2mRHY|)}bsZ87bd5j>hRS^C}-0ce2qCgJdG>;G}Vb zP9hg4GVVTd_DT`5O4FRZ7pw)S9=5j=Y`Z{)4A|naB1R_Wb`5X@$@ff(#|e?iAo0-# z#jxj8Qros34;2D9$4eN4M=-zsLfxHG(sGTAO(ti3utP_wM_08nL|Xil4z>A6jmf=+ zR9>)#t_xRWw6^#fVgV&6RDjmZd-$X-hC#>t$;Xh+sfOCokA^n}meP5B;76!1V|cm? zAeo>NqsPXrI)2*o+QqoS`^(HbP2{HT{PZRo#P$vg7AC=Z8;!1kLryeVIgfuhT*2cd zf&CCrt8*w}3I6%}$<H$3Z2$e%zm%&s0g4=kk=qw>LXw#WgtMu{E(Jsyf)wvPe=%)3 zYf5_o5}PGt#4%6jIcszHVD|QqE3e|9#IQIi$Bx<J2<9p5#dd^>Pz{PiZgPm_<DJw| zA_m<9s{r7twYr5@vaiqpH?7SP4_A0tHLz520tAL=J;fuz{xY~ayYxA8{Fh9oUTeT{ zW#f@lB&d`L77_6T7B;aCczAjpwQt(46Z(4Fk)G1Nj7toC#>P<U#aoOIr9jVKYK2(z zcF5D=R4evJ%$_78Ge=R?jzfK<s{7Nksmh|&0kpNK?T;ZhM(h#Fjn`tcshyUncc=^0 z4yuuxhzlLXw5=cCJXkmRfIQ*Svk&Avy}H)_3En*?-|>ZR&tIo0Fd}Q-a*HjFQ_}5? z6{K)VgHXo2^&BlK9it(6L-9a7v0v>nj&RXP4BE7AwLCf(;kLr^YCdJ7^-_VhW%oHM zl(VMKjR(mS35CMsogV}OC{dZwWH~~Ll%m*v9AN=CO_~D!fq=>;wuD%V?6Vj}@(_)Y zIA-vYc2e-MgPi}{ZwoulEElMnD^y)*wH&W6jHAW3!HyOfx9e%M+B0LTKD=6sQvX`& zJ6!zG<MaoVqOo)+`=X=gbe^UzCJTQ_t|X(Q9&dMeem{k3J?ojU1t~X77}%Wn*(xu~ z`GEXx>UE^SfPr_UKKPxjOW#NRkN6luoX3&CBwnCUZGn9?79Ji_xcLvUfD87?{9k_h zWQ*afRX5)|JgFxBpdO45rf?%3X@Md)740&Twnf?HQB_mN1i|n?FOg-e+h&X>+?bT8 zSsY&{s%(LkDWe{k)0rk}+r*s-Ew@fuv~Hg|H=ooUN>XyQuzK=T9P|{zvY|&tpxP8+ zBmiqIMc3+@xG<$HtyPQSzMe(16g0+=QMN5sA)($=a_E2ojMaKwKtyEsJdMoi$jrvp zlNl{)HPJ{T#ZS@Q<^Wsyy|y{4E-d|!VyqCNrF(hD)HroaHJ5}HOI35f#Ggs6j_9dt z6zf&FF+Xt6@n*G<b7q;gb5C`JFALU3ROEz4PM9V%Vq%{&veo6}AEh{OhtFPjGY>Pp zzw=730Khl@lph1|<Kg&||F8Z3xyo8af$)I=7{L7K#saW@<A2)V*I6LN<woV3`D3X~ zqemPqDT*1@*fzM5d#~_nBgA3FFdik|ou`gwJ=yZ4Grmzy8DT+@9DkO*CFMnEg9VJB z1v>mgeWee#bC@_A(sW9UW|he<CGolM<a~@(`{L<6rL`XC#!0IbC|~#JD4yNcBdE$N z-q98h88th@XtY}udr2P?0w<EXT4!E+>F*{p(XESTT8w>wMD2pr&yhn%e0l?MI-z`t zN|u1)!!*j0S)~du6(*I&q$o)7fqWG+uQP%}QD}%S4P_xb6eUla!YdKI*i5upW-ivL zQJ!}6<JTNm0)+aP<VwteLV%lO%V>rTZk?YQrN&}}iIl45S0ns7mqS_NkCM_n-19ci z$%1p{%gKM<RCLMcz=CN{S;=M1CH85lA6jg*38+oybDdSt<;6k1u`esz#pXZn>LCG> zB&R8kXYMnfGr{Ui#+}ZiKFDlAS<?uUs^(Nh3@d|4<%$S3?0J~?tc8+4CB|iO`C3eg z1LFhc*w<KZqiT{BI_3qB7YO4wlQhIibEMQ^5+7yty39z%3<mowrV#t^Rmr7=L|3HJ z`aryv$<Luc<ZY{yxyS#sfGiM!1s>+G2yxQbH1-hzJj`Jc;w0KM5vgc2pq+C96Q2?} zYKLVb0aq<4A)RFO3WEG{vy?Qtbf-HLaYdjcTm}S`XsZ_~m84q+2Z!3yK7u{ETuRp@ z+=@WSwhRaeX!X*nriiS(I_s~9uVGQfaQ-}ifKdubu*C%euniDkC*TGFRsz96wFcY{ zAjd-lc`8`aWE>ACA#yDOj^}b%w&I4__3jFDGR9OmmIRULf&`0(rVdN(W1$g+%TO={ zoSZC-A5~@SgPC#E)lrxVFmZrLu~PP@>X*a$-VqHG#f+9!88m2T?Q#@4beJPhy#z;# zu3I~65e%OsC+efI5S;q(xLB1BmQsG1<N&I|MG7i2(SvG$Fi|eQ_EW5pnKEaK!rV#w zb69Zo{abeYy@!~x$AaSoG1@MDlj|GvV|^r#zv(Nlb_+vdhDlC=U;Eq#uf|jG&;EHk zTb?4>pPv})u8NnRZRhk(A<y=gCi-X9ot@$o`U7uQ+sTuee?P4M<g1n>tg&~3-}}_Y zHOC~EzGZLSS8<<xeEY^rLJ*vT@eqsqD&Asz%(yUqVEmu;j-IixjUbn`r~Qw?9+DXR e3s_%KoR`Lh@fYL&!Aq1!V;6i3M&clY%8(_#<-zv= literal 0 HcmV?d00001 diff --git a/docs/fonts/roboto-v27-latin-regular.woff b/docs/fonts/roboto-v27-latin-regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..86b386372664a278c839b4a6fedbf6a05b396b70 GIT binary patch literal 20332 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 Z@IjO83?{s=KqX^`zGvsj4^tdfk^ttt3T6NR literal 0 HcmV?d00001 diff --git a/docs/fonts/roboto-v27-latin-regular.woff2 b/docs/fonts/roboto-v27-latin-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ebe1795f85a661c205e4a4612eaf47d56273e68e GIT binary patch literal 15688 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 literal 0 HcmV?d00001 diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..af60d26c --- /dev/null +++ b/docs/index.html @@ -0,0 +1,292 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta name="generator" content="Hugo 0.145.0"> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + ldsCtrlEst: LDS Control &amp; Estimation + # + +ldsCtrlEst is a C&#43;&#43; library for estimation and control of linear dynamical systems (LDS) with Gaussian or Poisson observations. It is meant to provide the functionality necessary to implement feedback control of linear dynamical systems experimentally. This library was originally developed for the task of controlling neuronal activity using spike count data as feedback and optogenetic inputs for control. However, the methods are generally applicable."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="LDS Control & Estimation"> + <meta property="og:description" content="ldsCtrlEst: LDS Control &amp; Estimation # ldsCtrlEst is a C&#43;&#43; library for estimation and control of linear dynamical systems (LDS) with Gaussian or Poisson observations. It is meant to provide the functionality necessary to implement feedback control of linear dynamical systems experimentally. This library was originally developed for the task of controlling neuronal activity using spike count data as feedback and optogenetic inputs for control. However, the methods are generally applicable."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>LDS Control &amp; Estimation | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>LDS Control &amp; Estimation</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest-lds-control--estimation"><code>ldsCtrlEst</code>: LDS Control &amp; Estimation</a></li> + <li><a href="#project-scope">Project Scope</a></li> + <li><a href="#repository-design">Repository Design</a></li> + <li><a href="#repository-organization">Repository Organization</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="ldsctrlest-lds-control--estimation"> + <code>ldsCtrlEst</code>: LDS Control &amp; Estimation + <a class="anchor" href="#ldsctrlest-lds-control--estimation">#</a> +</h1> +<p><code>ldsCtrlEst</code> is a C++ library for estimation and control of linear dynamical systems (LDS) with Gaussian or Poisson observations. It is meant to provide the functionality necessary to implement feedback control of linear dynamical systems experimentally. This library was originally developed for the task of controlling neuronal activity using spike count data as feedback and optogenetic inputs for control. However, the methods are generally applicable.</p> +<p>This library currently provides three namespaces.</p> +<ul> +<li><code>lds</code> : linear dynamical systems (without output/observations)</li> +<li><code>lds::gaussian</code>: linear dynamical systems with Gaussian observations</li> +<li><code>lds::poisson</code>: linear dynamical systems with Poisson observations</li> +</ul> +<p><em>Future iterations may include an additional namespace for LDS with Bernoulli observations (<code>lds::bernoulli</code>).</em></p> +<h1 id="project-scope"> + Project Scope + <a class="anchor" href="#project-scope">#</a> +</h1> +<p>The goal of this project is to provide necessary functions to implement feedback control of linear dynamical systems experimentally: <em>i.e.</em>, online estimation of state feedback and calculation of control signal updates. Given its intended use in experiments, the library seeks to be <strong>practical</strong> in all things and thus includes optional features such as adaptive estimation of a process disturbance to improve robustness in state estimation and a mechanism for combatting integrator windup with control signal saturation. For cases where the system to be controlled is not adequately modeled as having linear dynamics but has multiple quasi-linear operating modes, a switched control scheme is also implemented. It switches between multiple controllers designed for each operating mode of the physical system as it changes. It also includes options to toggle on/off feedback control and state estimation independently, which can be practically useful when testing the components of the control system. Moreover, to avoid the need for numerical integration of continuous-time models, all state-space models used here are discrete-time.</p> +<p>Generally, the <code>ldsCtrlEst</code> library does <strong>not</strong> endeavor to provide functionality for things that can be carried out offline/before experiments. For example, it does not design controller gains. Given a model of the system to be controlled, these parameters may be optimized before experimental application in most cases, and there are numerous options available to scientists/engineers in languages such as Matlab and Python for design. An exception to this guiding principle to project scope is the included code for fitting state-space models to data. Currently, this fitting portion of the library is a configurable option, but in future releases this may migrate to a separate project as it is not intended for online use.</p> +<p>Among other things, this project also does not provide methods for trajectory optimization, linearization of nonlinear models, or other methods related to nonlinear control, with the exception of the nonlinear state estimator for Poisson-output LDS models.</p> +<h1 id="repository-design"> + Repository Design + <a class="anchor" href="#repository-design">#</a> +</h1> +<p><img src="/lds-ctrl-est/classlds_1_1_system__inherit__graph.png" alt="system class hierarchy" /></p> +<ul> +<li>All dynamical systems <em>with observations</em> (<code>lds::gaussian::System</code>, <code>lds::poisson::System</code>) are derived from a prototypical linear dynamical system abstract type (<code>lds::System</code>).</li> +<li>These Gaussian and Poisson system types include user-accessible functions for one-step filtering for one-step simulation, etc.</li> +</ul> +<p><img src="/lds-ctrl-est/classlds_1_1_controller__inherit__graph.png" alt="controller class hierarchy" /></p> +<ul> +<li>The controller types for Gaussian- and Poisson-output systems (<code>lds::gaussian::Controller</code>, <code>lds::gaussian::SwitchedController</code>, <code>lds::poisson::Controller</code>, <code>lds::poisson::SwitchedController</code>) are derived from an abstract class template (<code>lds::Controller</code>) that is generic over LDS types derived from <code>lds::System</code> (here, <code>lds::gaussian::System</code> and <code>lds::poisson::System</code>). <code>lds::Controller</code> provides functions for one-step updates of the control signal, based on feedback and a target/reference signal. For the common problem of output reference tracking, the controller uses the underlying system model to estimate the control signal required to track the target, effectively providing model-based open-loop control if the estimator is disabled.</li> +<li>In order to ensure dimensionalities always match internally, every property of a system/controller class is <code>protected</code> or <code>private</code>. Get methods provide read-only references for most signals/parameters. Where appropriate, set methods are defined so users can change hidden parameters if and only if the new parameter has the correct dimensions.</li> +</ul> +<h1 id="repository-organization"> + Repository Organization + <a class="anchor" href="#repository-organization">#</a> +</h1> +<ul> +<li>Header files are located under <code>include/ldsEstCtrl_h</code>.</li> +<li>Source files are located under <code>src/</code> (main source code) and <code>src-fit/</code> (model fitting-related source code).</li> +<li>Wrappers for exposing functions to Matlab as executables (mex) are located under <code>matlab/</code>. Currently, only fitting functions of the library are exposed for use in Matlab.</li> +<li>Complimentary Matlab functions for control and estimation are also located under <code>matlab/</code>. They are provided as methods of <code>GLDS</code> and <code>PLDS</code> class definitions.</li> +<li>Example programs and visualization scripts are located under <code>examples/</code>.</li> +<li>Example programs that demonstrate how to use ldsCtrlEst in other projects are provided in <code>misc/</code>. See <code>misc/test-cmake-installation</code> for a project that uses <code>cmake</code> to configure your project build and <code>misc/test-pkgconfig-installation</code> which is the same but uses a hand-written Makefile and calls to pkg-config. As the names suggest, building these programs is a simple way to test your installation of ldsCtrlEst.</li> +</ul> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#ldsctrlest-lds-control--estimation"><code>ldsCtrlEst</code>: LDS Control &amp; Estimation</a></li> + <li><a href="#project-scope">Project Scope</a></li> + <li><a href="#repository-design">Repository Design</a></li> + <li><a href="#repository-organization">Repository Organization</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/index.xml b/docs/index.xml new file mode 100644 index 00000000..91925ca3 --- /dev/null +++ b/docs/index.xml @@ -0,0 +1,592 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>LDS Control &amp; Estimation on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/</link> + <description>Recent content in LDS Control &amp; Estimation on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/index.xml" rel="self" type="application/rss+xml" /> + <item> + <title></title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/acknowledgements/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/acknowledgements/</guid> + <description>&lt;h1 id=&#34;acknowledgements&#34;&gt;&#xA; Acknowledgements&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#acknowledgements&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Development and publication of this library was supported in part by the NIH/NINDS Collaborative Research in Computational Neuroscience (CRCNS)/BRAIN Grant 5R01NS115327-02.&lt;/p&gt;</description> + </item> + <item> + <title></title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</guid> + <description>&lt;h1 id=&#34;getting-started&#34;&gt;&#xA; Getting Started&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#getting-started&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This library uses the cross-platform tool CMake to orchestrate the building and testing process on Linux, MacOS, and Windows.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;ldsCtrlEst&lt;/code&gt; requires &lt;a href=&#34;http://arma.sourceforge.net/&#34;&gt;Armadillo&lt;/a&gt; for linear algebra as well as &lt;a href=&#34;https://www.hdfgroup.org/downloads/hdf5/&#34;&gt;HDF5&lt;/a&gt; for saving output. &lt;a href=&#34;https://vcpkg.io/&#34;&gt;&lt;code&gt;vcpkg&lt;/code&gt;&lt;/a&gt; is a cross-platform C++ package manager which allows us to easily install and use the dependencies in isolation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;tested-configurations&#34;&gt;&#xA; Tested Configurations&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#tested-configurations&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Building C++ libraries with complex dependencies can be tricky business—in our experience builds have inexplicably worked in one environment and failed in another. To save you time, sweat, and tears, we suggest you simply use one of the following setups we know work fairly reliably, using the &lt;code&gt;RelWithDebInfo&lt;/code&gt; build type in the CMake configure command (&lt;code&gt;-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo&lt;/code&gt;):&lt;/p&gt;</description> + </item> + <item> + <title></title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</guid> + <description>&lt;h1 id=&#34;windows-installation&#34;&gt;&#xA; Windows Installation&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#windows-installation&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;windows-pre-requisites&#34;&gt;&#xA; Windows Pre-requisites&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#windows-pre-requisites&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Scoop is a very handy tool for easily installing all sorts of command-line applications. Install like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Set-ExecutionPolicy RemoteSigned -Scope CurrentUser &lt;span style=&#34;color:#75715e&#34;&gt;# Optional: Needed to run a remote script the first time&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;iwr get.scoop.sh | Invoke-Expression&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Install Git and CMake if you don&amp;rsquo;t already have them:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;scoop install git cmake&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If that didn&amp;rsquo;t work, follow more detailed instructions &lt;a href=&#34;https://github.com/ScoopInstaller/Install#readme&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The easiest way to compile C++ project on Windows is with Visual Studio&amp;rsquo;s build tools, which you can download &lt;a href=&#34;https://visualstudio.microsoft.com/downloads/&#34;&gt;here&lt;/a&gt; (or &lt;a href=&#34;https://visualstudio.microsoft.com/vs/older-downloads/&#34;&gt;here&lt;/a&gt; for the 2019 release which we tested—make sure you get the most recent one, e.g., 16.11 at time of writing). In the installer, click on &amp;ldquo;Desktop development with C++.&amp;rdquo; If you want to build Python bindings, you will need to use the Clang compiler, which you can add on the &amp;ldquo;Installation details&amp;rdquo; sidebar under optional features.&lt;/p&gt;</description> + </item> + <item> + <title></title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/</guid> + <description>&lt;h1 id=&#34;reporting-issues&#34;&gt;&#xA; Reporting Issues&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#reporting-issues&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please &lt;a href=&#34;https://github.com/stanley-rozell/lds-ctrl-est/issues&#34;&gt;open an issue on GitHub&lt;/a&gt; and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.&lt;/p&gt;&#xA;&lt;h1 id=&#34;contributing&#34;&gt;&#xA; Contributing&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#contributing&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;We welcome any community contributions to this project. Please fork the repository and if possible use &lt;code&gt;clang-format&lt;/code&gt; and &lt;code&gt;clang-tidy&lt;/code&gt; to conform to the coding format/style of this repository.&lt;/p&gt;</description> + </item> + <item> + <title>armamexc</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</guid> + <description>arma/mex interface using Matlab C API</description> + </item> + <item> + <title>armamexcpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</guid> + <description>arma/mex interface using Matlab C++ API</description> + </item> + <item> + <title>C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</guid> + <description>&lt;h1 id=&#34;control--estimation&#34;&gt;&#xA; Control &amp;amp; Estimation&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#control--estimation&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;The control system provided by this library is comprised of a state estimator and a controller. The estimator is responsible for estimating the latent state of the system, given measurements up to and including the current time (i.e., &lt;em&gt;filtering&lt;/em&gt;). At each time step, the controller then uses the resulting state feedback and an internal model of the system to update the inputs to the process being manipulated.&lt;/p&gt;</description> + </item> + <item> + <title>Control Mode Bit Masks</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</guid> + <description>provides fill types for constructing new armadillo vectors, matrices</description> + </item> + <item> + <title>Defaults</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</guid> + <description>&lt;h1 id=&#34;defaults&#34;&gt;&#xA; Defaults&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#defaults&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;br&gt; &lt;br&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&#xA;&lt;br&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;attributes&#34;&gt;&#xA; Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const data_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultp0&#34;&gt;kDefaultP0&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;default state estimate covar&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const data_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultq0&#34;&gt;kDefaultQ0&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;default process noise covar&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const data_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/#variable-kdefaultr0&#34;&gt;kDefaultR0&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;default output noise covar&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Default values for common variables (e.g., default diagonal elements of covariances)&lt;/p&gt;&#xA;&lt;h2 id=&#34;attribute-details&#34;&gt;&#xA; Attribute Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#attribute-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;kdefaultp0&#34;&gt;&#xA; kDefaultP0&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#kdefaultp0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; data_t kDefaultP0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-6&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;kdefaultq0&#34;&gt;&#xA; kDefaultQ0&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#kdefaultq0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; data_t kDefaultQ0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-6&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;kdefaultr0&#34;&gt;&#xA; kDefaultR0&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#kdefaultr0&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; data_t kDefaultR0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>eg_glds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</guid> + <description>Example GLDS Control.</description> + </item> + <item> + <title>eg_glds_du_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</guid> + <description>Example GLDS Control of PLDS where change in control (du) is being updated, rather than amplitude (u).</description> + </item> + <item> + <title>eg_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</guid> + <description>Example PLDS Control.</description> + </item> + <item> + <title>eg_plds_est.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</guid> + <description>Example PLDS Estimation.</description> + </item> + <item> + <title>eg_plds_switched_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</guid> + <description>Example Switched PLDS Control.</description> + </item> + <item> + <title>examples</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</guid> + <description>&lt;h1 id=&#34;examples&#34;&gt;&#xA; examples&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#examples&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;files&#34;&gt;&#xA; Files&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#files&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#file-eg-glds-ctrl.cpp&#34;&gt;examples/eg_glds_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#file-eg-glds-du-plds-ctrl.cpp&#34;&gt;examples/eg_glds_du_plds_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#file-eg-plds-ctrl.cpp&#34;&gt;examples/eg_plds_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#file-eg-plds-est.cpp&#34;&gt;examples/eg_plds_est.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#file-eg-plds-switched-ctrl.cpp&#34;&gt;examples/eg_plds_switched_ctrl.cpp&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_glds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_glds_ctrlcpp&#34;&gt;&#xA; examples/eg_glds_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_glds_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_glds_du_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_glds_du_plds_ctrlcpp&#34;&gt;&#xA; examples/eg_glds_du_plds_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_glds_du_plds_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;function-details&#34;&gt;&#xA; Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;main&#34;&gt;&#xA; main&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#main&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; main()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Going to simulate a switching disturbance (m) acting on system&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_plds_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_plds_ctrlcpp&#34;&gt;&#xA; examples/eg_plds_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_plds_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_plds_est.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_plds_estcpp&#34;&gt;&#xA; examples/eg_plds_est.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_plds_estcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-random-walk&#34;&gt;random_walk&lt;/a&gt;&lt;/strong&gt;(size_t n_t, const Matrix &amp;amp; Q, const Vector &amp;amp; x0)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;</description> + </item> + <item> + <title>examples/eg_plds_switched_ctrl.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</guid> + <description>&lt;h1 id=&#34;exampleseg_plds_switched_ctrlcpp&#34;&gt;&#xA; examples/eg_plds_switched_ctrl.cpp&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#exampleseg_plds_switched_ctrlcpp&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;types&#34;&gt;&#xA; Types&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#types&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using double&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Mat&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-matrix&#34;&gt;Matrix&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;using arma::Col&amp;lt; data_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#using-vector&#34;&gt;Vector&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;functions&#34;&gt;&#xA; Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;int&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/#function-main&#34;&gt;main&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;type-details&#34;&gt;&#xA; Type Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#type-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;data_t&#34;&gt;&#xA; data_t&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#data_t&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;double&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Type of all data in library. If need 32b, change &lt;code&gt;double&lt;/code&gt; to &lt;code&gt;float&lt;/code&gt;. This could be potentially useful for large scale problems where there are memory constraints.&lt;/p&gt;&#xA;&lt;h3 id=&#34;matrix&#34;&gt;&#xA; Matrix&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#matrix&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Mat&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;vector&#34;&gt;&#xA; Vector&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#vector&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Col&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;function-details&#34;&gt;&#xA; Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;main&#34;&gt;&#xA; main&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#main&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; main()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;source-code&#34;&gt;&#xA; Source code&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#source-code&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//===-- eg_plds_switched_ctrl.cpp - Example Switched PLDS Control ---===//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Copyright 2021 Michael Bolus&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Copyright 2021 Georgia Institute of Technology&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Licensed under the Apache License, Version 2.0 (the &amp;#34;License&amp;#34;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// you may not use this file except in compliance with the License.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// You may obtain a copy of the License at&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// http://www.apache.org/licenses/LICENSE-2.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Unless required by applicable law or agreed to in writing, software&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// distributed under the License is distributed on an &amp;#34;AS IS&amp;#34; BASIS,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// See the License for the specific language governing permissions and&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// limitations under the License.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//===----------------------------------------------------------------------===//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//===----------------------------------------------------------------------===//&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;#include&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;&amp;lt;ldsCtrlEst&amp;gt;&lt;/span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;data_t;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Matrix;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Vector;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;using&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;cout;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; ********** Example Switched Poisson LDS Control ********** &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// whether to do switched control&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; do_switch_ctrl &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Make SISO system sampled at 1kHz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; data_t dt &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-3&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_u &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_y &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// no time steps for simulation.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; n_t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static_cast&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;size_t&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;30.0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// for simulating switching&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; size_t which_mode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t pr_21 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e-3&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// prob mode 1 -&amp;gt; 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; data_t pr_12 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pr_21; &lt;span style=&#34;color:#75715e&#34;&gt;// prob mode 2 -&amp;gt; 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// simulated system being controlled&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System controlled_system(n_u, n_x, n_y, dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// **Assume the system is not well characterized by one LDS, but is well&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// characterized by two LDS models with different input matrices.**&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; data_t scale_sys_b &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix a(n_x, n_x, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;eye);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; a[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.985&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix b1 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_x, n_u).fill(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.05&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Vector d &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Vector(n_y, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros).fill(log(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; dt));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_A(a);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_B(b1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_d(d);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.Reset(); &lt;span style=&#34;color:#75715e&#34;&gt;// reset to initial conditions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// reference&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Vector y_ref0 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Vector(n_y, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros).fill(&lt;span style=&#34;color:#ae81ff&#34;&gt;25.0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Let underlying system 1 be more sensitive than system 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix b2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_x, n_u).fill(b1[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; scale_sys_b);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// create switched controller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;SwitchedController switched_controller;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; k_x; &lt;span style=&#34;color:#75715e&#34;&gt;// feedback controller gains&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// create switched controller sub-systems&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// system 1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System sys1(controlled_system);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// set process noise covariance&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix q_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_x, n_x, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;eye) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5e-3&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.set_Q(q_controller);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// adaptively estimate process disturbance (m)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// n.b. using arbitrary default value for process noise if enabled.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; sys1.do_adapt_m &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; true;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// setting initial mode to target to avoid large error at onset:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Vector x0_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;log(y_ref0) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; d;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.set_x0(x0_controller);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.Reset(); &lt;span style=&#34;color:#75715e&#34;&gt;// reset to initial conditions&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sys1:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys1.Print();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// system 2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System sys2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; sys1;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// set parameters&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; sys2.set_B(b2);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sys2:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; sys2.Print();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformSystemList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; systems({sys1, sys2});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// controller gains for underlying system s:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix k_x1(n_u, n_x, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;ones);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix k_x2 &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scale_sys_b &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; k_x1; &lt;span style=&#34;color:#75715e&#34;&gt;// system2 is x-times less sensitive.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; k_x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt;({k_x1, k_x2});&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_lb &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_ub &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5.0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;SwitchedController(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(systems), u_lb, u_ub));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Control variables&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; size_t control_type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// no integral action, etc&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; switched_controller.set_control_type(control_type);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.set_Kc(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(k_x));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.set_y_ref(y_ref0);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; systems_vec(&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System());&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformSystemList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;lds&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;poisson&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;System&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; systems(std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;move(systems_vec));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;switched_controller:&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.Print();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;.....................................&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Fake measurements&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix z(n_y, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Will later contain control.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix u(n_u, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// create Matrix to save outputs in...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix y_hat(n_y, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix y_true(n_y, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix y_ref &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Matrix(n_y, n_t).fill(y_ref0[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;]);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// modes and gain/disturbance params&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Matrix x_hat(n_x, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix x_true(n_x, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;zeros);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; Matrix mode(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, n_t, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;ones);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// set initial val&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; y_hat.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_true.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_hat.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_true.col(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Starting &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; n_t &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; dt &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; sec simulation ... &lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; start &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;chrono&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;high_resolution_clock&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;now();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (size_t t &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;; t &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; n_t; t&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Let the controlled system stochastically change gain&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Assume another algorithm decodes this mode change and signals the&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// switched_controller&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; Vector chance(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;fill&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;randu);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (which_mode &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;) &lt;span style=&#34;color:#75715e&#34;&gt;// mode1&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (chance[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; pr_21) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; which_mode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_B(b2);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (do_switch_ctrl) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.Switch(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; { &lt;span style=&#34;color:#75715e&#34;&gt;// mode2&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (chance[&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; pr_12) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; which_mode &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; controlled_system.set_B(b1);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (do_switch_ctrl) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; switched_controller.Switch(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// Simulate the true system.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; z.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.Simulate(u.col(t &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// perform control&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; u.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.ControlOutputReference(z.col(t));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; mode.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; which_mode;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_ref.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; y_ref0;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_true.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_true.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; controlled_system.x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_hat.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().y();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_hat.col(t) &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; switched_controller.sys().x();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; finish &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;chrono&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;high_resolution_clock&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;now();&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;chrono&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;duration&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;data_t, std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;milli&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; sim_time_ms &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; finish &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; start;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Finished simulation in &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; sim_time_ms.count() &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; ms.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; cout &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;(app. &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; (sim_time_ms.count() &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; n_t) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1e3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; us/time-step)&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// saved variables: dt, y_hat, x_hat, m_hat, z, u, y_ref, y_true,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;// x_true, m_true saving with hdf5 via armadillo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_opts&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;opts replace &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_opts&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;replace;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt; dt_vec &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Vector(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;).fill(dt);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; dt_vec.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dt&amp;#34;&lt;/span&gt;));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_ref.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;y_ref&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; u.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;u&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; z.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;z&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_true.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x_true&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_true.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;y_true&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; x_hat.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x_hat&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; y_hat.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;y_hat&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; mode.save(arma&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;hdf5_name(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;eg_plds_switched_ctrl.h5&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mode&amp;#34;&lt;/span&gt;, replace));&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>GLDS Control</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</guid> + <description>&lt;h1 id=&#34;glds-control-tutorial&#34;&gt;&#xA; GLDS Control Tutorial&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#glds-control-tutorial&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This tutorial shows how to use this library to control a system with a Gaussian LDS controller (&lt;code&gt;lds::gaussian::Controller&lt;/code&gt;). In place of a physical system, a GLDS model (&lt;code&gt;lds::gaussian::System&lt;/code&gt;) receives control inputs and simulates measurements for the feedback control loop. The controller is assumed to have an imperfect model of the system being controlled (here, a gain mismatch), and there is a stochastic, unmeasured disturbance acting on the system. A combination of integral action and adaptive estimation of this process disturbance is used to perform control.&lt;/p&gt;</description> + </item> + <item> + <title>include</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</guid> + <description>&lt;h1 id=&#34;include&#34;&gt;&#xA; include&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#include&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;directories&#34;&gt;&#xA; Directories&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#directories&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/#dir-ldsctrlest-h&#34;&gt;ldsCtrlEst_h&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>lds</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</guid> + <description>Linear Dynamical Systems (LDS) namespace.</description> + </item> + <item> + <title>lds::Controller</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/</guid> + <description>&lt;h1 id=&#34;ldscontroller&#34;&gt;&#xA; lds::Controller&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldscontroller&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherited by &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/&#34;&gt;lds::SwitchedController&amp;lt; System &amp;gt;&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/&#34;&gt;lds::gaussian::Controller&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/&#34;&gt;lds::poisson::Controller&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller&#34;&gt;Controller&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/&#34;&gt;Controller&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller&#34;&gt;Controller&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp; sys, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_lb, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_ub, size_t control_type =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/&#34;&gt;Controller&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controller&#34;&gt;Controller&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp;&amp;amp; sys, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_lb, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_ub, size_t control_type =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/&#34;&gt;Controller&lt;/a&gt; by moving the system object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control&#34;&gt;Control&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; z, bool do_control =true, bool do_lock_control =false, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_soft_start =0, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_u_noise =0, bool do_reset_at_control_onset =true)&lt;br&gt;updates control signal (single-step)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-controloutputreference&#34;&gt;ControlOutputReference&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; z, bool do_control =true, bool do_estimation =true, bool do_lock_control =false, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_soft_start =0, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; sigma_u_noise =0, bool do_reset_at_control_onset =true)&lt;br&gt;updates control signal, given previously-set (single-step)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-sys&#34;&gt;sys&lt;/a&gt;&lt;/strong&gt;() const&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc&#34;&gt;Kc&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get state feedback controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-inty&#34;&gt;Kc_inty&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get integral controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-kc-u&#34;&gt;Kc_u&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get input feedback controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-g-design&#34;&gt;g_design&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get input gain used in controller design.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ref&#34;&gt;u_ref&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get reference input.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-x-ref&#34;&gt;x_ref&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get reference state.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-y-ref&#34;&gt;y_ref&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get reference output.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-control-type&#34;&gt;control_type&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-tau-awu&#34;&gt;tau_awu&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get time constant of anti-integral-windup.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-lb&#34;&gt;u_lb&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get control lower bound.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-u-ub&#34;&gt;u_ub&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;Get control upper bound.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-sys&#34;&gt;set_sys&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp; sys)&lt;br&gt;Set system.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-g-design&#34;&gt;set_g_design&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; g_design)&lt;br&gt;Set input gain used in controller design (g_design)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ref&#34;&gt;set_u_ref&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; u_ref)&lt;br&gt;Set reference input (u_ref)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-x-ref&#34;&gt;set_x_ref&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; x_ref)&lt;br&gt;Set reference state (x_ref)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-y-ref&#34;&gt;set_y_ref&lt;/a&gt;&lt;/strong&gt;(const Vector &amp;amp; y_ref)&lt;br&gt;Set reference output (y_ref)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc&#34;&gt;set_Kc&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; Kc)&lt;br&gt;Set state controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-inty&#34;&gt;set_Kc_inty&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; Kc_inty)&lt;br&gt;Set integral controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-kc-u&#34;&gt;set_Kc_u&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; Kc_u)&lt;br&gt;Set input controller gain.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-tau-awu&#34;&gt;set_tau_awu&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; tau)&lt;br&gt;Set time constant of anti-integral-windup.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-control-type&#34;&gt;set_control_type&lt;/a&gt;&lt;/strong&gt;(size_t control_type)&lt;br&gt;Sets the control type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-lb&#34;&gt;set_u_lb&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_lb)&lt;br&gt;sets control lower bound&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-set-u-ub&#34;&gt;set_u_ub&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; u_ub)&lt;br&gt;Sets control upper bound.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-reset&#34;&gt;Reset&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;reset system and control variables.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#function-print&#34;&gt;Print&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;prints variables to stdout&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-attributes&#34;&gt;&#xA; Protected Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-sys-&#34;&gt;sys_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;underlying LDS&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-&#34;&gt;u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;control signal&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-return-&#34;&gt;u_return_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;control signal that is &lt;em&gt;returned&lt;/em&gt; to user&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-g-design-&#34;&gt;g_design_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input gain of the system used for controller design&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-&#34;&gt;u_ref_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference input&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ref-prev-&#34;&gt;u_ref_prev_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference input at previous time step&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-x-ref-&#34;&gt;x_ref_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference state&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-y-ref-&#34;&gt;y_ref_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;reference output&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-cx-ref-&#34;&gt;cx_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-&#34;&gt;Kc_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state controller gain&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-u-&#34;&gt;Kc_u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input controller gain (optional when control updates \deltaU)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-kc-inty-&#34;&gt;Kc_inty_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;integral controller gain&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-du-ref-&#34;&gt;du_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-ref-&#34;&gt;dv_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-ref-&#34;&gt;v_ref_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-dv-&#34;&gt;dv_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-v-&#34;&gt;v_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;Control after g inversion (e.g., control in physical units)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-&#34;&gt;int_e_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;integrated error&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-int-e-awu-adjust-&#34;&gt;int_e_awu_adjust_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;anti-windup adjustment to intE&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-sat-&#34;&gt;u_sat_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;control signal after saturation (for antiWindup)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;bool&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-control-prev-&#34;&gt;do_control_prev_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;bool&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-do-lock-control-prev-&#34;&gt;do_lock_control_prev_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;bool&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-saturated-&#34;&gt;u_saturated_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;whether control signal has reached saturation limits&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-lb-&#34;&gt;u_lb_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;lower bound on control&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-u-ub-&#34;&gt;u_ub_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;upper bound on control&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-tau-awu-&#34;&gt;tau_awu_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;antiwindup time constant&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-k-awu-&#34;&gt;k_awu_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-t-since-control-onset-&#34;&gt;t_since_control_onset_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;time since control epoch onset&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/#variable-control-type-&#34;&gt;control_type_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;controller type&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;Controller;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;controller&#34;&gt;&#xA; &lt;strong&gt;Controller&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#controller&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Controller() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;controller-1&#34;&gt;&#xA; &lt;strong&gt;Controller&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#controller-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;inline&lt;/span&gt; Controller(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; sys,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_lb,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t u_ub,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t control_type &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::EM</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/</guid> + <description>&lt;h1 id=&#34;ldsem&#34;&gt;&#xA; lds::EM&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsem&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherited by &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/&#34;&gt;lds::gaussian::FitEM&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/&#34;&gt;lds::poisson::FitEM&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em&#34;&gt;EM&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/&#34;&gt;EM&lt;/a&gt;&lt;a href=&#34;&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em&#34;&gt;EM&lt;/a&gt;&lt;/strong&gt;(size_t n_x, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; dt, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; u_train, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; z_train)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/&#34;&gt;EM&lt;/a&gt;&lt;a href=&#34;&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-em&#34;&gt;EM&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; &amp;amp; fit0, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; u_train, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; z_train)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/&#34;&gt;EM&lt;/a&gt;&lt;a href=&#34;&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-~em&#34;&gt;~EM&lt;/a&gt;&lt;/strong&gt;() =default&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-run&#34;&gt;Run&lt;/a&gt;&lt;/strong&gt;(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =true, bool calc_output =true, bool calc_measurement =true, size_t max_iter =100, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; tol =1e-2)&lt;br&gt;Runs fitting by Expectation(E)-Maximization(M)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::tuple&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-returndata&#34;&gt;ReturnData&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Returns the input/output data to caller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::vector&amp;lt; Matrix &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-x&#34;&gt;x&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets estimated state (over time)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::vector&amp;lt; Matrix &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-y&#34;&gt;y&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets estimated output (over time)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-x-t-x-t&#34;&gt;sum_E_x_t_x_t&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets state-input covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-tm1-xu-tm1&#34;&gt;sum_E_xu_tm1_xu_tm1&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets state-input covariance (t-minus-1)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-sum-e-xu-t-xu-tm1&#34;&gt;sum_E_xu_t_xu_tm1&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets single lag state-input covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-n-t-tot&#34;&gt;n_t_tot&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;total number of time samples&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-theta&#34;&gt;theta&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets parameters updated in M step&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-functions&#34;&gt;&#xA; Protected Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-expectation&#34;&gt;Expectation&lt;/a&gt;&lt;/strong&gt;(bool force_common_initial =false)&lt;br&gt;Expectation step.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximization&#34;&gt;Maximization&lt;/a&gt;&lt;/strong&gt;(bool calc_dynamics =true, bool calc_Q =true, bool calc_init =false, bool calc_output =false, bool calc_measurement =false)&lt;br&gt;Maximization step.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizedynamics&#34;&gt;MaximizeDynamics&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeq&#34;&gt;MaximizeQ&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeinitial&#34;&gt;MaximizeInitial&lt;/a&gt;&lt;/strong&gt;()&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizeoutput&#34;&gt;MaximizeOutput&lt;/a&gt;&lt;/strong&gt;() =0&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-maximizemeasurement&#34;&gt;MaximizeMeasurement&lt;/a&gt;&lt;/strong&gt;() =0&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-smooth&#34;&gt;Smooth&lt;/a&gt;&lt;/strong&gt;(bool force_common_initial)&lt;br&gt;get smoothed estimates&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-recurseke&#34;&gt;RecurseKe&lt;/a&gt;&lt;/strong&gt;(Matrix &amp;amp; Ke, Cube &amp;amp; P_pre, Cube &amp;amp; P_post, size_t t) =0&lt;br&gt;recursively update estimator gain Ke&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-reset&#34;&gt;Reset&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;reset to initial conditions&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-initvars&#34;&gt;InitVars&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Initializes the variables.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#function-updatetheta&#34;&gt;UpdateTheta&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;updates parameter list, theta&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-attributes&#34;&gt;&#xA; Protected Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-u-&#34;&gt;u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-z-&#34;&gt;z_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;measurement training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Matrix &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-x-&#34;&gt;x_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state estimate&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Cube &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-&#34;&gt;P_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state estimate cov&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Cube &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-p-t-tm1-&#34;&gt;P_t_tm1_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;single-lag state covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; Matrix &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-y-&#34;&gt;y_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;output estimate&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-diag-y-&#34;&gt;diag_y_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-x-t-x-t-&#34;&gt;sum_E_x_t_x_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state covariance (current time)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-tm1-xu-tm1-&#34;&gt;sum_E_xu_tm1_xu_tm1_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;state-input covariance (t-minus-1)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-sum-e-xu-t-xu-tm1-&#34;&gt;sum_E_xu_t_xu_tm1_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;single lag state-input covariance&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-fit-&#34;&gt;fit_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-theta-&#34;&gt;theta_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-dt-&#34;&gt;dt_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;sample period&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-u-&#34;&gt;n_u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of inputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-x-&#34;&gt;n_x_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of states&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-y-&#34;&gt;n_y_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of outputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-trials-&#34;&gt;n_trials_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of input/output data sequences&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; size_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-&#34;&gt;n_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of time steps&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/#variable-n-t-tot-&#34;&gt;n_t_tot_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;total number of time steps across trials&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; Fit &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;EM;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;em&#34;&gt;&#xA; &lt;strong&gt;EM&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#em&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EM() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;em-1&#34;&gt;&#xA; &lt;strong&gt;EM&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#em-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;EM(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_x,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t dt,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; u_train,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; z_train&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::Fit</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/</guid> + <description>LDS Fit Type.</description> + </item> + <item> + <title>lds::gaussian</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</guid> + <description>Linear Dynamical Systems with Gaussian observations.</description> + </item> + <item> + <title>lds::gaussian::Controller</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/</guid> + <description>Gaussian-observation Controller Type.</description> + </item> + <item> + <title>lds::gaussian::Fit</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/</guid> + <description>GLDS Fit Type.</description> + </item> + <item> + <title>lds::gaussian::FitEM</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/</guid> + <description>GLDS E-M Fit Type.</description> + </item> + <item> + <title>lds::gaussian::FitSSID</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/</guid> + <description>Subspace Identification (SSID) for GLDS.</description> + </item> + <item> + <title>lds::gaussian::SwitchedController</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/</guid> + <description>Gaussian-observation SwitchedController Type.</description> + </item> + <item> + <title>lds::gaussian::System</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/</guid> + <description>Gaussian LDS Type.</description> + </item> + <item> + <title>lds::poisson</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</guid> + <description>Linear Dynamical Systems with Poisson observations.</description> + </item> + <item> + <title>lds::poisson::Controller</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/</guid> + <description>PLDS Controller Type.</description> + </item> + <item> + <title>lds::poisson::Fit</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/</guid> + <description>PLDS Fit Type.</description> + </item> + <item> + <title>lds::poisson::FitEM</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/</guid> + <description>PLDS E-M Fit Type.</description> + </item> + <item> + <title>lds::poisson::FitSSID</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/</guid> + <description>Subspace Identification (SSID) for PLDS.</description> + </item> + <item> + <title>lds::poisson::SwitchedController</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/</guid> + <description>Poisson-observation SwitchedController Type.</description> + </item> + <item> + <title>lds::poisson::System</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/</guid> + <description>Poisson System type.</description> + </item> + <item> + <title>lds::SSID</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/</guid> + <description>&lt;h1 id=&#34;ldsssid&#34;&gt;&#xA; lds::SSID&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsssid&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherited by &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/&#34;&gt;lds::gaussian::FitSSID&lt;/a&gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/&#34;&gt;lds::poisson::FitSSID&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid&#34;&gt;SSID&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/&#34;&gt;SSID&lt;/a&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-ssid&#34;&gt;SSID&lt;/a&gt;&lt;/strong&gt;(size_t n_x, size_t n_h, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; dt, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; u_train, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; z_train, const Vector &amp;amp; d =Vector(1).fill(-kInf))&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/&#34;&gt;SSID&lt;/a&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt; type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::tuple&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt;, Vector &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-run&#34;&gt;Run&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt&#34;&gt;SSIDWt&lt;/a&gt; ssid_wt)&lt;br&gt;Runs fitting by subspace identification (&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/&#34;&gt;SSID&lt;/a&gt;)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::tuple&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-returndata&#34;&gt;ReturnData&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Returns the I/O data to caller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-functions&#34;&gt;&#xA; Protected Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcd&#34;&gt;CalcD&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; t_silence =0.1, &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; thresh_silence =0.001)&lt;br&gt;Using periods of silence in inputs (u), calculates the output \ bias (d)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-createhankeldatamat&#34;&gt;CreateHankelDataMat&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;Creates the block-hankel I/O data matrix.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;virtual void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-decomposedata&#34;&gt;DecomposeData&lt;/a&gt;&lt;/strong&gt;() =0&lt;br&gt;Decompose data to lower-triangular matrix (used in Solve)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-calcsvd&#34;&gt;CalcSVD&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enum-ssidwt&#34;&gt;SSIDWt&lt;/a&gt; wt)&lt;br&gt;performs the singular value decomposition (SVD)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-solve&#34;&gt;Solve&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt; wt_dc)&lt;br&gt;solves for LDS parameters&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#function-recomputeextobs&#34;&gt;RecomputeExtObs&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;recompute extended observability matrix from estimates of A, C&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;protected-attributes&#34;&gt;&#xA; Protected Attributes&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#protected-attributes&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-u-&#34;&gt;u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;input training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#enumvalue-kmatfreedim2&#34;&gt;kMatFreeDim2&lt;/a&gt; &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-z-&#34;&gt;z_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;measurement training data&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-d-&#34;&gt;D_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;block-Hankel I/O data matrix&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/&#34;&gt;Fit&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-fit-&#34;&gt;fit_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;fit&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-g-dc-&#34;&gt;g_dc_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;I/O gain @ DC.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/#using-data-t&#34;&gt;data_t&lt;/a&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-dt-&#34;&gt;dt_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;sample period&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-u-&#34;&gt;n_u_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of inputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-x-&#34;&gt;n_x_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of states&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-y-&#34;&gt;n_y_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of outputs&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-h-&#34;&gt;n_h_&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-trials-&#34;&gt;n_trials_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of input/output data sequences&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;std::vector&amp;lt; size_t &amp;gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-&#34;&gt;n_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;number of time steps&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-n-t-tot-&#34;&gt;n_t_tot_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;total number of time steps across trials&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-l-&#34;&gt;L_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;lower triangle decomp of covariance matrix&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Vector&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-s-&#34;&gt;s_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;singular values&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;Matrix&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/#variable-ext-obs-t-&#34;&gt;ext_obs_t_&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;extended observability matrix&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; Fit &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;SSID;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;ssid&#34;&gt;&#xA; &lt;strong&gt;SSID&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ssid&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SSID() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;ssid-1&#34;&gt;&#xA; &lt;strong&gt;SSID&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ssid-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;SSID(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_x,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t n_h,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; data_t dt,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; u_train,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; UniformMatrixList&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; kMatFreeDim2 &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; z_train,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; Vector &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; d &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;Vector(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;).fill(&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;kInf)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::SwitchedController</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/</guid> + <description>SwitchedController Type.</description> + </item> + <item> + <title>lds::System</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/</guid> + <description>Linear Dynamical System Type.</description> + </item> + <item> + <title>lds::UniformMatrixList</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/</guid> + <description>&lt;h1 id=&#34;ldsuniformmatrixlist&#34;&gt;&#xA; lds::UniformMatrixList&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsuniformmatrixlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherits from std::vector&amp;lt; Matrix &amp;gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(const std::vector&amp;lt; Matrix &amp;gt; &amp;amp; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; by copying existing vector of Matrix if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(std::vector&amp;lt; Matrix &amp;gt; &amp;amp;&amp;amp; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; by moving existing vector of Matrix if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(std::initializer_list&amp;lt; Matrix &amp;gt; mats, std::array&amp;lt; size_t, 2 &amp;gt; dim ={0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; from initializer_list of Matrix if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; (copy).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-uniformmatrixlist&#34;&gt;UniformMatrixList&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt; (move).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-~uniformmatrixlist&#34;&gt;~UniformMatrixList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Destroys the object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::array&amp;lt; size_t, 2 &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-dim&#34;&gt;dim&lt;/a&gt;&lt;/strong&gt;(size_t n =0) const&lt;br&gt;gets dimensions of uniformly sized matrices&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-size&#34;&gt;size&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;size of container&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Matrix &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-at&#34;&gt;at&lt;/a&gt;&lt;/strong&gt;(size_t n)&lt;br&gt;gets reference to n^th element&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-swap&#34;&gt;Swap&lt;/a&gt;&lt;/strong&gt;(Matrix &amp;amp; that, size_t n)&lt;br&gt;swaps input matrix with n^th matrix of list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp; that)&lt;br&gt;assigns the contents (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/&#34;&gt;UniformMatrixList&lt;/a&gt;&amp;lt; D &amp;gt; &amp;amp;&amp;amp; that)&lt;br&gt;assigns the contents (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/#function-append&#34;&gt;append&lt;/a&gt;&lt;/strong&gt;(const Matrix &amp;amp; mat)&lt;br&gt;appends a matrix to the list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;MatrixListFreeDim D &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;kMatFreeDimNone&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformMatrixList;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;uniformmatrixlist&#34;&gt;&#xA; &lt;strong&gt;UniformMatrixList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformmatrixlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UniformMatrixList() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;uniformmatrixlist-1&#34;&gt;&#xA; &lt;strong&gt;UniformMatrixList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformmatrixlist-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;explicit&lt;/span&gt; UniformMatrixList(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; Matrix &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; mats,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;array&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; size_t, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; dim &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::UniformSystemList</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/</guid> + <description>&lt;h1 id=&#34;ldsuniformsystemlist&#34;&gt;&#xA; lds::UniformSystemList&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsuniformsystemlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;#detailed-description&#34;&gt;More&amp;hellip;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Inherits from std::vector&amp;lt; System &amp;gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(const std::vector&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;gt; &amp;amp; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; by copying existing vector of &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(std::vector&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;gt; &amp;amp;&amp;amp; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; by moving existing vector of &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(std::initializer_list&amp;lt; &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;gt; systems, std::array&amp;lt; size_t, 3 &amp;gt; dim ={0, 0, 0})&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; from initializer_list of &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; (copy).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-uniformsystemlist&#34;&gt;UniformSystemList&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; (move).&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-~uniformsystemlist&#34;&gt;~UniformSystemList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Destroys the object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const std::array&amp;lt; size_t, 3 &amp;gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-dim&#34;&gt;dim&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets dimensions of the uniformly sized systems&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-size&#34;&gt;size&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;size of container&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-at&#34;&gt;at&lt;/a&gt;&lt;/strong&gt;(size_t n)&lt;br&gt;gets reference to n^th element&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-swap&#34;&gt;Swap&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/&#34;&gt;System&lt;/a&gt; &amp;amp; that, size_t n)&lt;br&gt;swaps input system with n^th system of list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp; that)&lt;br&gt;assigns the contents (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/&#34;&gt;UniformSystemList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;assigns the contents (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;detailed-description&#34;&gt;&#xA; Detailed Description&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#detailed-description&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;typename&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;lds&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;UniformSystemList;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;uniformsystemlist&#34;&gt;&#xA; &lt;strong&gt;UniformSystemList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformsystemlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UniformSystemList() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;uniformsystemlist-1&#34;&gt;&#xA; &lt;strong&gt;UniformSystemList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformsystemlist-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;explicit&lt;/span&gt; UniformSystemList(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; System &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; systems,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;array&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; size_t, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; dim &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>lds::UniformVectorList</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/</guid> + <description>&lt;h1 id=&#34;ldsuniformvectorlist&#34;&gt;&#xA; lds::UniformVectorList&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsuniformvectorlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;Inherits from std::vector&amp;lt; Vector &amp;gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;public-functions&#34;&gt;&#xA; Public Functions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-functions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;&lt;/th&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt;.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(const std::vector&amp;lt; Vector &amp;gt; &amp;amp; vecs, size_t dim =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; by copying existing vector of Vector if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(std::vector&amp;lt; Vector &amp;gt; &amp;amp;&amp;amp; vecs, size_t dim =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; by moving existing vector of Vector if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(std::initializer_list&amp;lt; Vector &amp;gt; vecs, size_t dim =0)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; from initializer_list of Vector if dimensions consistent.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-uniformvectorlist&#34;&gt;UniformVectorList&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;Constructs a new &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-~uniformvectorlist&#34;&gt;~UniformVectorList&lt;/a&gt;&lt;/strong&gt;() =default&lt;br&gt;Destroys the object.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-dim&#34;&gt;dim&lt;/a&gt;&lt;/strong&gt;() const&lt;br&gt;gets dimensions of the uniformly sized matrices&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;size_t&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-size&#34;&gt;size&lt;/a&gt;&lt;/strong&gt;()&lt;br&gt;size of container&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;const Vector &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-at&#34;&gt;at&lt;/a&gt;&lt;/strong&gt;(size_t n)&lt;br&gt;gets reference to n^th element&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;void&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-swap&#34;&gt;Swap&lt;/a&gt;&lt;/strong&gt;(Vector &amp;amp; that, size_t n)&lt;br&gt;swaps input matrix with n^th vector of list&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(const &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp; that)&lt;br&gt;assigns the contents (copy)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&lt;/td&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/#function-operator=&#34;&gt;operator=&lt;/a&gt;&lt;/strong&gt;(&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/&#34;&gt;UniformVectorList&lt;/a&gt; &amp;amp;&amp;amp; that)&lt;br&gt;assigns the contents (move)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;hr&gt;&#xA;&lt;h2 id=&#34;public-function-details&#34;&gt;&#xA; Public Function Details&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#public-function-details&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;uniformvectorlist&#34;&gt;&#xA; &lt;strong&gt;UniformVectorList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformvectorlist&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;UniformVectorList() &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;&#xA;&lt;h3 id=&#34;uniformvectorlist-1&#34;&gt;&#xA; &lt;strong&gt;UniformVectorList&lt;/strong&gt;&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#uniformvectorlist-1&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-cpp&#34; data-lang=&#34;cpp&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;explicit&lt;/span&gt; UniformVectorList(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; std&lt;span style=&#34;color:#f92672&#34;&gt;::&lt;/span&gt;vector&lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&lt;/span&gt; Vector &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; vecs,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; size_t dim &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;:&lt;/p&gt;</description> + </item> + <item> + <title>ldsCtrlEst_h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</guid> + <description>&lt;h1 id=&#34;ldsctrlest_h&#34;&gt;&#xA; ldsCtrlEst_h&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#ldsctrlest_h&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;files&#34;&gt;&#xA; Files&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#files&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/#file-lds.h&#34;&gt;ldsCtrlEst_h/lds.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;&lt;code&gt;lds&lt;/code&gt; namespace&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/#file-lds-ctrl.h&#34;&gt;ldsCtrlEst_h/lds_ctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;Controller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/#file-lds-fit.h&#34;&gt;ldsCtrlEst_h/lds_fit.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;LDS base fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/#file-lds-fit-em.h&#34;&gt;ldsCtrlEst_h/lds_fit_em.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;subspace identification&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/#file-lds-fit-ssid.h&#34;&gt;ldsCtrlEst_h/lds_fit_ssid.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;subspace identification&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/#file-lds-gaussian.h&#34;&gt;ldsCtrlEst_h/lds_gaussian.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;&lt;code&gt;glds&lt;/code&gt; namespace&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/#file-lds-gaussian-ctrl.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_ctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS Controller.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/#file-lds-gaussian-fit.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_fit.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/#file-lds-gaussian-fit-em.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_fit_em.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS E-M fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/#file-lds-gaussian-fit-ssid.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_fit_ssid.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS SSID fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/#file-lds-gaussian-sctrl.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_sctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS switched controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/#file-lds-gaussian-sys.h&#34;&gt;ldsCtrlEst_h/lds_gaussian_sys.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/#file-lds-poisson.h&#34;&gt;ldsCtrlEst_h/lds_poisson.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;&lt;code&gt;plds&lt;/code&gt; namespace&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/#file-lds-poisson-ctrl.h&#34;&gt;ldsCtrlEst_h/lds_poisson_ctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/#file-lds-poisson-fit.h&#34;&gt;ldsCtrlEst_h/lds_poisson_fit.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS base fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/#file-lds-poisson-fit-em.h&#34;&gt;ldsCtrlEst_h/lds_poisson_fit_em.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS E-M fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/#file-lds-poisson-fit-ssid.h&#34;&gt;ldsCtrlEst_h/lds_poisson_fit_ssid.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS SSID fit type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/#file-lds-poisson-sctrl.h&#34;&gt;ldsCtrlEst_h/lds_poisson_sctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS switched controller type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/#file-lds-poisson-sys.h&#34;&gt;ldsCtrlEst_h/lds_poisson_sys.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/#file-lds-sctrl.h&#34;&gt;ldsCtrlEst_h/lds_sctrl.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;SwitchedController type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/#file-lds-sys.h&#34;&gt;ldsCtrlEst_h/lds_sys.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;LDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/#file-lds-uniform-mats.h&#34;&gt;ldsCtrlEst_h/lds_uniform_mats.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;List of uniformly sized matrices.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/#file-lds-uniform-systems.h&#34;&gt;ldsCtrlEst_h/lds_uniform_systems.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;List of uniformly sized Systems.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/#file-lds-uniform-vecs.h&#34;&gt;ldsCtrlEst_h/lds_uniform_vecs.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;List of uniformly sized vectors.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/#file-mex-c-util.h&#34;&gt;ldsCtrlEst_h/mex_c_util.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C API)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/#file-mex-cpp-util.h&#34;&gt;ldsCtrlEst_h/mex_cpp_util.h&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C++ API)&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_ctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</guid> + <description>Controller.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_fit_em.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</guid> + <description>subspace identification</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_fit_ssid.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</guid> + <description>subspace identification</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_fit.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</guid> + <description>LDS base fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_ctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</guid> + <description>GLDS Controller.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_fit_em.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</guid> + <description>GLDS E-M fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_fit_ssid.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</guid> + <description>GLDS SSID fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_fit.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</guid> + <description>GLDS fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_sctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</guid> + <description>GLDS switched controller type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian_sys.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</guid> + <description>GLDS base type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_gaussian.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</guid> + <description>glds namespace</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_ctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</guid> + <description>PLDS controller type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_fit_em.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</guid> + <description>PLDS E-M fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_fit_ssid.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</guid> + <description>PLDS SSID fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_fit.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</guid> + <description>PLDS base fit type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_sctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</guid> + <description>PLDS switched controller type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson_sys.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</guid> + <description>PLDS base type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_poisson.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</guid> + <description>plds namespace</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_sctrl.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</guid> + <description>SwitchedController type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_sys.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</guid> + <description>LDS base type.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_uniform_mats.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</guid> + <description>List of uniformly sized matrices.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_uniform_systems.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</guid> + <description>List of uniformly sized Systems.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds_uniform_vecs.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</guid> + <description>List of uniformly sized vectors.</description> + </item> + <item> + <title>ldsCtrlEst_h/lds.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</guid> + <description>lds namespace</description> + </item> + <item> + <title>ldsCtrlEst_h/mex_c_util.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</guid> + <description>arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C API)</description> + </item> + <item> + <title>ldsCtrlEst_h/mex_cpp_util.h</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</guid> + <description>arma &amp;lt;-&amp;gt; mex interoperability utilities (Matlab C++ API)</description> + </item> + <item> + <title>Models</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</guid> + <description>&lt;h1 id=&#34;model-definitions&#34;&gt;&#xA; Model Definitions&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#model-definitions&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This library provides methods for control and estimation of linear dynamical systems (LDS) of the following form:&#xA;&#xA;&lt;link rel=&#34;stylesheet&#34; href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/katex/katex.min.css&#34; /&gt;&#xA;&lt;script defer src=&#34;https://stanley-rozell.github.io/lds-ctrl-est/katex/katex.min.js&#34;&gt;&lt;/script&gt;&#xA;&lt;script defer src=&#34;https://stanley-rozell.github.io/lds-ctrl-est/katex/auto-render.min.js&#34; onload=&#34;renderMathInElement(document.body);&#34;&gt;&lt;/script&gt;&lt;span&gt;&#xA; \[\mathbf{x}_{t&amp;#43;1} = f\left( \mathbf{x}_{t}, \mathbf{v}_{t} \right) = \mathbf{A} \mathbf{x}_{t} &amp;#43; \mathbf{B} \mathbf{v}_{t} &amp;#43; \mathbf{m}_{t} &amp;#43; \mathbf{w}_{t}\]&#xA;&lt;/span&gt;&#xA;&lt;/p&gt;&#xA;&lt;span&gt;&#xA; \[\mathbf{y}_{t} = h\left( \mathbf{x}_{t} \right)\]&#xA;&lt;/span&gt;&#xA;&#xA;&lt;pre&gt;&lt;code&gt;t : time index&#xA;x : system state&#xA;v = g%u : input (e.g., in physical units used for model fit)&#xA;u : control signal sent to actuator (e.g., in Volts)&#xA;y : system output&#xA;m : process disturbance&#xA;w ~ N(0, Q) : process noise/disturbance&#xA;&#xA;A : state matrix&#xA;B : input coupling matrix&#xA;g : input gain (e.g., for converting to control signal actuator voltage)&#xA; n.b., assumes this conversion is linear&#xA;Q : process noise covariance&#xA;&#xA;% : element-wise multiplication&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;lds-with-gaussian-observations&#34;&gt;&#xA; LDS with Gaussian Observations&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#lds-with-gaussian-observations&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;For linear dynamical systems whose outputs are assumed to be corrupted by additive Gaussian noise before measurement (Gaussian LDS models), the output function takes the following form.&lt;/p&gt;</description> + </item> + <item> + <title>PLDS State Estimation</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</guid> + <description>&lt;h1 id=&#34;plds-state-estimation-tutorial&#34;&gt;&#xA; PLDS State Estimation Tutorial&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#plds-state-estimation-tutorial&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This tutorial shows how to use this library to estimate the state of an LDS with Poisson observations from input/output data. In place of a physical system, another PLDS model (&lt;code&gt;lds::poisson::System&lt;/code&gt;) receives random inputs and provides measurements for the state estimator. For the sake of example, the only parameter mismatch is assumed to be the process disturbance, which is adaptively re-estimated.&lt;/p&gt;&#xA;&lt;p&gt;The full code for this can be found &lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</description> + </item> + <item> + <title>PLDS Switched Control</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</guid> + <description>&lt;h1 id=&#34;plds-switched-control-tutorial&#34;&gt;&#xA; PLDS Switched Control Tutorial&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#plds-switched-control-tutorial&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;This tutorial shows how to use this library to control a system with a switched PLDS controller (&lt;code&gt;lds::poisson::SwitchedController&lt;/code&gt;). This type of controller is applicable in scenarios where a physical system is not accurately captured by a &lt;strong&gt;single&lt;/strong&gt; LDS but has &lt;strong&gt;multiple&lt;/strong&gt; discrete operating modes where the dynamics can be well-approximated as linear.&lt;/p&gt;&#xA;&lt;p&gt;In the example that follows, another PLDS model (&lt;code&gt;lds::poisson::System&lt;/code&gt;) is used in place of a physical system. It receives control inputs and provides measurements for the simulated feedback control loop. This system stochastically flips between two input gains. Here, the controller is assumed to have a perfect model of the switching system being controlled. Note that in practice, users would need to have a decoder that estimates operating mode of the physical system being controlled. This library does not currently include operating mode estimation.&lt;/p&gt;</description> + </item> + <item> + <title>src</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</guid> + <description>&lt;h1 id=&#34;src&#34;&gt;&#xA; src&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#src&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;files&#34;&gt;&#xA; Files&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#files&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA; &lt;thead&gt;&#xA; &lt;tr&gt;&#xA; &lt;th&gt;Name&lt;/th&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/thead&gt;&#xA; &lt;tbody&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/#file-lds.cpp&#34;&gt;src/lds.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;misc lds namespace functions&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/#file-lds-gaussian-sys.cpp&#34;&gt;src/lds_gaussian_sys.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;GLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/#file-lds-poisson-sys.cpp&#34;&gt;src/lds_poisson_sys.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;PLDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/#file-lds-sys.cpp&#34;&gt;src/lds_sys.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;LDS base type.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;tr&gt;&#xA; &lt;td&gt;&lt;strong&gt;&lt;a href=&#34;https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/#file-lds-uniform-vecs.cpp&#34;&gt;src/lds_uniform_vecs.cpp&lt;/a&gt;&lt;/strong&gt; &lt;br&gt;Uniformly sized vectors.&lt;/td&gt;&#xA; &lt;/tr&gt;&#xA; &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + <item> + <title>src/lds_gaussian_sys.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</guid> + <description>GLDS base type.</description> + </item> + <item> + <title>src/lds_poisson_sys.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</guid> + <description>PLDS base type.</description> + </item> + <item> + <title>src/lds_sys.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</guid> + <description>LDS base type.</description> + </item> + <item> + <title>src/lds_uniform_vecs.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</guid> + <description>Uniformly sized vectors.</description> + </item> + <item> + <title>src/lds.cpp</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</guid> + <description>misc lds namespace functions</description> + </item> + <item> + <title>std</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacestd/</link> + <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate> + <guid>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacestd/</guid> + <description>&lt;h1 id=&#34;std&#34;&gt;&#xA; std&#xA; &lt;a class=&#34;anchor&#34; href=&#34;#std&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;br&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;Updated on 31 March 2025 at 16:04:30 EDT&lt;/p&gt;</description> + </item> + </channel> +</rss> diff --git a/docs/issues-contributing/index.html b/docs/issues-contributing/index.html new file mode 100644 index 00000000..9b21a5a8 --- /dev/null +++ b/docs/issues-contributing/index.html @@ -0,0 +1,263 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=" + Reporting Issues + # + +If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes. + + Contributing + # + +We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository."> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="LDS C&E"> + <meta property="og:description" content="Reporting Issues # If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please open an issue on GitHub and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes. +Contributing # We welcome any community contributions to this project. Please fork the repository and if possible use clang-format and clang-tidy to conform to the coding format/style of this repository."> + <meta property="og:locale" content="en"> + <meta property="og:type" content="article"> +<title>Issues Contributing | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"class=active><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Issues Contributing</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#reporting-issues">Reporting Issues</a></li> + <li><a href="#contributing">Contributing</a></li> + </ul> +</nav> + + + + </aside> + + + </header> + + + + <article class="markdown"><h1 id="reporting-issues"> + Reporting Issues + <a class="anchor" href="#reporting-issues">#</a> +</h1> +<p>If you encounter bugs when using this library or have specific feature requests that you believe fall within the stated scope of this project, please <a href="https://github.com/stanley-rozell/lds-ctrl-est/issues">open an issue on GitHub</a> and use an appropriate issue template where possible. You may also fork the repository and submit pull-requests with your suggested changes.</p> +<h1 id="contributing"> + Contributing + <a class="anchor" href="#contributing">#</a> +</h1> +<p>We welcome any community contributions to this project. Please fork the repository and if possible use <code>clang-format</code> and <code>clang-tidy</code> to conform to the coding format/style of this repository.</p> +<p>When editing any documentation/guides, please use the markdown docs in <code>misc/docs-hugo</code> instead of directly editing the HTML docs. +This may require having <code>hugo</code>, <code>graphviz</code> and <code>doxygen</code> installed through <code>brew</code>, as well as <code>doxybook2</code> installed through a git clone.</p> +<p>Clone the <code>doxybook2</code> repository online and place the executable in your <code>usr/local/bin</code> folder or another <code>bin</code> folder. +Run <code>sudo chmod +x /usr/local/bin/doxybook2</code> to give doxybook2 permissions. Run <code>doxybook2 --help</code> to ensure that this works properly. If permission is still denied, navigate to System Preferences &gt; Security &amp; Privacy &gt; General. +You should see a message at the bottom that says: &ldquo;doxybook2 was blocked from use because it is not from an identified developer.&rdquo; Click Allow Anyway.</p> +<p>Updated docs can be built by running <code>cd scripts</code> and <code>./update-docs.sh</code>. After a successful build, navigate to <code>/misc/docs-hugo/</code> and run <code>hugo server</code> to create a local host of the website. Access the website using local host to ensure the correct results.</p> +</article> + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + +<nav id="TableOfContents"> + <ul> + <li><a href="#reporting-issues">Reporting Issues</a></li> + <li><a href="#contributing">Contributing</a></li> + </ul> +</nav> + + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/katex/auto-render.min.js b/docs/katex/auto-render.min.js new file mode 100644 index 00000000..3a6d6639 --- /dev/null +++ b/docs/katex/auto-render.min.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=1)}([function(t,r){t.exports=e},function(e,t,r){"use strict";r.r(t);var n=r(0),o=r.n(n),a=function(e,t,r){for(var n=r,o=0,a=e.length;n<t.length;){var i=t[n];if(o<=0&&t.slice(n,n+a)===e)return n;"\\"===i?n++:"{"===i?o++:"}"===i&&o--,n++}return-1},i=function(e,t,r,n){for(var o=[],i=0;i<e.length;i++)if("text"===e[i].type){var l=e[i].data,d=!0,s=0,f=void 0;for(-1!==(f=l.indexOf(t))&&(s=f,o.push({type:"text",data:l.slice(0,s)}),d=!1);;){if(d){if(-1===(f=l.indexOf(t,s)))break;o.push({type:"text",data:l.slice(s,f)}),s=f}else{if(-1===(f=a(r,l,s+t.length)))break;o.push({type:"math",data:l.slice(s+t.length,f),rawData:l.slice(s,f+r.length),display:n}),s=f+r.length}d=!d}o.push({type:"text",data:l.slice(s)})}else o.push(e[i]);return o},l=function(e,t){for(var r=function(e,t){for(var r=[{type:"text",data:e}],n=0;n<t.length;n++){var o=t[n];r=i(r,o.left,o.right,o.display||!1)}return r}(e,t.delimiters),n=document.createDocumentFragment(),a=0;a<r.length;a++)if("text"===r[a].type)n.appendChild(document.createTextNode(r[a].data));else{var l=document.createElement("span"),d=r[a].data;t.displayMode=r[a].display;try{t.preProcess&&(d=t.preProcess(d)),o.a.render(d,l,t)}catch(e){if(!(e instanceof o.a.ParseError))throw e;t.errorCallback("KaTeX auto-render: Failed to parse `"+r[a].data+"` with ",e),n.appendChild(document.createTextNode(r[a].rawData));continue}n.appendChild(l)}return n};t.default=function(e,t){if(!e)throw new Error("No element provided to render");var r={};for(var n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);r.delimiters=r.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\[",right:"\\]",display:!0}],r.ignoredTags=r.ignoredTags||["script","noscript","style","textarea","pre","code"],r.ignoredClasses=r.ignoredClasses||[],r.errorCallback=r.errorCallback||console.error,r.macros=r.macros||{},function e(t,r){for(var n=0;n<t.childNodes.length;n++){var o=t.childNodes[n];if(3===o.nodeType){var a=l(o.textContent,r);n+=a.childNodes.length-1,t.replaceChild(a,o)}else 1===o.nodeType&&function(){var t=" "+o.className+" ";-1===r.ignoredTags.indexOf(o.nodeName.toLowerCase())&&r.ignoredClasses.every(function(e){return-1===t.indexOf(" "+e+" ")})&&e(o,r)}()}}(e,r)}}]).default}); \ No newline at end of file diff --git a/docs/katex/fonts/KaTeX_AMS-Regular.ttf b/docs/katex/fonts/KaTeX_AMS-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..afcd2eb4d1488b6eb04b00302eaa6e223812b012 GIT binary patch literal 70972 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 LMxXrG{U84UTIJEb literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_AMS-Regular.woff b/docs/katex/fonts/KaTeX_AMS-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..4f575152f2d92dfe48ed316b668e5558c6102c93 GIT binary patch literal 38868 zcmV)!K#;$8Pew)n0RR910GHGN4gdfE0Tny|0RR91000000000000000000000000_ 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_AMS-Regular.woff2 b/docs/katex/fonts/KaTeX_AMS-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b982d6eaf85fcd6eaa94a0302bdf1db9c08e8231 GIT binary patch literal 32944 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf b/docs/katex/fonts/KaTeX_Caligraphic-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f84148db5806b752524c18c6173fa19e8675c976 GIT binary patch literal 19316 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 BW7Plv literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff b/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..ab56ab7fa707dda6bf71209cf0275ef6be2bab03 GIT binary patch literal 11696 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 H0YLpfq!Ad2 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 b/docs/katex/fonts/KaTeX_Caligraphic-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..710c26179c5f1c9998065035a82a578cf45d60e9 GIT binary patch literal 10448 zcmV;>C@<G{Pew8T0RR9104UG^4gdfE085wv04R3=0RR9100000000000000000000 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 G0002%wf^z| literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Regular.ttf b/docs/katex/fonts/KaTeX_Caligraphic-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..97814db7e2c7bb3039692551a4fbbb7f33baa46a GIT binary patch literal 18684 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 OTGv@~T#9_E>wg3J&>da? literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff b/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..aec8a33389cb27a7e2e603ece720eb000fb9a0a9 GIT binary patch literal 11460 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 b/docs/katex/fonts/KaTeX_Caligraphic-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ee5193d7c888fe2e82fb54342f2bb5d4c34b83a5 GIT binary patch literal 10240 zcmV+bDF4@YPew8T0RR9104M+e4gdfE07)1C04Jva0RR9100000000000000000000 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 G0000_1BZzK literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Bold.ttf b/docs/katex/fonts/KaTeX_Fraktur-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..483a7cdd4eb2e0aedd07727ead59f50818497a0b GIT binary patch literal 35660 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Bold.woff b/docs/katex/fonts/KaTeX_Fraktur-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..189fea5e4ff5d5d66f2793d6753f702590add053 GIT binary patch literal 22632 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%0APg>lfK9+NEbfec22@_xp#;D&6-v*%$^l@Z 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 M000310ssF14|n8Ps{jB1 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 b/docs/katex/fonts/KaTeX_Fraktur-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..dc3bd4c040abd23afb59d2fe385f80e23b0cff41 GIT binary patch literal 20360 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Regular.ttf b/docs/katex/fonts/KaTeX_Fraktur-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9aa5f674daddca531e771b400501317fe476a722 GIT binary patch literal 34352 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Regular.woff b/docs/katex/fonts/KaTeX_Fraktur-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..d01450e91806b0c316af1228ce35f7a31788e77e GIT binary patch literal 22088 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 b/docs/katex/fonts/KaTeX_Fraktur-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..7eeba377932c6914b32e8e3da14520c231559e77 GIT binary patch literal 19784 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Bold.ttf b/docs/katex/fonts/KaTeX_Main-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..dc0185a12290672e819e1d62ac9a955311c60341 GIT binary patch literal 60784 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 DvjTj6 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Bold.woff b/docs/katex/fonts/KaTeX_Main-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..acf48e66893be130a57bb66b9506becef9b72f81 GIT binary patch literal 35464 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%0DBe5}C#ke#N-e*jJ!67~X2qoJ9|WECVv5YL zo|OF3Xxps&ht5W)UgH5X)SVqL4&f0-Ft#QkjA3F;GniX52f8rZ+R;y9oZb4Cm|=Gd zr#U{~HBR$68E0zWlLxD-_aqe)8+znQQ>Qr-OKJ!Gq)0C>lGXfLT#%sl>Qb4i(GAnn z3q03{0SvcHX_8#swM;iCMljwEvLAY>dEYTHW8FoNZFxQn)}p`giV4=$D}=q=@=lXf zRE<y0On1F56mPj;(mr=^puej6?jj9!8kO7{I2-zH_&pQURTwo)U}`-kFr6k!Qkjz? zy}u>nh4fa^S<oYTNYJy-M};+Zp$C0<SXW$Df(Bs}O5xs?S|c<c#1qO9jID_WeR$Xn zBnRqhUKzn8s(30pEb3nWJ%}uX=obYnt))BZI}waw0#mE!L4PtxZ^kgUW~dkCObx82 zDGNgvdM_$rPDpZCz;g0fL@>6N0Sw_0Mlgm6%qA1Rv?efhMhnNYmSqkLSpES49Ny;u M000310ssF14<m4J82|tP literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Bold.woff2 b/docs/katex/fonts/KaTeX_Main-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..cf5ababf46d8d4d15ba26b1c72fdeb39500b3d1b GIT binary patch literal 30244 zcmV(>K-j-`Pew8T0RR910Cpq*4gdfE0Pbi20Cma$0RR9100000000000000000000 z00006U;v0p2s#Ou7ZC^wgN|f^!VLj70we>7XbXfk00bZfjX(#ECk%lK8{)(_#GC1e zyH#}^1PY~&LSW+nf<O<AM#ivl02_2>N&f%;vy#dfnifo25LDQ<?}k<sP0$D_+XDTR zCyWze0`m&oyl^7IT!TiXj}rm4=?TkY2t68@X-;H1V?~1XMR(GTkT>XSlikwXw1k4_ z2!z+bR4Y{JvxsI#xM6AOzmPrrca2X`7RjN1AK<~)(*9vX1!5AcBo_V(<ozIz;8FH# z{y#STew?#lto3nT_n_>%`mE8ky)~9>9(W_Y?TYkf^)s+-<WF+)!pFBCzdJMgzNj?B zqkFW(p+r1+h@#y(9?8&XB+&>vKy&Nvw%u+~w<gv^ZzK9xBss>Y9P~h<OiJQSTCsUC z>R+%w74`KSBVYTo%nt^2_P%5Q4B0_;fE2=ax#I%iu=Cty>&LnC<@9sLPO|T5?4%%A zIF*nsD{$}vf@c3svpsb{wn9I#WT3F^Fn*9UWjwam4!ZR(aOYS8B#0#re&-djxzlPk zw_}&>ORsMl8vsPp*_s*umjK4vO_^clRI6hypPZp5sDA;dC~Py*=I;l0?}uxjcdd{> zF>g8tv|FG9%u*3D#pILF6*^A6Y#ILfa_{b&@l29h`aeCX0reo}!7Sj5HJc6deN%HL z>DNqE9FSHvuYimgRETDF9P4@t`?W5SpMQ2+mRi<Ih66Mog5$sehZdZ30fC6RVT4Hv znIxfP^n3SB6IfXM`~T&%{=WO5swCS<gB@xjNGoc<9FLkj9>5&^^kVbZgEHM}ge6Gq z^MUR5j>J>dCrX>qUN^&FIEM|-V;lPrp+X=fQh`7+)u*A~cDLzfD28Zh2lU_XSJqqk z?jrbA>jZKlWQ6J+cTUB92;q<nuq8ccQ?dD9H`AI8TgGY%74tja?)^dvL1k>+b~Rlk zUTTG$|2fkphwX?!m~(TJ6MAa$H%BSa0n?bMFi10^He)15UJ<Cty$R*V-t7O(v`niS z;N7s#?RqZG$>9uFHd1=VCAs7yJw?^OdU~pV^|<O8u%{Z>9rS>^K+l>%H^2jXXvUEm z03;tM=PWo}>cqiWfaiR)QmE`AS)VG2ZICGl6{hn0*3<^(2Rl`FLit?|%>}oBstQ5K zYBsV$_Qe&1Mqz7|>}wg-pdc})wlN-v_Wb6zwa3_O`1+X^3Q~Ep{d!y0Cg63~4DHe^ z8`+2vuk&w7J1+fZy+L>Vyav8NVUV$8l0f^uw69p-`#lKYYk35K8=mzUl^`I5Xd~l` z0N?w7pT9C=j-JaJCqeRkagrL9ms3I#fZJyRpx1s2fH}YgspwN3VaZOq?!w^XD<kje z24*+KtYR1YIm|4lg*W+a9`Y?Op-LH!^d*1lyS|_CxbZTc$xJ6mWe%2fNQOvCN<xVu zC-Q$7(taA!gckHl|H<AQ%=<Z)&vFAGi>#5g$vS0&EMpZTj5Eo4_6B>0o{>cgn^G0( zEZ1ky22=Lf?<1G}<_}JhFUps7rrxjf^;<i8$^0;{^}k(0_w2=QyI293qf`~;qsq77 z7qC6sANgb7_PvblEkETsNYXADBnc@9*}#9rJ&^Zu7G8i2w<a85h?N@aw9D06rq@8& z5pTj5;9oOG<O|7jzjyt7zkYCw`qO^`;OWmUK6~o;Thk+J``ad4{(Y@<sGaTbgV*CW z1!G71y1bojZ)+P{k#jloMuxIFJ*f#JiTrSn>2lHu$IUtHkOTJHYmXV@Mr^dfIztAu zYt^Vsfjka&i7bL_7A@n)0Nwp6=_}J$`meCB*mrl|-Ev2Ehku86^ZLyS$#XE&42X9C zP^dyOq(&G7-Ei|rC0wVN>+IIw|53(;RJv)qei)~@j3`y=(6I1`$f)R;*tmEsL8H|H zdV|qqwpbIBl2cOCZ0Yt4M`l)bPHtX)L19sG$tL)Cxl(N!Wk6NUrHb2k;M35TVFv2z z<2Ww@*8%cm>3{mAmo#qJI65{w;vy46(4SXB^D>~Lb7|#rpasL-7)cnw%?aovIDd#m z4B`R+bY}6HPk|Q~U)P-i>-r336%agx&VZ-?ART+k_<ahza%CZp3*?7DYWknuh<d6M zCqnQ-EVUg0Ve}Otm?{<(F<=J%!_&g2@q|p+O}jUrgQ&Rt4Bos7LSzo?|9PUH8_LM# zG~<bSzJqCu1~#zCKOI;g|B}9hb7K=z!^r(HA`(@ig{C94J5YkQnCJUe#DNYZG|gO- z<g0*9?-_VVCt{SMA^{Z@%!*eYgEj~7!_u&4nVO39K@d6h|9ja=qpu{w>kwbL^u~gk zqJiFq4V`2*a|_QUL7Lkm!1O~XL7S$6fL9Y)Ey%5XKu^7<0Z!NRoc=Rcjaip|5DodU zy?I*<MtbVl`7O~nVf!uMag-X+RLascqRWR%Yj9&J;2V^c>|I7CVos>aYnQnao<x;L z7Ac0-L;$JwAL~gs3*uC4OK^*bf^{<fqFk;h!3%&E_=JS@VKdq|?4Y+)WLHH-Ar-xB z!9T*)`Ea@VS&Yc<2x*AuehQwx3&on&R_5+%^@mWKE3d=Tw+~e(enx*)4F$+M-iw7S zLn%EM%%qB`z6>7ea0}(2Ag(7C4OVFNF#m_88>#55^Vf4YgjV9uoyGwN4#5%z*lQFk z9#{Y_XiFh69ihn6AFN}kgzb74xZpiH*4Y<l<p~Rq_$euWFRqr|g0Iit%g%xE$8C4R zDjFz2->zt)3<=&5%fN_j;KVTq;u<9J42t*$O=1Q^;s)DU9LV$xexaEX`QJ>LZ2ziG z494HI(_+#jt!N4=5*Qc>4V<J6f@BPmWDSbs44UK(h7=4|sHy_wkQAhvh+Hht@PUm@ zAcpWv$W>}k2=gK>ep%`O1~?<qw5z#nbgaOM9p#3NZY~Gb(?ZI=3%5*4*)+~*V_GfX zzQ}G{`%TCe(iG!h_)r9_U*P>8PJ67or6v2ViXGq(gUA7~WVWq#QC~THUsW`o875jY zTYD&&JFKF1nn(xt>f`+1pfOGe6PFuV=l0_@k^RN3ihU4RB=m=m+n<Ho^L)+&hq*uU z8iN{YI_#^w$7CnIlk!{9>+qAW^DlSD;rzus47M2}r)LD(N8XX~gb|ADjK2@YI8)+G zGqx(#<Paka4V)Jw1>cSc+NWKoWZ0AX=8V3{h}u)Y6T}VXZ;+bOz>%63l#<Gcm3gm# zu7{nln+$WnVpJ!C83H}r?ahpq$#^e|9$-Om7><Xy6nUebjr&mJP{lQ!2ZT6|-m9UF zZI27EZLOg}oOcd-UoK9BDA7I`*XV(AI`*tgQDAMjpnnsZomQ8c?6!N)oxRqp9ng&7 zct7=ghkYX~7GiT?In@M@`KIy>ymcg4vo>B{1{nzjKWr)v!NpyY&DBm}_G|6^@qfh- zfFJ`v%#T0RT_-NDi;0PAjW{MSHg(a4%?*&7Zap(X)reFS7o=P#eM5Gu`<(({rOGaf zgU;=}@o2aUJ4KB?AC7LRsZ?^HPqG2)yDlYg4gd^}rWzngGeLmb3=q=`)2Pv~1CR_8 z1gOIRF%ziP$O0hQCJ4|R1H@dQdLs{j<eMNs3k(noftrjW08(s%04*^<ECs4F$^eMd z1OZxZfR9%M9*w!Hv9hOn6x(L1XacEL01v2!)RbcrNbPJashf?JE=M(_z8rT(8fN3j zlG!-YI2%Wro*Pe^XX8oBY&>b5jX$F;@E8dXeSi|W^Gu}TC{AJPd|&AfY9aulT48*L z!3k9LhJxJLxM`CCY?}ryp1umOyyZ)e^ySw+6oST$sbvzJ0mCmKLp&!qd!vyYR4zfm zkDh)EN8(ImF!Atm%8nZ;xc-a50=Gxh@cp3~2PZP~`dr8|j8Ke@J175k4~7ltb`C#@ z6C0jhK%>bk5dXhb@Yf-OPb5AuE*#g?RP9ER;)!sZ4hT)P91rbN<XyvP<V3k@N}3cT zMZxa~k3yXe1#V_4-SS<-tgig1>=|*TpJW&EL#~{=E~*iuGcwVUVp^rF7r5$)RP+IF z^O>=I*E993oRrJE;<S35_dCzRQz-DJsuW_kbf>OL*ClHRq7X>J@Tiq^wlAB6Cfky# zgq9XqoMQ?_SFjTEVM!G3-Xw@rJRVOZuG@vCX;%2o>eW(s5dI`zuGy&W4&&)$GKv;q z7=Vn={$jnjy^{`FoQbT=L^lj}FcM3u{>osCT+b|8R!NZcRH{)$#mZ1GibAEH%ZY-f zh<a$cW&)p`YQA2p8#SStOz1JeHe8xxD3{}0o|Ac3)?7hwwUil;vGSNnDw?LrvYZKr zKZQyzZit5Bh$iFf({irl9XzGXe<FB(t$jZ2ZUeL~h<VW<s&|hkdBQPFHj+w?E7+?c z2CNfG>yQ|K`vdLw<l`#veBu0$t;^UhP!$T={|67@BGqaXvj|c|@*m!v9FC<8&-b~& zUIbOxIBKt@{=}LO=ov+|9fxdkRDAs?(h?+x(LRSwNDLWb_Rho*jaKoI%OO+f{fxZB zb_7tM!qLcstYNPLE4o7*+oqLH9~-8($Hq3q-`B<g5x|R)Nalg((I#hA$U`whB&%Ht za9znpLx47RY7pPFu6F@MVx`nGyGVwv**5q9qKL$pxyMNKF2z2!@??z=`VR@-LkQ1t zq__)&D%hzOWQd=g*t>O$!p>@3?o<B%7#sog-p%K)4ybnG4Ey!4fC&MUj5<ih+tm=s zYv7P(PhG%@#SlL2?;`W~ZDi2};+sO%BB>eI)dv)q<xwQEP(^`OLyp)k_C7r}c$ukn zce{ufY>-M+IJBsc6=o(;29V8`5WM7{CPg{^g=icirA*gSa&yC|h61~~Dn%TTE5~TI zS9@}J*(AOIUXXnN&_kqGU%kT>h6w{iz=ETF1zk=aTpyFzuf4`RKE%FL!g$;(hFm=X ze`x}fxNb$`FnLGSNdE@dd~>D&w1oRrX$rp4ZgmCeW$luJX)sug2?nRT`mU4JahEaI zTwhPS)V0LL|Hm<*DxffRa}MH%Pw6fp=aAxMM*zDz|0Z^`r?3yHnF=(&p(_wUjQ*eu z5mAQFpSn#G2;}VNk(xLp-14#a9A&nE!(~wx-^GNBT0A=o1BAH(sVK_&et4(dgLne3 z1BF&{cs6kB8D@nR4Xy#fkgLV@#tGj}_NaEm$DY@EG#O-5aatutDi!?MhdhYXb5x~t z{@qO^F|#RXjk#fvV$C=Q;-Zu=lA$ma+2(FayLeDa+<7O2jnA+u(iUn?gnTMo>xU&* z<=_h#da-!Yr$M-=@D)#bHyxy|C9kFcSsM?K;+?cxHwqL%Emq>HY>?_%l|{s`>?=}f zdwD^d5K-mZ-xqYNTyTeGv1>gzx<hs$B;Qj{8A?c~112aoEF8e3Y$ti*Hwdz%ED41x zCdp*p+>_~IXC&?5<bGA_x~7A8$+Z=t@Vqi^|A<arKn#vl=O&mOD*BK<?jglj0aw{Q zK6ZbJw-j_pSuY~>#V|>A5gPO|In8I7fALXZKhjC~f$|_);n}F8Q{rw|H-c<u5p)A^ z&`Cf=ef@a@8TE#61y?G3quHw!h&N8y3n<_IFV~*_`Ok3;F_{y$-v85|QkF}Bng}FE z`N0;3+H{ss_^H^KzhN1df2HhRZWuN(?PDLQsI)0vucB4%S}?r~pNFNYL1tzcN75HO zG6Y6e2-{u$$4IDliV$#dOcdV%5=hg#y*Ks@ZaKB%>x~ha4V)d6R)=_*DLT^Ow{rV^ zO(Fs(Xnu5ia(vU=TEX6)=13%^Rv|y;A_Cs#9|AZ43g|3>kF*afADkgQ-O4gNGyo5{ zDAtSfOp`qw!RX##g*Nh=D<%)qdmER!ILZJ^E+sayDDmf(Iq73d7Ge0Pjt^}T)gcKs zH540M-rN^t#hkEZ3;u?}x2SPC4^iYFr`$L*Ya=}$a!d|ihiI`kcCgk6Q9^;^4@QG7 zC4q=P+)~ppu)Iz{|58?g1Hg7EPyjvc83D>5AaUPtjBBQ@chx0KQ%{}GCbIh2mBa#` zgnERuzgQdlfKN54*0d?FZy7hOI;J}q6&9gz(#L+e_|&|XF9zlmp{Vkh05%TBhW7+u z%UF*?o=m0^2*Ba?UaUyjaV=LZ2%pudr*QN4b{7f7F4DFtO!!LiO~7;|A(YjK`G}RN ze~Qe2IQ9dsG&=+0&#rnKwd0=!2z+V~^mD>pw^11=%?pIVt<xHNL=O2%KE{o-GV&XB zYItkZ5kHRy{ad7L-ZPV(%r3KE(qtjo8oii!l|bZqNj^IK4g~T+Z$Fc;&SJY1Po|6r z4aAGs-4ZnV6s{3>S0qk{q9|PI=edV2#@b<c$xIR(e4Tg^Hk45)-rrxxWP!;kU$G}5 zQY#UbK$*&|>qtGFmUyim(5eCVUVp?9QWajp{*>`ZVF?Mq=16oRuBB0PptGA%9M!Yx z%XxWmIV{4z`M%mgsj-b~q_nA|$|exVFQcpgzegyvK22@_t@L(sqS|?1fmiE*_MRFU zoc1&@o;7kaq^rPcgt_$S$=(`a^9pSTEzU%ps>JT@;C6Es8~`o|f^kzb8)Tj52!HOb zzMHNKpgiLsumqHw@4wx^q3^bUOQS;Ost$dIaxtj>I*6+1+%twd&4aM#w_DbDf%_n@ zd<Veg)-uB^9{ss+D=f%@MAu`F$S>@G%TkZlw8mruW@vCqf^QtDscl)N4P~K|Fv<P> z=K)*Z&6v0169zN?Sr}ago_#znLueBs@!0XK-yI5oK+X#M)Jku$M?xq4UX281(!}pC zg?FGwZLbrMdd<xQ|H7;ZowF1~-hcq=n~riBHGG;IH*Zq5fTZF7<ss@*Ai5!UWV_}C zq=Vnz{|3?}Fbsfz^W>9DntH`iW^VLLg&@Av-n_GCe}Vg+t`NH&#O@@%L!Yu&t&uxR z*>HC!PkbB+1muG#@lfUXTb1M`RzMlv^$uyj3bY2GPhP3@AY;z_7CUR8R_-kL0wfD~ z5S$aqNzD{USmEPDSvc`3&0jXN8yzH=)KjQOYEjKnAL827P!BX`;$;W8{dVe?lyOIc z>+#Tq*`S~{coh@BkHB;#!yfjys2DNQ(?PcmAXkP-WxcJp7}LHY^Y~|?(yEqowTfaf zl5kbC(QN41M>BjKyI0%5rd?nGX-O+^K^#&f?0XX!xFSV;6wvjFL%P=+0X^Y2jAbqV zVAf*yAdVI@Q-wMTVn!<1jRJD~?GEzIqS<@oQo}V!H0bXXkj9P@fD>hV@(s8C8DJp3 zzf(m;LVhg9a&!^Ju=eNuM)_PO#P<F!Hy9Q|GaW>XFoiJjJbA^-Lkpl_@1yS)`=hs6 z{!<y*Z2X-K0@d!^a}Gx?FT*8vYaaM>#7<s%f>Z9bsU9#VZ|NRdvE=lePEDb9qbcsf z#@(R?w(o`9lT(Q-wzp{>4v~Hry~I1dt&23E0&NSse+P29^9s_@YY->!1GZ|d=Hckd zT0wpMEu<-7C;K)0YY8Z_%m>$yLRNrk2~m|I>SVl_jibS4dk4Gs9eA&9n}AWnM{l_h zwTFQqWO}9hiu&1b#+uc~jdbkIJVe0s1~$#p5-;6JW%bn#)t_<uoFJ*1pR)$Q8hi&f zWjM%{upt^Tq;+T|_UW!(?I?!96+Nq=ym*h@Oud&n>2L+`{5%gDO^?0bq$bQE#^)At z{E2zinsHj_cTpfLSoR7x>bZxB_5Jp9URxQ-gNNEJKEqBrCcRZ${2rv_`?B?c*6<gs z@+@n9ZVkKHQ%&oK0}_FkSsq}Ix!f1ucA>Nv@$c=T`2!VD{?$0(oNu>CMu)pQ{*i0a zh0lb5GJ+2i2Jij_m=9v@%e9_me^|)@LC)L9N^)hF><Sk^`2o)tN|f}lh2G}v??7~r zz)E_%tJUlaH1F>8gZ=e7xLl)WaoBIhf{C+2vKkHOv&vQ_R>Bl25Kwk!HB;fySUOtr z68Eq}eQw^KE_bU<JM1@Yg4i^ls6>4FEZZ#U#k1q(ASJ*yr|f2MsG~inD2)Bo%Vu#W zgioyXuVBMvr!Q{cUt|R@8<gSWoS=T9i#=?-i`i5QNe2nk>H+oLu;{epwI7F+UHb%n zEAd*9wp5zur_TsX`ILQuJvg4aJ)57$Ze_q5kmif`Ekh)pLsD7@GFOURNP(f8<QjnE z=@TQo#NHuIh}aW20M#NuEbLHJhy-3l3)QS6_yOM-Zz!C<ZpusY@?69xAyldzD}_CX z>X&gg=%R~B_d)Z9@;D3=YEbe2elEEeo-xaj6itQ=l*S|_-O-f`@`}vzXq<~eL6ca0 zg8VdY{s2ZwUJJuL75p%a!wY}lQ*47T^F#*TIs)dTA6Nz*in{&ym&p)imFJFQuj$Xk zj&!lrgs(hYh{F>A7^04Jy+>t{ud*G=8$zCIU>HYjmZQ>FWggJpIMz<pAm@<;-89jT z%Z0fdZj<5{{@?bkdtKMJ^R}(%r($6l;30q!jkED6qH?}X{g@*Fg1D)9#$LtlU*R(< z5M(YIw9qm7Ev})s>aM$ff;^AkdUnDX>HNx9o&j3EIHKsx&h9S#i(h;e(*70u9Z38V zPHq_mLV0{N9`UhhURuY_7PH_nIxgQBrtc%=Dq2E})KYlf@7QNKpaRSDyjJTWj?LhV zGda%e#2s!@Zc`6cVh?+0VST7d{+{A(F8G8Inz+Kdc$UnfPbzQq__MbPJ%<h;ui_*; z*)(h(Z=vZ%9^N1v<p_pCWo8}p8IAZJt$Dgv&l|&K^kJZ!?p6pN54Te@5ZkmOih7at zDqdFkZ;J30B{sMKij}+whot7W_Nj_((>$S0!DBoR(_ma*Zxp8@ldN)aY-f2HEZy{o z8QJl7W@A<zCw?tNpwD8Y=)UJmqR^Q8<0a397C={5HLllJmp3D;IsPoex%OgYcyzbg ze!1?4W&*CB91^035SNujvm1I4n6wGBmcM5*D#1wn*5C3}(_`6CnAp1VgU0fh=5gJ` zj|qWpXk@jloQz2|0XgAIUC16}2Pi2EfyCl*8Y%?YY>BWF{U65qHh8-AZ?aK_m`$<Z z8+y?-v?W9!P+^n;BI|m^W&G?2`z9}e*gT%O!0TTRf-N_7-~FjU0CBvgtfFUMVD{Gn zQ@!CiOa@s3o3tOHqT}dq)d(I(F+7zpFpk{o*_e?U^ukh=Fx5JnC>(-IyJ7~QI09>y z#cgqxBY|v%@JHx`=Z0XLLGTz(lxODtP^bWbpII!{Z7u*8JAHhYOIh79&%N!-<%mAk zQ%Np;ads!UC|R{VSm9U4VcE}^FbH4q)dZK_!hH%7gK}MXS34Hi3;lngyC=IT>Hz-D z#ef#M*z0&?B7eWy3CK5q>M<Gd>T8f2F;k;G8o+UCNK*(s8KPET8UPT!5&TM*R5Uof zr^W-GPFezs%}Z`{IsZG3*>9B4v1saLrwU!VeX}o~Oz{o3CIlL4Jk;Im<67f0Y!1Ne zU(J9PIz?PcVk;I`4fuw>02Pn844V#8WNNUW6OCD`5;C)Vo;i9aSAnphazCZ-&mc`@ zw(9^FkqZD~#kf6#>a#T*(%ym)5t7lnIDgYxEB5-7S+LUPDtW{uc5DHU-pT|eGz%UU zJ&xmDw4!jer%&oUxweLZ>o54)fF(BgM7OxrpT{;G>-DEs*cm9Veywo9jVGP1x<@ew z2&LgKSptoTXStYHhNNnXBOY~vH`*T5k3_)kg3%xaa4Y3)xPL~+II8F|=~WOqk?f?Y zL)^S+n{4Bu>>lV+?QJAhn!vnRq?d(5siPpd&S;LRqb7r?C7ZnkPB&8G_;r(}TJz^* z2~I<iD(Rs1_jCzuw7<M#!G0goIFANVUWSjkzz0kItBbweNEiF;WFwwoyzdx0G}RSU zlrR)APSyB(9HVIG<Vv@|u4nAN5b-AOym1n+ZO|6|83W&@o<;3Hwd`5r9t$a57nyAY zb(**sMaSTaQq$Jv=AtuD2W9J*7J=a8&>X;y4LoDb!q@Fa6Cw)m4k@y*9ViWcb;+&H zZ!C!GPLGhsOjM>&Hwq}cJR0uwYa&^sOxGGaeJ<`5#9RnBA4ggN>_jD00bLBShgh@( zF8#ztp=lc&wO{LI3jp8QsDb$p=f0BZx^yh@o?7u4pw#nS#bNti1NUH(x`M;yr}^|y zRZ|z?beJ%_cZeK4Olz|eAgNIO6nqEGf!8}qFZ(H8!4)`$u;m(v!`|}Q1Y4ynvdHt@ z6?7L?%QH*|MaCgoRIdU1G?(pyKK?US!O^g6V`44DOfqV7pj)uU6+@b;Y}18KgIlaN z=#@|`PvWgaDw6q<p2yiPU<8xl{I_;66szR65sV0;41Blx&~COUed`+_bsbtCOrUu+ zoRoXg3dhoSG>61cKCE6S2SfgyjWLSd5^9!LH`1xp?M;O%OsCXB7B+m2mKa)*s*MFH z)brGkg!o4pA8%Y}rmdRTaVhV+_II-X!gelR(s!0gnC*BGEtO!SvA4^`35ehRU&aQY z9f_`h$=5rOTJx)Q&>!&Hp_m=ll{PW>IVPRCmfU1a{yJqT&fegy6{2yzz6a4Vu$5U{ zs9I~yhS3b1Plt_SKI~ZgZ_F?hKy|E!i(j<335Wp5`0j!S>Ok49=NUgML!al9VahX{ z34amKyp8Pf-wa%P3b_z9WZUK~E?2EXje2cMsLx0)a>3U%10#)sxiC-E3ws-ZWc2Ft z?7J)7zVcTXYi5@qIs47dI{oadeL^U-@gvZ&o2C<9zBYpVI~XF{b8jZ@mhiB8+mc9W zSLIfSk+o`Bd-!)7Qt{cHT)t*(Yn+>L3#yU@Y4+a}@4VB@34YfvneBoI`ai|J?edrr z=w4N>oU5B?iTvMi0N-K&YH2NQs-4|$?VM!W^k`7y)hupp&F={FkF&Fv&-z2%R?vF& z^){W7nE^Fn6NgZu{#A`{ZlJI3?C(rBSR#IjTV9V2Hzb_fciyKvn{J7FF~NFU!P)nG z%OkHdOXA9w;3N$1Y`=Ke>6MLry}xF>{kTtW*T=<oAo9zbZgH)_mFZ@eQ>zErAn1I^ z3Qfxotu*c4qd|=r3|X$JO&YbeR!#8vVdH_>ktLHtP3zTUv!9vc1C!TqB#HrjhWitu zxt5BwN}_>_c~3dL&F2Y^4s3nmd`RzCSB2J<1K0Z>u-Z`NENh{rm$Vp9*ZNOl<ZA+2 zV7I|xPFLLooG$kq>OKK__PvnYprk&a_Gp!@f^VdZ-X*<pTABfC9KIRC`;EAF_vr9R zw!eK-jTPU_h<o`2eB~hx(AnU<fl-`|C)mM-+G3=rB`rs6nOW7&hoHe;Cpx{UOk5G> zF0`1wdgFY7!>2v2o|0OuT?VVyiSR8tPp;o6fbHx0s#AkQ_VqQ{AT1Hqrrk_{o)d>E z>TE4H37skhl@DZfoU}(1rl}^C#rQ21Iq?(YDbHf#8ZKLKITSgIzn%fVA(SYb@wbhv z#mu~GV@5W(b$!A@@#U+N!jKg8eE~QroYw`RS$B^g@#Q^6uZjT&TaQGjo)1Z0I%*-w zOXj8yISKMmMw|2Y2GPK{-=-LeQf}`%WR%2pFAU4sNG2Z#-@r0#yXL7eQCz!n1x}li zh23V<nfct%_ixHGtruw658vWE|HjKk!Q1<eyZF<DVD}Gx@8*IB>)Uz!+{p^_<43Ln zO8+hX)ba|B=@yec1!lQFdhGbWREw_)?f(#K%n)u1P?MbWRz#VI1i^D10U<@SHL9B4 zm7VQ&-wLnPfRw@AWh!#(Im)|RKv0ZdyU;M2IWd$s8JUU)6awM(((=&Ft3x(u4R<DR zU@1B}ElRJ_uWtLb-bXo?-vVSe{3as0&SJcj%J4TW#{Q<8KFAUz&eEU^oahrmed%H* zk7JETLdXl)Ve^>VKxy_-tMz*>1cFjVzN>yH3^?U=-DQ0AgK|UWQ}y3qQb|*uh0D^> zAcg}7@u@{kX;YS30EpNL92j~&7Eh7;R^on+gn$h?H9&W`g&8+wcHU^00cVEvMWZ;5 zF7dfj(6CdbTJK&Z)ij^>yW7|otC`>i!BZAKLW&ow+`H^R{BZn=a5h~baY;EdKswsf zJ#v+KN&E0I_B6O!D6x0>=(XhX=7g?!ECO)aa@S1IZMhMKO=yH<j2i+aYjGuPvdFoT zRd{-(>?|Aql&qI6M)!UM6_zGy!4|d0yl=VhT&00MRf=nl6+;AbPZbrO(yecm{*hK* zk+{`Hy+G1KSQExwOS8wHA|uc7d?K<Z&)m+<!Ndw=_BipQms+6Vrv#-Z$)ZVU&%7mF zav831kHDX7aVS&!w6kP!(ch8E3JKB%i@HnMToTDe5IcTYp(y7rVdwivs*|37mGjNX zzstXevGl@CN|1Kp@B9^=?-~80xBY>#j|XIRP*Kl9qrvGr{&w|WqqM6iApf&8>e={e zC~}%1Bv~L%;+6j)gUnGgrxx!oH<t4H#i^-pesDSi$J;3_-WH$Q2je!1Z~NO@jb1%G z5Y4`o?THRVGXLZ9qUy_;EwpG2kdE;8!mTn0OBK`a;CthPvEgK(gS{VA2T_^3ki2V| zNzu4CWD5D~YZhsYOh^q(aBp%Ab2VIlJWwh>e;I2nPD~MqE}w;Ioxw45p1!RI`?~rR zihgp%wdBZL_~G1|>KT}e!m9ru1HGx+JH;W!x9<;RL!}iw;w2y{16kl$Bbj<cyU5wF z`PMz@-rqfIW3?ddfoJ2_S~X55l}b{*hchLSmLj8Dk}0i^f7$NgsRa5{QTZ`@Y+cEL zJMAl@zu0Iw@Ke!*BKRLM)d^!EDC#r2{~cxY<8VgGV!MRs+!F#udiI-dWUGptp3|06 znwFZA#QOQXO<zfCXv8K{Y#GQb&ux+L8#@-db8BIFGf}4qq%8f)imjA+J<5zyIV$4_ z5!~k?O}^(r)rc{n^*}M622@t?qf3cW;7kR)JGanJxUk!Em#pE`85f}chCVm5&mYdX zQYy>3Y}n4ZG-kVCZ~Fv$z2ksiCIVkxLSo4qDm%}YX0MmlEQ4|%>~?+(`>-xfV)U2T zKP?_H*747bjw6N$wOR7-!7jS)9{F>QNa)c&LIHS1JooyQE3ZGtIl`;md~#6Rul)4q z&XZ+a?b%0}R|?MWj)l=zy6sy5opV)teJNLzKr=0YJzn)b*NV4msg0C$Ji0Pi`M~>( zrf)2PtEEob4gE#DBEyL#tFEl87>WY-m8bkbZDRu);cs83=)>Dy)B8E0BZD$vHDkrb z<u6rk{Nfwa?ip+xnP#4xUxgq>{LXKg5Ct@;HEvSN+)xjVp#0Tj0bU7zJ#NK8^=9(& zoAS(+d2SE)H?>wxLe3>so6y?Rp(&$9BR(RBPytdW>R%^=>x&am84cD25%b2cfeq9} zD%C`Vs)h%mt=BZAcF-I?cAYA8X|2=+_MXVnCd=_vv3en`hPZiYC?0Br;!6K2bnGuo zy*7B3(Tvt1IV>SwS8#h$_G*<|(8j$iCy7C5R${$k4>h)wRhpxhMeA966}igAZ~u^L zP}#&uv`fO+QgMz9ejpC&9Nc`auphuRuXPi2_S4z2DT?UQTi`Zl$Cg}#ze~`7x?6uE zB7pq*m-#wl^TJV1lhbO(#tus*^5Ur^{RLeAg%KMU^Y6A2m4!C2IetDsoW<zN?u-CL zS+a0h-mE|_lj-flMJQsmiCl}%af>0hzwsm<Ebmnlhj0^wU5ZFG{WQrWc=x?^(Hur^ zdcK(`k5S5c??1*pKb8_1Q$_$9)VTpKx@lgTAQJ}N=vPunI5Of%!RZlVVr-QU0<YDK z=yZv-T%*@lIEd$QnznPl)WZ`Y8j<mFE@zMcQ=C3`d3c=uc3q~tqqc7Bu7?`JPXjt> zbB#ZGNJAxcl&0mz%{g4bAF-W{CFs1V!_x?@vz<y`QsGuAzDcY>uqU#!Z|?w?wy^q` z=*{4HIk*@JLbr8-^FI73PT*ZLitGBYWZO*bd)T(Ln3sum%m3hZ@zNK;)nPFI=P}BE zK&iQtT&GGst44BKB^0S_VHt!l!kgKUR>c-Zup^oPwim-&`m4h0x5WRwykad^ywu^T zQQ|&9QBv$a^1;uD@ctjSzB(re1;IW6UvXE7^(hf2)G!-3{%if$NNUl;bQ+7UPI&>9 zl=@dFI(&LVT`U?(>-=OE7O8n0c3u@w6$f$?xkeV$-J|An(7ZLknmB*o9<Od$1BV7s zk#xdea8M_k#-x(dad6lIpoYV8eGvy$?nz61)7Ka3?gDE0wDC4c_>&*;Y^D5nyR@P) zmmG$LLM&=3rKH>@HA%&lBH{50A`3LHF&F|aAz({0E}yNmu;L8n^40MI+>FM4Itcx| zDhazp$(H6ySWgG%yaZ!eJ*<hpzlA?ZqgJ|zN%;TAi`Z69eZ$X^qM{xD+LjL8f{58$ zIJTY=PqjTsJOhFRsL5v}*Z;7XR$?ZnRA)Lf7mdX_M_dVA#b0L0e8QvwG(4B1`O(l$ z6^Rf|!TDJdk9e!1@)l7j?%J1HP&rjGuxMJZ3(p!QRqhW$aDM<YOW%cRvgTl{`2TfO z8nlT2Iy+Q^^yHIp7#=Td$W6e?ibLuqvcjD<m+?P$n(x?AZCDiO?ITXjaQC>Moqii) z!ovu|Pao`+#>q0P69a5D4F@+*B{uzU!p?NapS#ogir(V8rEL8#*Y>~~>u(M;r0vT# z##M(egG|@TtX?!SynIW{PcUKgeflO%FgJq`geQc^g0FKhdzaoU#28Ge6M=tyI+wEX zZq(krAdXxnWikrmm)>Ibn!-W_xi8%q_y-RIs>VT9+ot7*9^gr<AxH`1@Z=pDW)gn3 zqW@5h=*M&DD-QR2HkVTkHe{%p5#Cj<sAgFJ2pxOIs`KPN=90=(T?>#?%^j{u-&bhe zAKSFE)who`vpX?RJ<b4ocF%Bn3u<p2d_4ly94=31h40Bitq$+sy#2fX)WxQ|X*ZlE zz(e=xgR1+JF}$o05U1vIy&nsydm2@ZBCA7Oe@jUUuRbsIpq}nwNXour&);d`(AgAZ z|CUQnJ$q1^l;$D8?O~DD)4dyyx^cctjNBkIMzu}O1??7AThS#!`S$P=nwFZu1fS2s z5IwL$y?I$NSrM`Q73e|$t8fX+$Mdm0QBA}8{`?baw6HMNU{d5hIxmJTmG)!dH~;fG zC|6dka`Sqc<qX!CY^T0{Q>Py~s0o@&jH%OXxX`-=ijadC9-=o;>J|7SLN^F4Eo*A| z^EqdmDAtk~$F_Zom+Ah_OM7l;&Pa2##=aDyjeV-8X`D1l2rmmY_oq}gX*5Fu*5_W{ z551J_t@u-SeYL*r(NW$DM5a%=gN;r;CjPMtid9&{1D^*3R>{K^r=_xkW;WD6fzq(X zLLqke_Tk$SyAn@>g({sF`}*}<EVnQhyK|d6cFK}p4;XvDmQG0{Boci?blCm(4sd+E z$h?*BW=L(5)qQk+B6=|2p#wk_g!|g$=15C%-%TLJFqmtT3)`78PcjOF(a}u6&ioOp z=xQu>jyJZ5qqb?H&adH14pxpP-Q)QzG)Fuv@Ds@7(TCT`S40>7pAzA2V5n@K2}@)1 zA{|rXBX9Rpz~2cjQAP(}YL<9TNCJSX28Hm*m1o^6<AJ1>)=16kiVnb{A6q0#s^tuX zyu&qeE+tp$90oEgm|7IWLsA?h7UM{(Cvgai0FHyJ1p<__>%_VsGqNHGFyOhr_m=p( z3d1!4yH*#cdgC&Lnv2I*@4XYBAHlZJtt%bH*&T5ao8*!=8m!)}ljd+o5V?6xRB{%( zQChZ=vb)XXO<Rvy1A@J*z;r$YXMPnIlN=v(d#P8=L1`dAct;<;lMR}z+t&JM4^~9? zBlxC*XlF`5;|9L>j-PCIH#nZ9ne+7t;#`TSoy7}@+x;hMThAzy2BfA}FFEH^^r~=g zjMZR(<myh}uC{N`d!b%T@wlb+q2-Ymz4r&XAv8hVa^@5A=KDkt2ZDwwStj+24uUGE z3Z-fxiVDyW!xrOK*$|aB6l&P2-3+GV=pc<c6za8Bu~sNw!|pW<@w1X^*xX2MRaLxt z{_<fpmy;uDYzTbsypUB68k5G1m1@w==rNOKN?t{!G3ng{;h{q`>X6L1Rlh~F`_H&a z35$EaCc?Ug`&*Dk>(lt{u6~F>73x%Mj!Ne)r5RbacztKEfIWZVYpKk@g8}9vKe6OO z`TV-*<^5~K*|)KojfeJ3Y|qF`P3>Plw{sF1CN%mCDio@6BE;srdr1FQ+SN+;uFcd& zPdA~MQWb9eV!PEDezt|Wr_;j;nsQ>=4=Ndnvw#`^GxLwDi(1QR4x&fKnK;`@UQZ?? z(Ck5HnbGb(sBlo;jd={5y>NC?96em-JKs@pm4t<bLEwuzy&_%9_w3Zar~{#}FpPq( z--^<(pg?fy{p`i8M*s>8!ZLoY076%(Rzl_fKw9r5*yC@z9r%`{hkPE3u&Xe^$ONwZ z_<P2e`-45-^skv@D*p#wO<n4p4Ix8er9}mQ+U-D3G288WYZkW}sG*gW0X=yft+p2( zz5eFGG_CwnmjOz`SUi>vCMnlw(*aems+?3$4=~vnBW~-Aws4IsTw}DY_h>9&gqR<w zC{C%r)Jf0Q>vMD09=JJ!j<ETHaPKA9cRvBj-#jC3&8d3LxmPb)qZ{eFegMA1T<Q>@ zG0CJ(%LOhP=_^Q+2?3P+;^Kg`u5%}ZY|Iy93#_N{B@(>ZS%h+*GLt7E49D{4fw)ze zE6|12<GI40yzm$v^dzOl+4RV<Ow*5f2h_9MbktGN3h`coJ11%Rmf#;>lxkjhPEY!9 zFkg)0Gdb<~Vh~Fi4C?6GS!r!b=S{Ks)u9>j&JJ&@TMBLgpYd$Rkjez2?D8Qj|72NE zuhEQpRL=aM=ttrGzQ5!_;p$<GF?WOtcyw!U6wFrP;OnG>X|2ciNyF+b+hkPCrFLOd z*|sfeHvC!sGwTlNRtAV1fuZHEmub{AtXbWR(bT(kqPvmmNR!w7>Ej$jNIamq`1g8@ z@#X>Q?=*V121mxypIx9}9!Vvh)R?}1Ek20UP3I<6FFwyjs@)r=*BLPiIzajz9bSNl zSZ#~5Hh&5<?@=2<w0*M+As885J~2we6mk$e2ZKuc*t`qN?)4A}@bLsEK)3Cvj*bng zmEpE5#8-Qd>?B}5F;{=RO2X+QS=V|X=kq0ix+QL1ZgZg3j!d<H{2}Uf==cDK@V9Z= zL)#k^+QH>@yiBJ+G^Pv7AQEtOe~S#QKLZSY8Kj<Bt5UMP$JiW|1SkK&{XT`@!Zd%Z zPxmWP9UY+W-o@-EDdZ&jisaNv=Ru!c1B{uij9#`d-AxzRJntP&XhrICfMI<^0`pM2 zH_6?N#GuGAA!5CFSiNPd+DMO&n<q`+jWzD_x}EOfKP$}*?Ca$~@naVjT;G@iBncB8 zK*imAm*e8A^AAL;lqjX1g(@5uDODqEqs;IzYQhELAm9-<*qeto$@8|=HIaCNE)RB3 zOGvFtJ(HjJotM=OIq;qJ?<Cd!f5#-+KBw;rSvd&Pmh6j{VWK0zy{Q=Uq$-xu)%^1( zuRS8=((s^NYh+WKow|mdzm4mm=@b3K*@Rs)0b_ujrIY;@m$u5rR&v&JsHX71a3hUp zu2gboe%n!|(O~JnKfG>;0bE#ZHb28VZqaZC40c*JPiY&>%~4052um~zRXb%8Ei13q zD%~AZW;EiFMnc*D<(u|16M<mioIoe#a#T(lDbbl}S04=)yMEMel}i4|EFx<?IljO8 z*RA4o%UVo<<&bSw06}g&%4uS9X`ZlfNgXL#-~F7w;N39iPY}jnQiWFOfo}}-nZlUO zXFqIaVpBeWjd%~GmQ(>_*61V7-=)tMh=_Bnx%op|O<4i_<)`5+zpsFMBt(-Gm2_0F z0RgUFabfXkM|4Y^a|q^K9ai_!Q`@QyO>gzoenIV-!HH31gWFIVmQ!@E_|7};s#-Kf zcXP1FX!TpNc7%x@eFPD){FV?mQ|xw8Dr9Zd^7wr#K(<Fxf6)?IV5bc=v&Ttvo@%uf z5~ap&cQZAI6krszcdVC&1qFdScE(S~PiKDAJucw(onQrKF_-nWJ|Sb;-^=>T27|Pb zY30N9+A!;Op#iMX88PXdGvJDh80E<ut$&iR9(!YYBlz{T_?$g0dtIQk7LS+GSeCLR zxjMN93<}1w3uT+9g2BTL*YvV?>h37Ab<^q~NpJ58GR9~3eXQM}mp4X6TgvAVtgdeN ziZj>iF>Bfp$|9Mp8s{?s@I#LPjdQ%EMFKIXG^Q<y%v<%Mwj30T5s1kH5foL;2w3bD zF?dj)0!%CyP)7&|6w&qS`#tg14b}0!37=CTI<-<8<$3Ap{w)X<rE&T|0ec@ya5R1o z`e^Z88NF*cbH`GM3F1zE^UX;d$b{oVu~5+;64}y7*@29L#41CKoIIT>=_a6@=)WW6 zGQzh!u}Lb{dqkH77xcVuT9yKpblg{RhIoP*>GDqTVQkHHb~9LyD}?DQuZWL;$B7{9 zY4pvm=v+}v#8-B=y0Y~#N!gFYK|vgiiiYjTs!WX~xBrzjozLS3eqNPlv2}Q_*9F@h z$zFDmw+AWKLzC3P;Y!O$P_#gAcK<an?{aZ$SJhpep?$7gs@FIq!gGW_+KY~WdAK|r ztWwdQV6hnFY$b3&9&hq*%Xnma3C1g97|Sw~$ldOl$BT>EfBs=<fSFhtR_>%~H9`FQ z4?hht-Kfs|VgUV!!bpxtUF906rss*-ZCMGw<oP$6Nc*PBSre+M&7{=$L<7M_31>ep zP8`S9kyjSDf4}>coc3jV*!=SPt0c^*(PR2pvhY<smO_9U;nA)fnYb;cRqVci^HZ7d z(Gks<OOu85m(v46f}3mFE2Cp3%U#wNC$2`aZ>asa@A24UN^Kic0%-<V==w01ce1+( zi63um(cP@0atamc*(E^Z$@G+f+`wXeWUxN6D3FdX53TI$CjvOhWc0+Ee%PB5A=ddC zCp;x$sQ8hDA0pvy#b9z$@r0bquU)sz$3}RkfnA+V8O&LM$sDrJ4Hz0SVx;-T23f?c z9)d&a-9ydsI|M*|y%g?h!xBE7uEi=G=F)>37Q&ufTnr4a#xP{As(xO%nPU^UfPNtr z!>XF;_C68*|6@kH{#k5dTTDi1u&7$>*)L}QgBzhl)00@A-Kf@w@p#yZ09qKQsxo54 z80kqZahR^z1;SX?+oyL%nwD&|V8~p4ElR=|k0MP`<`PYV5kuzdYOf+(3iJE}gVw25 z74@!p@kd_ebLJ=!gfm%~eM!~#yI^QbgsIkK4cTM_MA`hHf;3L>@o`2ygH{zlDk@`Z zSBJ!0$w$u9eM`2p0#|?Rg+-Ko(tn}f8Pk=EMH}kKwsyy~&hLWl$3*>+h1vGmg(G^+ zW?%6cjU56%s8uI-l(((?WKlPB8G8En(hvUZ*(J2fdQfiqr2UVqz677(D`n2&Eq+5M zgUkMVT~pcAT0&xo|1eP=4cR)POgvxG%V*P-wzm?WZ1l#WOOuU(1#6Y2F$HGwWG#_F z$e4yrT1hO(&c?{|E9zyL*0{q&f&wQujsNhMy9f47P&vka@ZYnGIXcRFCfWOVihugb z<)I4#9}-!r6X%K7FxlzBs-TK+H@T*qr7h<RbA_Do)5%G16_5v)QdT24!RD&e#rIC< zO}psWhikF58-a0OkbT#K<cvDwC&6T1JclB0Ubvj^(2l*47>buZ|JEzJ(dSGdt`G;p zvnODnZtRAFp~%|_#xfw-jae_Q?M33LQ{dsi-b7%7`I2_<<Vl4j+3tJZ4e0CU*N4zB z%kaDN>E>n_Gw~y1R>!HE6|LG})oO*hIWA^(B;SM~bI!(j5~d7mQ#a$l%CA9CAi(^V zynEFFcY81a1WdEC?D={ilqF-Ta9RQp&$*DtD*3Zoy=pm-cfogN=@>6sj5t@~qP4^X zxB^^6i9CmQz&imW_U4qK2qqLlxWXSmhnqH}Ga8G3I$!AMC1NGBXZ@nJHe+@MhRl^# zqa@73UkF06zFrt|wcu@V#c^?FS%S?`(e2mZ2h+-c@a5AI6idvg;2z+Wg)Wc7M|HLQ zCl9%N$v_2ssQT-cFG5?C7*T!VtG=oG36ksHV=9t`nnJEf<|$0`MGP$=1c-6~Le8F5 z?dDKHl#l}kG-f6$mn3TR?==`;ehk5vE=gy&G!*PPv~1_w{+ho(pcqU7NGvWo@H4c4 zxT~COv=S)8Hzu;Gx(SChQYIAy0V<OfZ6^SnLcJm#s6^RLm7w=l_Guw7z!19$K{_4} z@Y{ga+mT0sY*{vNH1c*U(8dSwcseL_lQ4h*US;ymm%PERzGAm>xSLN7N>YB33VXg= zvjZ4lNT|SKX~NUy?|MAL<#e=)SxV^QF`_-16-FaHOyU-tUM;f<g%a;%&0dlJG02z6 z-j%2~%UJQoN|D4Zt92RvSo!~u;TMVQo;>DQ6a5cYitHENeVO7X#pBQT8I4+BAF4M8 z&vEj&PEKPZmjl#rxW0P_2C^&mx|m{r<FrRKC&HWgA;V*Z0{$Hmr1RY_@3HT>?2#oq za>kY)Lu=a}U}lCPXg<=$kpG!$ara|Gg(Z*3zIf=(44x)+iasWZc@&CginoL?4x&1_ z-+X7)9D;>Oyb7n<yb3hf3OrZ%v*KUftXt`(dDj1}adctVS(1*ZSRhnfycA5ud@*V! zm9U`rB|aBPo`zY~Y&H(VfdM=ar^eEM*&ostF@u4D*xS`ad020FFKBMw_ZRgK3Vu%2 zf@m`gu(eepCDB1Dj?Za9eQ4$Iaa52_l1gNXN<vFh<7y@j_7owU@TR8LwXU@tMB{EJ z+y@5+KoEMqOQaPWvuZ*zg3=CTb(EOgnMx3x|90v=UY&HRBaOQyO44I3{QGrf>JGb^ zhiBoY0^iuBW42I290qE>J&RR*9NPN#_P>~4wu^Gnn)&E&K-}RyhACC&RiZg&GgMLH zQDb^tVHVqBbuT$|*BaEVK!64{+i#Ay3agCx4Fyf*1z}&-NreodU{T1&5udW#BU0V7 zlE0Rb^XRBS#nQEHiwRi~?0}81!~+j@|4!D-CzLwRHfWSA$1jCt)vgNGglh9<8a_2K zIn~NHB#pDUMjeS+6qeLnZ4M9hFC<?Rt(6u9(A5&F`Y?ejBc`0+QYZKRb31J=O0w9g zb;K2vFYqz@8*hpvGxBh+N(m|Qh7gYfHP8fU758}dum0}_1a9yN{J|%1JMcpdhZ~`- zstga&pWpOdALMsA$oEKq&kteJ3##${_-z8A=WxF>p{po)EmK*J`TOtdUYcCGmARNH z{h}83rDt!ulAgBHfy*9otnb&o?~}0D@cOS&pxTkFxlB~+Ljn;YSn@BRrP8US4>Nyy zm{!4rxl$+dfI8I_t~TLhRp~#Vp3Io4x^z>A85FVncr4t5pUdq8Va0CUf0mqq0`C;Z zz6VTxa0=e>^-a8&@Mlht%0kZfI@tMLsqUB1MjLnWL8eVY-tIrhlD41+i(Fe{C^0ny zC`lq2lNl*{377)i2sZ9{sC%RIsKdTAGg{ry@WK0g<%G5ko%K;g@hcB4F~zjE>tg}^ zH3G_N?89Ad2xDA0VRO$EShwr**gqsyphad}oby$aoa&ONqCDSL=5kt@ZYkPO&VV^8 zC)pAduCvjp?VLm|lcBn-ycosPrFmO#-;|}Tcd^7*WrCmi?k+rS{7s&YTiTBVS8c(+ z#NE(gEl>a&{)-nHKA<SDehf)BWkQ=Wr4XFvuHT^-4V?2>@8&r&X;CEk_(TB)&)b>T zNBB)kJ+``YL!$JNdRCNAl8;3(Im{AiqbzYLq)>wA5&vRSEEo5FR)vueN|Np|s{PMX z6BoUtbvGp>WwA`=#f}E2ioSCk7*E5Q|0l9^pnL`A!6mQ}M;iIF1%g30K(*0-$WV04 zy<Mc|(Ij^K;ijdHN5PSE&|gh`y70h3434r<2?N`ANHYky)~PVdtjf(zeM#Ol>dlUS zk(oZa(LbsER?X=~;Bg~>EMW#F@5^mc@9LuMT_b4Ttn9VsK?S-!X#)$!ct=F@u<)-U zZu&&GVV%dwD)FRTHhF=ff9niwJ-0r?WEW)^8eg&}+EhG8#PXqsMP4b_0U^)6I?3l@ z0hEtX(AC?I7>>_E(x*wEu&kdXeY_Ba!Ql(Y0@w}WaMfNM@The_#4;E5Nf<j3SIMsC z%MUTBPKw=NomoWgV>mM6w_iRxJhy9FzTXca<FF2kd{hYvq=#25YAaRF6A%AK7m@Ln z6giMVf>n*qksi=itVtR@45BQ-jMQpL9|FZ<kN{IN)9e|rzAdkLpuV><$~@H9N;M^j z#j}jsjUcXwSn)13%X4jCf)WB!b5pd*ZB4>5Z>Vm+az(M{0jWOQ9?jeryqCncaWu~r zmVTUuI5j?q(d}hrbZl^Ziip$nD=g1792=jsH+Uahs($OpVJmU93j^e&xa8*Kq__d{ zLhVuk^-iAP?n{W;@jfdo6x@EB3@?v)ZoUe>1j)CzgXEsKOJLZFeBTxgj}gGi;pwGx zbVj<Y^i#x0U{^1);5<Bd=@OIGc>@zS{%1foXl28Q@zc^}s&SOQbb7cFgz>{2abxY1 z1TZ{&5QG1HQ_FE=wYoJF#FESLbY3u78=le>pzS4sKEOpgCvQ{?;4mD(s$^8Ma4wg0 zH;0bkHHt3xf~u8X?>4;awX({+K+%QQAau@dsmq094bv;<;TDhPaqJfXACTB<(G|R} zFHdW%HY|l-{WAwIXyr)%ea`Z`g6sRaROjDrw|AUS;RsXrh{)s#z~BUYNy|?XdiUM# zC6;pq)Q&e98HVqfICzjsyAWU=^rryZc-~LQvQed9ax#0(>RKscd2k!Kr3|;RhXlX{ zt>q_3#gJ#v4?LDMfxi}V0fdKR?w)7_>ghiAB@B|;T39S|Ax-pi<7N_rK{Aii&mm1N zDoFDrrt&JdX&m^=-HwEN?rL}B%Gb@l+U4mg2&SkdVvus5nG!VxhE(axFYrB78vs)n zJ&(n}mS&0{4bG!53<<axBZ(M)Pss!!_b+lVGQ$_=eBlm(OyzqsZtjv8sfSy|6!Fgz zJ=JgA&BJkKvZBB9ZGTg^|EBg)Ny!1F=exMW2!5Yh22uNtMm*l4WbHpeenMqjF8He? z2Bp9Ie8Hfm(m*Qxolw^2hYtbO9Mshu19uVwUva4>5A~<n`f3ahHl8I@8^Jj`VFemy z*FY8=9Sw$=+Ee<=jZA}9i5py&>?JSb<f7sOL2a+4cp`;nQ{Vr!41aTiuvp%PXyYu& z>w#6xej2+i(k)ULF%zs(#6Rcu!>`^0&l2#Da28@p&^wEQ)-g#!mcd`BQw6J>WT9xQ z4M$Ltm%UnYXmMXGRU0iM?hKmygwv$QJ<4LSGi20)HKjBQ-#r!#wiGgy^O2dgA;V7c zl?yzW%^BhFSG&{SJ!nC-%oxF<h<){kH-a$lOP<wb*#=6HM*rzg^N*@fs7#ui2+V%u z_tHJ3$eRMPBJQg>7yh|?v`sdt2tdkPo`Kp@Ndb&wLz6%JVn>4LIz}Q8F_UR<^H!4M z|8)~cs+q>nv;eImYf7?OL7*a#HIWySZU8E!cAGSG?Kxi8^}jSB{vXn{ZvQH{yy!%? zV%eI5&PKP2Rmj>MUUE(uliTcNQJDW=XzqGwEbXqM;)H1!`1<Q01-s#Ttm%z6S?s0- zN;)1-CE{$Z;`WdV=bVu;i(^uW{&B{h@bLang8TBc>V^o1f37As?Eh+sJe!}luB|#` zB3Qo<<A3i#@wPbNcWgp460;os4IyMfeIOpd8KHtKg|`pnY~3XC^A$Kc_xFG6Dg42c zzC-rKUAhWCne7=TqB~UnDhHD-Rjqdm3!uL{e>Er~F)1c1Qs2>F5e*Os3ouWm3opyw zfhU${G-Klc?;{9y5u5Oi#l0nZ7@6=@EhrNAkhS_@9Q860U#*HF!Yd!Nf2~ODE00kk zo9`1T^IDJqLY`CcO9<#q2la=<SRg5&v<jF5ly4S8b4c|u6$ED@BuImW2K@j+kO1$# zj0$N=Cfq~T+LB3dRz53SqL(gh=&F&C*4gq&#D*|A3aK3YKEXh0+)aOx8utq+6J<ae z%;Eht2&Gnq2m*TkX<p8CV?QtuH7SYGGKf<yWrS}-N!(4rF}*yx>4Y+QCyUi4ONzc2 z;Hh34=MU?egh?phv>n({o6BvqZ?~Z;yOy2N-jU6_MyFlN=CyaQGc*}ewr#r|wMQEx zUTp_p$y&;C2G<zIPGhZT;iQ6mNUoBoa3iKTLE6$L8;T1vav94hbXTb?-m(=aEINVm z+JM5MtqGPAnG4puM)S9C2Lf>sTCPI{Qf8P6sa=bvw=Q*XxI_@fqb!|7%XDyBm!j#~ za_;ikA6~s<8;}jtlF5fDktGByD&RKrq)X(*Y_T=oQi=>ZcZVEjLzgxQ(!7}DnpjTi zik37MJIn~j;$-RHe!y`62*kx%7-@<ZJQpRrPg7>F<hpLQGP<mZpB7h*X2)xV^LEKk zenobO&qk}`-ryIwsi-M}t19!X@&2UIpWRIdXs})Rc)mjn;cz&$SC-6j+TLeZIw3Z> zo}OHyw(KCFEV#@h(K}P34xsn%qIdEQZIGb8XrjO6!U0oUl6HFCSZ@_vc$UVXQI=K) z<MLBOk;}_!gofAe9*g8cgq(C05#Dc$E;ni=WiSgR>`*5bCDZH4Y{;p!_t{S;^_u*H zv%s?@Tl$qMSTjj~L__S7r}OP%2*Tsk-dQp;XWgwWPm4ZHM1E;VeWOijpPZxvO~~r* z4o|xK3qstVj{4vyjTKyUj;x$a|I_Io=;tzTjC6S01)K{2B_LsV51ZEp>(0G;nw5kJ z(l4jQgyWLsBw|NzwwLe&<8{<S1h1%(*EMbP{m}x0Q<Z<6mhjU*Nso?%JlxUa6(6MU z4EPcFg?XNhhdH25YL{-XTE`j<DwJ!i@~PnkSe|Y=2w;mo2prn<%-_tb@u@O$3G}um z0yC7g>~$aq#<OOmzr?4<g9m|4PXjE<XhA0>s2t2Aqsh`fmKskWaLzM-{t?KAGkDsc zepKyd69{-Ut52!{r%h8Eov`rs?{9v#>Cge7ipzsF^E7K7D;XD&Fyxe|g~c;Rg7EOS z8@5=0L^Y3#=G6kVcQ;tqGnp#8WbNz^H9+0P8Edu`uy*dE3|n(}1X|n1q`@_ob#LA9 zVdE$RP%Z!1P*?%{#aZ^=<y)h!<;WG+qPBjwO%DQ~TCF8<oB19RP^K<kXc;+Q2rAg} zUoDsRmTHd|QfVoPpMIGCtDLO>3(t?VEEHFO5)XbeoKVATuv&mE8@`?937jg{vf~(5 zaBQfS|K|XO0Q%fmE32+%VH?|M1YVAHn6j&L0lCC*Vry%GJM(Lpb`_Jk-n!v#Z4FQh zAKd|Xu@q9Z)`sIJ8MS`SZDJN9s`z!(l)m1$1xofZ*~xa3S2DE4SOp#QR~Zp2-t2_i z+(7Ov{Bxb5c=6D8QqBRmP9l}5K8pJzRQJ}yxAN(l&bW_Mvi>f}S_kD$zlB9`*aY%N z5}-<vYSn2Jwub&0{Y%>slDPEY(!`J*X${)Y1p{CR3_jKXyF`uRad-~fF6m{yh2>5c zG|Q$>>`&KE=)6xFG~wwZr;+S`RXDtpl?xOd{m`URXYg2@Y>Kv2J$e}!WCDYiud6<g zQnEQLUWQtA&RlY`2CXSMdCrVo)osb`r&sug#-iB*$K0pUrqJ+Hl7j;W-`Sg)6VQHe z00A~pA5oB|mfMPqpvoNIB6-T@<0y?y5y&<gI%xEc&{6A%^@~47hCzwdJI6e2c@wGs zJp5D*aE!FxKgg7zNf>&UfV}$)Un&yw|K*WlSq5iTGR*L6tfaTBtXDJjK5yFc?D<_} z8)wOc&Me(4sLs}=#p;WV`I=wbzgtnR_sn??_UJQ#uK(F~jvcY(5%}y();KV=c*6mX zGG+JucY=h~TEdk>l!`kDeEzwL5AU#0lvH@f4wh5)wrHrq$!UAKtt3q2Lz<6EPEIai zQzeR50xuQMhMZWOcj}&@n7xTIIL7w}Tsjl-k7GN%mW_?xX@-6KO%rR1am6HmMBv>V zv?gPbxxYE>q?aOb=N4Qh_&vit0>a}!(7P#U9XWCrJP<no6WZR)HjG7#IZW?4^7j?& z1Hs?he+<d4IIZh|z{gYGNXhG=civr11{K}z4F||Zpb2OLN?P7bLK`aCrWI7^?`M5O z{TuijqptAGFL`lS*VhU(C+D17=B%P`?Q<{L54Tvfu6Ac5-I12*ptXoOJWpDyBcmX< z7wKM715|T4JY91uP|`A&h&EKQjno-`RYm@dj9P#uZ5qSv_jJlPn>j!rsqCt%qrAFT zRT}vA0}g3~T+ZQPghl~e8(Bi|xnudl>2Gm*!E=W$_Y%4D9M3$-H(z|aGBD4BGslIy z-Os&|ltk8$nYX-eF-c@?C12R}`u2-&BzI%MxXfighv&}mgQ26ilBw4<MP*B}=znm_ zKKTY1>AJT$Gjnss59BBj1n*q}*Ul0%iKL9m%@wc6de)heaM94;`iLPilSwB)pmglG ztOH;ffSQl1FN3e+mcgNqS2y$BdI}kaLYM%D8{!=n+Eq5o4*K|v$Qg{b?4*k4N3x?+ z(#2Pl$ql6}ORmnv)E4CE$x0%1zoYX=Vp)NZ<SQrKX@)gqzRnh#5R)zH;qgbv&X^hG z-bt7LzN*eYtFp>nd~cEEKT8C&h{zWS=#DN|er0;l-JQea#QRH6G0eB!+Y||$#ZvNf z{vi7|*gQ4%Dlp}9Q?ilvgc+I~_M-ps?jClOUAu$FElmmX3TCORn2y>^9KQabHH~b3 zNdZ9!#C_vH+`-cy?`b)PLk^OlaT=@ujA=vLHP(M6zXFuY6lfEf_APuugeds4N0S_q zb|Mk~)m?lGkGW@Nx=afrWs_Fnjhn*62`$2%M2Z+&UULt0(P^w9#<@xsomvkP=qws} z6N>~y)Hu(DRruQeWp%&_v2@UF2p|Dr73)~WfzVB48jDVtyodl9fR;q>7!rkoIJl6( z9sF3N1bX8^P*~I+sCPG`E8ceS?I4H&bud#Y`goAb>E)ceKqyMp)VXPvnS)y^2^Y?_ ztAKary=Bi$J)HCNp=@5-Tr!d2gd1mb`fhST3}dG9PHPCdT7BvlljBJMd*~j9kgLtj zNIv_6(1$0u*G=h*WTAgsS1?ynB^Gs#>>o|Haj|0u1&uca)x^M=F48CLg3{GS<PTCv z68r1<D?bM598PNom9(EmDXd<b73w}fx*PnjxpHeIoS-BfW|@*oQ<->ONbXNNw~PJ6 ziTQR!^KK}f7ntvxe=|_Q)%_Egor<T1o4N-`j4g2HE7B*Zp#I*z&=kS~*l!Nf9Z_GO zn7+6UUztt~PmB5340TYEQ#3MmH9EiClpa>LJqmv{EIcjxW1P20)9L<~e-#Y##eNHs zQCdk=F&)icKr)@&R~UK6m7n8IRyR|5L20#4hg*MLURbh!a*{067IcN}hO46CLq8Gq zua0S=iTr}Fd7Yc0gl77d=FT`orf`*>Tku6Xy+?mcepsU4%7U2@{E8#SA}Gy^-bFz# zBdRA{IwTl6xU$L7Cb$$7d?^D3#+3($XjG7f*C_R>L1|B25Bs`@=XC&JQhGdWiVSNj z*U@Zb03yI=@qt6FyynbFCjmq!m!jw`a#>4>BG<T<`#svh8PDXb_i8x2aN`-6lx6V= zs7C-<hemn6y=i_9Syj=ef*0KSLWL)q1C+N4X6Yo=>g6#jVk<)y>C|p)ld)<KQq3iu z9VNgVPp@HhSgfZxA7$qus|0k$)D{Y}ZU>f`Ih8|h&*f)4oGRhIRRGdR_DTDu4E4c` z%Y8@qj$&8z5eUx+p>c>OXcOEi;a1@mLnRRx-NXffERcjGDtUp&bXnH@?9u(XgRTsr zF}+2HLAYhCr*fxg6DN=&^bQ}QT8O4HX>MtV`C?g%i0`NdOFE{=y`y9e__5n;^4s@n zig}C>8aba^m1RhdT`p2A%x8Ffo@L-x<-^5d0Z4P#)ZBYp4faF27E}f@MI{%YCLt2d zKa=K81E_N&{4wGHX!LsM-Id)NSAI0g{jIwRn+1*mje53`Ra?r{UJMfFGt)Iuy7(em z_c35@Memne);H<QzA~vrY}kl6HVZtirEVW|E7>0`B4CD@4tq*7v*q*wU$e)6MjeM_ zCcz^5(IQ+Ki>t+u8undwU={&pyDJ)%s%ZBpDzz19Imw+d*wW$Aokvtu5}RvAAXgC! z3bbI@Y~nci+H{WDG@QuhTFiiD_#%tf2AC}tGmw2u@L$eoAgb{i0p@x7><WvB7ugF? zZng%{_;eRCdFG4J#Nmo$r|<|&l+`}mw-IdwTG!>HNdU3~vtYtxiGqYBTEf0;?swTd zu34Ftm3ShNUcn%b(+_ZY?9}sGnN&yj@9Gy0{}MhW&=QQ#iieT)5QUox8su>Y;e=QF z-@Ck%$_?F>MEZHJU2ui$?L}s8IDB}7@rP%n!*?HD?{kZzCD01;#3*?=q1EFtlOT~0 zs3TR$UmR?fN@4QqOsg@znOY`<B~k(?VUQj1_Yx?5`zF^O%4`ZXAWlM8Lh+gdo5?+J z*L}@rB#|k}%Op;XSz4D8vJv`is=DgRmCEs&YWS|qn72%uV=}H?8}f+>{Qr&WfL5^w zVxvq~SVRYgJ*+1SU*e#hslnBqX)%-4)m(GK0;YUKH%Q1;pR(}y{deDepK8((xSdyZ zefu?@szb$imdvVY1amU;9wny22ks+Bj*LwlJUlUc_^q+YgNKYz*gv7Fdu8#OV88kS zvSz3LEe(=JM}7ouqlrWgU-j(S=qS>a0w<V!*L*()jE*w}n^upt!RXizg4HI<!ybzq ztUFj2DAP+a@suh|EFIQ}kQKUqs|rxfFa(=Q62FzTkb{>@QqE$<jpk?OeS`exQwL!_ z2|(s_FQ5Cc5hB~?#j^|*nIzS|eCZ`85fz+QFI7$Zon@a@lsWtQblM0qe~9(9^e>q| zQMAuvIOIoy*D>78Ofi{ETmw_a!RZ2!m+Ird132P4L?}H|Wx)Pv)5YD%Ooyt^!Z-l$ z<M7UWvv8aOuOX6)$ul!JuVaKf9vrpnpU=UvPXVy(Gw72+E=i}%ypSAp_uA4-r9;Vt zdF{MCY)Av`FInjHND0oqSc(^HA`r_y1HjL}08f52m^wY3I(U+7jc|FzKL4iinPQSV zR}tUI-s??9*sO{w`UB{j6!N)St8U<NG$vmlqOUTIRxZFbGN#6G+(XscT>ivWM>$Dd zt5A3fnHT(#JK4Q0;x(|(3kqs=`xD?}r7x-{v;?@Qj>ce6KTP^fKeA`}t+*G5J$mYH zxi*+_BnE@Vc+#@@sYiuQU$KW%leOyg6IlCrh$^9x<#}6lx^({$>v6k=df9tb-&CEP zOanX~jqFk>6X6SR9+1ODyuizGUzozzGigI%+t=c_cy`|Jc307a-42AW-9wn=&jIDL z03so%$q7_mz#F`vmEl9MPkH6sNggR&i2erUVGr~PaC^6!&%twX+t-GXd<?Rwev6mt zkpNivT?Kpd8M$dm%nmop&sm3f(x%0jd<caO(GU(`1TsGWd+~xzam0y(9l4qQM(aCk zTUR9yQ_^B{Vkx$yjT_dtesy2)bTcNUn6=V@7x;`#OepiD@6xZ<>>eo(9n>k;a}Ddb z2EV2E*6f}c=lU<JeUKoN%vCrf5m7TBZIP3R@>RZmDn3ed7C~BuTKfC%4oKLYOB1yp z31-1aVzlu>MgqL!TahvKeLC%v1`n+vdu^p{DAb%mfHL$Qm%CxjkFNJM26t64L#?kK zefE(hI>8pxBNb9?aY;UlLqE_AUkY&YxK95|J!uJ3i#~Bl6r1qW2XP6xCI)Qvv00Lj zKU?wX`t3j0rC^mVS7o%nt;k#C`S;zsRmow=YfSOJjmWXj((&lUm*JMG_5C%a(J0$1 zy}zbveYoZ2ag=>rdVy5EZotL5EYIST`C0PItaDpd+&I=LZ&GY$%Rn$<>3cJ4r@|b2 z*LA!b0fI;VsNKA-?SR3tYXG^pB;w@O^ym_XJK3<LINgw8Qc+Uzyb@b9xCWHU2CHyV zf}zZ1*OX7{nB}rg9I^jaGsL2iPs<$-GpkI-#LD`=;FTB81jZ#NoDlGHqtmeLp^Vjl zdo=qA+h>$1NMx^TAw;D}DRGb&FuVZ*&t}$AoSq1FjU|Gp(QG^jv_-6l(az?xySh;j zMOBhA$#(>54EheZET%E^;<4=QnPG$EbCcjV%PN(?#3Wd$tWqNBYY1f#JQ@W@j6%8J zT?}l2snKXutbuj%;zXq2B}}n!wAz+F=1q+_F%}mpTf|eN**dKgp+vFM78!VGrFXLs zi$!32dl?0*4bgyOP;W2&g`r9C3*};9A{J!TpUmR#Dpj4^z!u*=E=Vr^HsZn+KP~<q zrZK6UKHrjW;Dj%!AdST^_#o$s9%c5y+v5j4C{;=$e1UpLi1V)sTUGoGtZTe4WxIeN zxDYM3=<+Y6<7+v)(6*q|$JSO%SS~hY{+>l~^1~V%1HV5Jo|61hI2L5N2vV`Hh_{S| zK&q4;m4frx+EO}JO2IX}3Yzmqi%mdq+f%;!f{ARJv=#yrI_s%m({3hi?F6Iu2JRc& zo)%FJC}6pG><Lv!h}uv68nkpbm*-+}D!|kVkdsJp(p64Bk)KnwZoPCjN+hDYr8`Qc zn(*sW6ezdvY{K_(h}4t((AE&ADyX8yT~*#syF|ijAjKQV(veakVg@R2LGD(p;9gCM zfZM`jF*-{Uq19D`>^MRJ-@8JEs0as@l%i1=N(E>ng-d+RA#0hT2b>L5c-rWfTH`nX zSiq1)G8f^^{d}?%FB#s}+6VscUs2X)rb8RMWG~?pJx~sPPJ-|HXZ)fU`pok*=TJJ( zknAhgCs6|UgLebgAMP^RG<0r0l3vW7Jxst0!#g1(0%;II2Y@*OQae;f+IG2)En|6j zCq{@+et41StN@wqS)0!>TgU3~&LJie@=z(UafTQT=>h+Fp=Q*DDMy-Fi7aeX%6VsM z|9$=WCm+54+RMZ3<+NGmvL9qS>Y@U$-aEW=Za}pUPnWpY?s<rdw{wgVO3)ijZ5vln zmmZ-Cw}J>IwgC7R)v;}Vz2Ry3)J7W)Mcig#&;@!<UaN;RheH{26rdGz5+o;?lTp>V z`i0uP-lVR*%ktV^|9<o7;c_bEWW$8+sl%ykZxcf4B)oWpS((e|K^b^&r1Ani_6VR~ zr9?->c%-Fc;z}Z*jGRd8yv!!91@Ha2zXh*({rWua3n|)=I=oxOY?H1*Oi!bxQ+qg_ zXNy?c1g2s;2z}Z{<j=Xqtr$yOA46p7vJTlc0zMU!6(i6=8@q>h*hfRKHHtLj-^Dl2 z$N&J~&~HwpbY3DuwmtFyfQn5oF`)|Q0N@=8eLW%e%t10W)*na%7r|Ic3gbnoKfEX^ zeGJH{6NN8#g0YO?G>;Yl0P8@~Xv2r>RC~6dl%Wp*kw3zl8VMOMIppoIs?n4oQU^L( za@aSmm>^ry0DnJdrFrBbwVtDj4^6BO%MJ14M}5mM&85N3w=pP9=RqW<DT)FTS^!+! zj!^+2+{N1P`QOf)uP6Y3REPE`slOp`h|eMIC(K6H_9(=wpDVYT945q`o~c0_PY$a& zj8TdCK`FH%g-7xpni{nSpq2z;j)U)}l*(tZyG5iMIkRo-8Qxh0)(8%1D!IJC+_p=m zhX}%-MHxd@iL;%eG*UZ+8%dG|_znY)I)o<V{V_~&S4?}Q+##roz?(hj>pG)2#IzFo zEC?Z67$R9DPZVk*aU!`R=SgZs^K7-~NAuXXMHZud#yh>Q!tUY0NqFeN`=Dt+Kw#2G zB1|AAQwn~BhgeKZ+mFLY<1F9Z!euRvF9lB)*Ytw8!(I7Np+YyYHatiH`GOrIA7C=l z{vZVr`o&O(8$(90;0GQVv6u3rZzv=6#7(!+dAAWPKUIY|v>rzgI0ff*K1AiKlxoyd zEj9kT$ElfkseOL8Gq08t%vNb|^d2uB6c`&!Ach_yoS_0f&ZCulo}?4o9%e<OdoJz5 zJFy)9TFQO<md;d*rtyH3cwsd7uhF?13uI^WOeNuLto9ggrg1_4w3dRT0;M7gnL$;4 zTux1Wh<Cnh8bDl*Cpu^d{*{ogUN`-)#RaTtwbs@kPysg$j}GQbTgiQt@d(Vk!+qrK zPI;ach^_T+zx#NDQ&wN4RHzD;H5mUN1HhAV(7e<{l2F7ih)^ky<^7OXTr0l+dU<+v z+HcV`I(JQx1;DGr@KK){(I!kK8K7`bel9oh$R*Q6OE)EjOFZPx)jR5!4;P~rU*&YG z6@}IJoecf;;}3T#OA2WSB#kulnP%uDK-@@D<rH$n7IZJ34t?7wBOutWs~pn3uCe7^ zF~xfHvY=Z90!?dir8+0Rz1};{%WYc6h-o}kAcCs6;I|88M!ga6*Ai?P7)$>#ARGTN zcmkC`D)hKgoqa6;cjz1eO*kvcxlAc#xDjO*pNSYnD=we!8o`;s2P7E!qd^KcT+tQt zO+5h5DSPTks7k8J?hy)enFXoZ6tm$*kxs{ISne!QI&8G`I;84BL+Y6_yE7s6hRkC> z)&}a@{-^DuB8yx2EL;e9Do%gC+s>wMzVZ6)GWLRf8?Z*5{0uU7nY`<-n3D>Vs7n>@ zJcXU+xp;nNLg2d~<LJyd^xPGd)S0r#(<H`-OD?Dp`!!lV9X#)5(u=ALrgtWp&~DW+ zfsZ5w66jqa&2?6Ae*ysVvyIcUJp5ZlBEoNWkMjD0DovdfcxhLva__S0RyrNWM(B1+ z9zU25dbjK4bUe(XXDBjTitY^*W++Kv<p^|qC&r&b#XF8GX%XtU$-tp5Jg+)r!%*q{ zVNvDPgl$P&Fn7kFDp}G)rOWM}(d+dmV0c~*fw`XKO__A|tRGJ?)H_J`9V@YBnbui) zOUys7w#Smf0o2w(xu<|PDFQ5<#pg*Zkb8S);5DjA>Eom_V#JoZqr7Nq`#qLlKf-c8 zFO;hyv>E@_-#nuxSB+S}z$TewKZ5iV;}Mi%L9)WvF22+A-R)6#9smGL=0Lq|^7tW5 z$cOm$^Yxe0VKlg$1dCA=*ag>hy~J4jXu~}4pT9|F4)zR?W02=X)|vdgrULRLeFP~@ zVhi?9H%su4hxcaFH%@cga7yEitR$wEsUmSkJn|mT3B7RKq69F(Nfv$oU%#47`c|+A z829=Z!|vhpzu7m>$PJ@Y;zoG!)hN{?8vuI!*kL}nI3YgZX8SXTidiTP@II#=6OC53 z!BG`#p6yhkTkB53-2RQ6u%0Zy5g=tw!^ylc$daAUjgbhcT_@JZ@i1RLbhS;%<{}{- z)K$7L*WAjy5;rmP=Dgo-)~kg!A^V?{yqUsZ)E{s^a`&nF#!bN{<p~NWCGS*qMlV3L zwJEw=(ogO{TQB6HHcZvJ-P1OpGqFNt=(OUL)OTXJ5E_1_O^F-LnRB=&#3*$$rx79o zEy##3;AH1XHu^50=ZBlE4)psot`h(nX-D`Dt5`zp&pQlB7P3~M%g+<}MaUGPL_edK zq{sz=kdfyyy*pUJo=Tye#vYvHU21=TcPMRr;xc$_qf_ESSV;+=4>{PQ%xmTT)?y!j z3s600Jt^W?Uabff68kXMuS|nrRe>YumxRl@l$Y}@1urg#j)O%wZH)|6qN3tKIG9gD zc-uXj4U}i6mPCh~kcr{MUR`EM6!^m&STk#>?i_a*W2b~Wx_?(0K!Wf;pn$Tq*yZxH zpdDF(ujXmL`Rrq6w(1Z^cW9l)?&K=rv`)t(-lk#L-NBOBB_?ff$24`sF^v{DzL581 ze*ds+Ew`_aL#r8y5pb%EQ}$F2IgS;mqjR+2b9OOwgHxe1+y?{~@c|J&Dp%U4e$8d* z#T!CgJlD6Z&J*bWx#zf!pYdPXWu&^X5-J5L{-9z4o04^pG;uC~Td}(6B=V|xzU9`( zk4(r~(5bdT0)6DY4R@Ln_Zj#g6PG+SEeSd{JAei*CY?{@qJVy3KQhamM1vE1${X=k ztG?|~Bd=?U#bUXNA`u^!ZOi!%KOyI%cfb7n-S0p8{*Bk}ZkLN;?>I}|+os@|uauai zVK_wBs=1!hT-8Dfk`1<zIq4QBGF-oG{^SU^*fl-R(j-Pv7(l1O4Rd2*4m~weO?w`^ zNO_9rX_A6D93dSK!dp!_u!+*TL*Um3n}Zz#fERKHvN#~Od9U+;xF7$c(p-cQL9@6u z#MP;fh0H&aFz;iS{Yt1o=cFRTk$tW{*Rk!_$4|w4vhpf7Z_ZbWMLcIPK-WAv;14Qm z@;R$-M^{CH?Ma4P*DmM7)OQkpvS&($B4Ae~C)_r-+X!ol@}|}xFUHg@wPs<w^1ldR z%_PGyv6J+4@MnZ{x0+Ttqca-YG2XMgJHi&DVM#tI!(|nqWSNKdXyWag<6*zsu2;*& zd_25f&Zpz&9wnf01iTz_0M2S8&x;8T94je3p;|`u;qY+yglLJY7pn&3U6KmDNdSRg zWn9y>N12`0hASXIvNG9la67?HjooIK&w8hCkSaKRWL0^mSZPBk=v3OWC+EXjRoTw* z5zXIrr)v%8PdAFwvdFX4E=M8oaPEjXQsdHqCv$-yvwCKULD6J*_)Y<+vTHl3Wj&Gs z@kCfLJN_s}=@la#5rPd7sY^;}aRdPSgT_~Pfv7^4U}p>N@X3HCN2E58q}SxI2D8@4 z<sNeO!RS<iXw%Bb2sq67Hu31*uzx)Vlidjpp|RD|jOl3Q)x5eg649^^^5Jlqu+PC| zd6AUjiZ$6@%#|aZN}GB6vjg;Nb{Z*?@P7R|g2uCY)tV?n*AwlQ-C)ir=J${2t>Mm% z^RZKcRb`QRHSU_buC+T?$(D-s7!aW7Ho{}d=3^@4i4L*mQ3khaL6PR|a#~qpy;M29 zwz?c#Bl3PX@#eB!FHrF|%04%)46)qafO?x|C+VxM->Vaf=V=kx7mV1J{X!_#r4hAK zAqaJM0`#_?kJAux3}Ny@ntLajy=9T>mn{@F$77Ci-A+vm04o6dNA#w)A`F&h=Pm#y z%6T=(l926(DFuDZ%sZ7Xo>w796mrb=v3HQ@I2zpPkLa}&>;Zy}8AnYE+~>nCAHE;; zL~{QKgPh;s7T8Tm2!`s8U_j;sT*K$6nQlT{j;8k7@$AujR_+8tkzKIqzF4iFG|m;O zi1e@=sHL8=JyAyDtc=&B1}(HuT8s#=OCe~mJY%Z1i5XUzZu<Jeb-fz)&&M)PaIk@x zel?-gNo^+9*PuLy#m|!-V1i9?It6)sq9pn3Ck$MG1-4-U?T#+Fwp5lBVt}x0ZXaU$ z5qO^z_;M$ADB~g*;1HE>q3jc;U46<2u+hMT7T^bNEV1oGm)7W6kS%pTz8T?^YGcC4 zlk()3WW)s(I|^G$q3+V$cjKxA#HfvTzkfyQAKll4@Epr2Tk089r5coeRkzZDR0GzA zinlzc<x~XKi&FjG{@kgq#s|K?1-Sis`*?o`-tzYC*+ayuIqFA7d;+G&48f37Jk7Jh z*&~L)7D@24jY~?D0J@fOe%BR?5d4wQ#cBF~Hnl;IRD0GFKfXOImn}**y=P%vV>DC( zxcJ4c)o679mO<&i32h5qSyi^P0=0PodETF4?uh_vraYA%Fp*l;t(`nZLl7YmsDN}> zy@FBUM;vr_<?X_dgP{{v@J*xIN(lI)XOH?3zV(6Uu+yf*8@+nS*+hU_l5lg|=KKvz z?XX-O$GqwQu({49vY`$Q?jdS$SaU|g(9&?;R`f;dCBPX4D%gF|JG=O|a^1&wJ|S<l zeDn32%XYIou8#9rPD#c2MUc#5lP;1uhTxpi8Ro8k&^HH~zpm@U$IIMVu9vR}CP*GQ zVM*e;lxv+Osv$hi6z%x>*K#W2$VuKZQaW(o;I=jEi%}`+q_O}L7cS$3iCaEM;U0xT z1Fqsm%KF;NoYwpeE}!d~s=%RJ<ECzk9?=q5+AIM=5tSTe5YNd~$}{R3-{AsMEbRML zHFb&f!olFX-J^L$^(hf=-xUmBk2WFW>EUu-&e23oXoKi0>^%`BBso>-#0hJF-ZxxL z-FhmhS30XRQ@ZyVz59i+zB4h!iR^m1?zYEHOEUQo3cSHXCv~zp^T|zo6_6n>{kJjm ztsismw{^h5ka|3m(ek@)?ZBs;w6i<tTVOK;-@}*6f+skP!3ds9fXPWf)ebX7s#4XH zMCt_0+RE!-;aS#}p{!fXVBj}I7UoxWP`P2EiQ5&{B|m$K(_9(ZdS6~g!p&jv|BY7t zfA<ofPXA-1TkGBTB?q}MJmQ&4=3Ad?qey+~1&N4O>!h5_(w!TeerZ{MkhCSAG<m)6 zB4eg-b!m>}l0J0GGlz_}lseobL<xx3TXELpOIpzm<GAEjR<SaQ{}Nq`Xq~5Q8RKgZ z{D;&AJJ6kE;UX4<3q0MzcnoEKQ5GQKKQeemho{`V-nUA`XqWItZ|^>=cdoO9rTBjl zmV-@F)KaP_b1&6ao`TK_;MjDN>O-rl)vEpf=hB})|Ma5|o*oYS^&;wYno|@8PFEs_ zAXUBSEw{2=CnD-buRvxVDE>=DZPHiuU7xP|*5u}Mht<THd;Oskto=1ycg9W=K{IBn zJUlTot{FOJ$&{_oELv}28#9)f2QmON>zUTdlwii4<Ej}v>hz){H2}jf14p4OC3^F* z0Oi#lPIxX|VlQ6x%9WKL!2|Jg-n|~CUgF_Ids3pYHdS+mG;P!5#1yHHL6*Bb?82}i zW(-F2ewjQOPTHeEH8Q2fBPk8TL|`YG`c680QlLmzH#5SgXq?24<AQ;+J-SWpMrza= zmMA4`s$KMRa%JFp>Zv9XSkdOH%x9QMUHW4+0xS#?OwXDz&HP3xx<>t3Z>V8=0=msD zJFXqpmBV!+C{o|i!(gL#gv%E{6t8A!u<#@El;<=L7P_+I!R_wcOR}NSY(+uUFUfIG zU&A!{(DqgodyVbAVd<GXj}PO_Q>96xwJ7K3G&OeZ7c7zt+8lOY4i>5-d1SIU#_@{y z>8`Mviwd3#Q?Q!zF*IxWvc`VZ(dVANbi%<LM$G1}oh~Dnl`%6b{_4|rYM2CLW6G3H z_!b3d_Dwa`Ea3;YWlmK=!)D70s|^$vRy3}eWF_BCT6G?@F+_~Zk_qqA;HNJY_s0F* zQgGh`z{jU<F6d|fB~8Pf`W;{eLlSE-p$T4VOI76RR?GEYq%^^6&<Yxd0QmMmXUKIv z-uVEH6VT)x+N?q;SWDE0R3(VH*7l4>cR|AzfOmrowb5v*9G8Se4RAi_9?B|8P~%z_ zqYX0^_(qLS@~<`H<G-zYy+wz}>Ko`dLfb*oFOlPO#eh#WC7L3NNzrk4tx51p8?3KD zSoR<QSDHe$Ka`K%vWTeAT?K1^S0h;sJ+;VjwznC;oQm-^uBi1{UP{0>QM=CJ8Aka4 zuNW~;wIJ};-Tb&d3O|G!kBV_i9tJKqg?ugQk~4|+P!pDD57_sjPPof|2`$o9Q2cN| zEISiVkdn~K&(azMWrU0tGfSuH-(?j0YCwOX&9YMKMukFwmSDebt$aX~-o(4*MU)zn z1n|B1=L^-}fP8s;)Opg86QG$W+8iJm^BB<iG(2{4T7+(D!vOXx2RXpMpLrWW+-K=- zUG&#jO7yRTSITke`7!NtH2JQig<J#W8faMD=KA&@B63E^mv9%LTZsd13@&}yi<Bm> z<nhO2jw9B><DRBUt+b`9MAi#OYSiD6Xzm={@OwPi8zqiZ2!}f0%sZ+>lN-hdCAwPU z429Z!A+N}I9>#)vk2I_{jP&XO^yvT0msr@*ZaM$6UU8+ySV7E451{1vZY3H`6KL1n znpWDHA0=(6#!Utbc&Po*K9+jR!d;J{oK_s?tkf&cJ-Z>iJ<KXFIsmxRDY$raMD*7v zax5Rtiw=&>o5IQYFiY`#p_lXX4c*)vZ;8!!OU;(~9wzkJcwdg0eu3lTg|u{EfA#ZX z$V2m{&D-X~X3xwQX8dHnVIshIi=XdydEDlE*wCyR|5WncDD?afH;OCSSc1u-drT{& zc}t@9a!U_f!(>~Vdgv&EW@<HYgfH^<Zx0SyrUUbHECrT8jvt2^G(ZHb?GEb8Hz!bG zaJ-V1xZSI}OjjBC0cML%OAt(k{aZdMZF7EYSFU@oV{brJlhYh`%u2(pfyNMeWNVzv z=IHFWGu!IU-&*gnd!#@2r`D(W8u@w8a*D37IPwiT>18xWiUPLH^LGT!AUS>g{DR)H z2Nlsj%=&e6ihdAJOWg=9Du@m$a;3b{U}D&>ow_Wi(Tdn~S99dkGL*Pn=2n|>@4ja^ z$^IbQqCNMhlfX>p4d8iA4UZ-L`s}~|vrFyTLRXx-$?w&iwj~gLiN(5uG&H?b+rJco z`y)!L+!;M&aCa?Rx?QX4K5&I2|8fnhU>c&7F9o>F|09!<!h^CFW#Ts2>(B01IrOQt z5Pg&eS~WbfQbtud8C^gft~ESL+@#=&jqP@-`&rP3>z~vrB9g0E$thQ~^SLfaUVucB zBw=XmPexx2dO!R26AP&ht}|hI$HfiL-A3S*;VK!q!*H)E60(N#_mJ6ekgO16*RXCq zhBj9@*Ko{AETF9D{Hwoy-JH^VegPL>R&CINCfX`+>=nT8+ueVPJvI(59zH<Tbi=f4 z$MyVJJdsQVVLFq|<qO49xl*mwA94Sct#+py_4<S1XgrzD5W<vkA(b}P`4GiPn&m}V z)lJ*6os*lFU%>cQGxasrgNFNkq&M2t>888Y(Pk~M-eV>%fsO8UzdJpYuwW}iDyd~4 zLm9~e?gT)APz1q^9qVMLI^KzTo9~cVB9+M%N|hQx#TQD;$jZqpC@LwdsH&-JXliNe z=!*2(3>p|38TXrssTl+S_K*gJKw)qMvSZK~Y+t|=+L(kyrch~g29w3+aCv-zP$ZT} zWpag5rPgS5dV|qqwpeX;htuWuczu4o!DupDtTwyD>2iC#KEGtsqEQ9{a7H}29P#TJ z*@CJ<<Vz$DL{FgJd3gDwFE<+bq=E{uZ^NdFuD#ww`g*hJx~5464`2)k6h4cyNdpJ% zH9v*W-~Gb^*>SA|JSgoTnLj?E#G3W!9ovq7jF}q&g&1N2EKB!|2J~aWx@#o8{aoNi zm?i|AHx>pYF?PmlH=0~ACXAdz;S`)jiwl6WN(-3W6@f*vM?FR6#GXf@IgdT&+;MF$ zxtc~8rsjcb<j}GoH;=Gu6DKY-W~@6BYuun0H-KR5BG`>662_2Kec>@y7|h2hzrq!+ z>Behr;Y<WHK_ROXF-~%u!%$b^6X$BGNG}|Zx0le3kjf-xmcgZ_0eFe8W$o#?+wBGi z%RLczj4TV6IcWeT&*<mYE)#3!go<pBxu&jRdYQB(h&vy^am;LEb_9XbxEDFew8h~2 z7xW+6ah7nEm?P&P@U7!#w9Oy+#Aj-Jc-^@-QmOZpv^P+<8*>JOBPf}u%8Qb4T2gR! zcf>Y;SAJ6!-GmM!5xz$jU{^bz+sL^EiV1@ws+9#dqLoaxW+>lmR#<<ui4>4@8n|rF znFYZOxkteXe$Q!~1z_Er00jJx_l{8z5ZP^W;p;@*J2(IU0TP#x1wivM4lv9rz@hY< vqne={E|pijkGz9)10cS>=5*wY>*j{nj?oa1>VFzs?4QA}pM_S}2;~L<Wr@HP literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-BoldItalic.ttf b/docs/katex/fonts/KaTeX_Main-BoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4346f173ce347459e433f45d1fb06cfc74bc8eca GIT binary patch literal 44496 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-BoldItalic.woff b/docs/katex/fonts/KaTeX_Main-BoldItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..d2cfe4e319168499f33686f3b1524addca1a596d GIT binary patch literal 25352 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 b/docs/katex/fonts/KaTeX_Main-BoldItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d0178f421501dbac3424821b480c7e58b1dd9b48 GIT binary patch literal 21944 zcmV)4K+3;&Pew8T0RR9109CjE4gdfE0IpO3099T90RR9100000000000000000000 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 PQtWEZ=5h_SOlTAUVvCDK literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Italic.ttf b/docs/katex/fonts/KaTeX_Main-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f2c3ebaec0e7306f4ea36c98f43e301c0a3308b3 GIT binary patch literal 47640 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 XQglzIQbW0WGIxjiEcVqP&vyPh#XzB! literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Italic.woff b/docs/katex/fonts/KaTeX_Main-Italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..1184295def56183abcff437cd382b3b295a8bac0 GIT binary patch literal 26228 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 X(ymb24FE{wN(KM`00962|Nj6Ffm3-< literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Italic.woff2 b/docs/katex/fonts/KaTeX_Main-Italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..aa05e142c4293315104e02adac9ed65dff454deb GIT binary patch literal 22748 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Regular.ttf b/docs/katex/fonts/KaTeX_Main-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8acb365453b7590425ad0fe65c41a5488d3d64e9 GIT binary patch literal 69520 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%0ARlyh{tY7?GU*2VZ9W&j)?g>A@b9!9`J|#t?B! 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%0Dd|4rLbv|Lm^2UjLSuRddForu%~Gk{yjR?}w? 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Regular.woff b/docs/katex/fonts/KaTeX_Main-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..9f8228fc38b5d404b5573cad5b33f28dbfb47cf7 GIT binary patch literal 38112 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 I0ssF14_>G65dZ)H literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Main-Regular.woff2 b/docs/katex/fonts/KaTeX_Main-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e3f71eb7e9c0568f6144e45c59b3000ed3dda7d4 GIT binary patch literal 32464 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Math-BoldItalic.ttf b/docs/katex/fonts/KaTeX_Math-BoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a645df64e5114034e6596c79103b380dfcbc8061 GIT binary patch literal 39308 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 KZW7Lh*Zl{xmaf16 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Math-BoldItalic.woff b/docs/katex/fonts/KaTeX_Math-BoldItalic.woff new file mode 100644 index 0000000000000000000000000000000000000000..87d4f223eaad873324b3e0d42a9a0cf211929ed6 GIT binary patch literal 22324 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 S)VDhlcw(sl0RRF2{{RnpDA*?e literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 b/docs/katex/fonts/KaTeX_Math-BoldItalic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..83b49962aa8353a7eb7cdea57fe843af4dc06f88 GIT binary patch literal 19720 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 bXssLhNj(c?!93G5;NEIzo9EzaliuzI?ubzb literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Math-Italic.ttf b/docs/katex/fonts/KaTeX_Math-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9c38359cca652bf7fc7e7f9581df11e3267a375a GIT binary patch literal 40992 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 AJpcdz literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Math-Italic.woff b/docs/katex/fonts/KaTeX_Math-Italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..959746ef56fb302f4f8381cd199acb54ab7db0ed GIT binary patch literal 22844 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 Ocw(sl0RRF2{{RmnPtGy` literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Math-Italic.woff2 b/docs/katex/fonts/KaTeX_Math-Italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e3ea522a6a2da7b5bfcde8aa4cc4825593e3f857 GIT binary patch literal 20096 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Bold.ttf b/docs/katex/fonts/KaTeX_SansSerif-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ff108512453a58cdbfa2549249ee5f2562640f08 GIT binary patch literal 33688 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Bold.woff b/docs/katex/fonts/KaTeX_SansSerif-Bold.woff new file mode 100644 index 0000000000000000000000000000000000000000..f0d6ea739b8405ce37eab96c71feb4b0091503f4 GIT binary patch literal 18516 zcmY&<Q<Nr6u<h4&PusR_+qS0dY1_7K+qP}nw%yad^PhX}!;M;dMMhNYj9R%WA1W)t zRbEUC00j6a5-I@r|8!v5|F!=c|NkPdOwR-W@TC3os{Mn$*q(upk+p%{Ki?ey03-td z024W6s`weXI1vB<A`<^%=>CBV1PB4z)XvNr0N~>R0Kg&v0GQM@l+8Oc1IK?{iGMr* z<o^P}%*x&L-&`U907nr3U^Y|zM@G)v#K0H;;QlX$?LVM|I~|+<bN<Wg_Rq%u2NDQ* zkRo$yr++rLz(4-KIH126bT~;?wnqQrxc=jQ|A+m)F;i;;w|{v#&;Hp2{{bihmfyy} z+T`Ce|KkJq0RZ5iH!{DH?Q9*L00420e{&)KgEwtmpO2k`$v>{}*gu}nKSU%%eNMP- zB&mbBZE7Ipq`y9q1X_(JkZv+^x3oI<)#G|n{_adFnzrspJ6RY`+ALmRWZtDcgczOr zCwKFB%-&8*PdUcy(+@V-7}K8*A1A)dkMe$bKmfgB4h99mkGMTZm0H&65>4xj;*sl4 zY`BQrPi2r@2+&oKvAfXzAf~IocKxJcZPp*2(%ddCxN-HQF=6j61cs4)8bQ-=)leep zAY_@^S7HDM3vO^Sj2NN=)=7Iw)B=t2iJ!!3xz6zoxK(>xZPW@1Nn2H}pK1jSgLoL? zsB$5LZm!-eZRq$gO5)}=0j0CS3Jr@2=z@WtyXLg%v!mJYq_j2_jVy&mOI8CQgrxh! zzhddKz+`*xHdY03@?<QM!2M;i5+uwp<5N~K5<wp*Bmtx$0Iq4c=f;*U3_NfBv-};3 zX<9lLkno`rkpgm&3e)DyYtu#hLmx{~{$06Bm1-9}N(BQPPu`JY!E>C#G}P#bUY@MP z9pbn~bf+0Hq#<*wB!#ZKd^$itxiWek*m$202XyUu2si4%1QM|T)|RCx@(?kQ;m~^X zJV87j5$M1l6%`b3&ou%+jIktqRE*zXbS{;U;x-&!`md~!FsV8$?A+bO+^p+3_?UF2 z%;9Y&As}`5ubBfi?V`Pq8$R!RaMLj64^8V9(j{1OIx8Uxs-15Np_l8Hk`EYqMv)5{ zvCp_1np6|6SEi+oy~k+HX#=OHFv5lwskpuOViupJI;Q<e$rKf6>ZWFom#EwE0?P?~ zxDyIKt8|+qAI0}oW~ktdv}EzjJIE_$rihh|%rB=2B_2fWgjl4oCd`5%+GZNN9y^by z?3l{2<R0-9xb33RlEy#&V9XZ3f#aX61?PS+@FmCMx)bcE0fFJldc(VmdIr=wurLhE zyzCKvFpH#nS#;@E(RL&2Yy^7(pJID5KEHoK!mg_V_6BrZg^jw@q$mceHBluKXDu9+ z9<X9Pqt7-hdnqc1rOL5hdHCw>H&2_nG#@-#k9UL6|FBJ`h}&$XxC~m#w^a0Q`lvE@ z(qWrOS?H{L`M`o?jEJ~Z$puj0kCV1+Sr;XhS;u!YjrU<N$wIZThsG-`%Tf>6=PUJl zJ)H$lCUWv(RH?ASCJ9F<*_!t|0Pbt@7_wwL`I8O&t3nvL&7*=icEyPpY!G%@1=$}p zsT&?lJDGEUL|yvS*HcN&1SN!VJs5szcVMH6=m%M0LJA<z1OjLYnzj-4c<KQD06nF^ zArLRV%=I4Y$C4ee>=Vso8D8z$Bz9CGB1Pd&l_XF$666Ft@si#9h1#SDp`mIYxB<)I z0ICV^I{7la5;c>)tRJ$THxT@uSlDCX-lKar0a~<W0iKyrBPhWn<o&dC*oVYk6`{6E zzNmZI=4y9d&V{c#`ax_}QAPF;RIKlfResC8)oL%s!RX2w7Ppo(es{+OSTGK6b-2#@ zGCDan)ayS;$(Y_VGxnon`fa)loxF_-v9e44{eR2(nTl)&SZD@-++0`JT2<<uWC`$V zk*ejQiz2cH69oMJx_}{CgcHUe#!fr`0&V*Pki=&o1B!8y`n90?Cr)Q3J*ue)#-Cmq z5|OT|Cx)e$qO^*eJFw2o-PGQtei=hkkE)MF?vF#Be(oRD<gAVwngys_+tFvJROZ!Q z5_URpR^8;A1Az!G?6N~mYbc8MRwUOVb=@e?x(bEj+8-dK5a~t;;`Y*Eslh`3?!o|; zDs|$7iEYB6s$_0g5)`!jM=dBLQ!Pfcnlyf^i1D*JOVGA4+F2~Eld3cU=dJn!#e0@y zW_BBMe<fy|U4dPlos=opQ)d$=E0s-mPnk18QS)H+TNopW`s6;sepqNVN$uH^(yApV z;tL8v!S@*Q-!3EqFx8fwMg+*gLUg%!i)#}w0a2aPS8YOwXtzKE@Shx?3#&uFe{U-b z%jq9tGP3lq3f(2atx)9y<s4N%^N$_>*$XZDX58m%`2PLmh6;ypB46&-eYH)<(1|F} zM+~sjXA>JJr)<%Bc!9jV`D_IDMrGH5s8u%uc|~KbcC!W8w-trdI5e3Be^01B2m5BX zxgXrlsx$T_jos7B{O*1z?j#0KoCfv%eyFPAnRLYg8Unezc%Y$AKhc*DU$7i*(pKHw z8VZpvm8Uilih7b(ikG-R%Knzg@ZHhVD<iU(i)g=%e)P@t9Rx}&2S8@lMfE0`&Gd`7 z$`A}V_1iLG8tpCfiW^Ey0{T+?fHLKOEiVX*`u3Z@HImXX+jv@@uKFmJ?RX&;M^xEp zi(T`*Wvh8AIUFW;@;AO0?<7_4tC2Q6Bo-u=U36qqWac|@`itLb6lB4tAIGbRE>@Q0 zEe#IEsgx|1R?BRt&2XdBmX;ZjW-o@;SDh}PE=6A6VMJufY4Q?Ohk}%8iSf72{!nX1 zzzaZKY|k?Qo5nFqf&q_$2`R+c>#qWc^b+X!_^nc}rKSoC7B5gfvCm^y-3;pB+}1sl zPp?sw!EngXV6oN__Uo1&GHpxxLIB%dj{E$Sht0Lfh=@}WtKBX1gAbgE@$wiiTqjG{ zchYTdjNIUUX#Nxrdoh}azHyT}n~d4yON_N{ZQj2KVev<ySo(uR*y(T~VZL4HggJJ1 zBnING-YS+>34ikJBJewjDl<wl+7Hqa-mMi$kM@@o(um9V7y*0C`hA@EP|SvY90&mn zgax}_h;irH?kwG^3>0u6Pm*M*KQU8Qb38sJz4}>=x|7C1x<2fVFG3>-s4JIHPnUwv zuDCD`+f6fL+>7`?K>!E@A)vZB!*9Gdh5Bu?c8NBDeifSVi3x7EyIo0ThJKrUn+cV) z``*kpD9~nQ-=Uv5b^<7~LFso&&TpQ>o+cn8Jl8XQy|opw?Pc?YST^G&VDPcK3TA@a zg~S|DSaVDaB5jPW(Ye${ih(r=4+^9Z5;9c1kp`KUy9$}+_l6~x?(G7S?jE$^Z2z2Q zZd%`a0qen;aTx^n^2df$7bzTMFxOMYP7y4$Xaw@2Fl$oe6x{d{p<N33>m6<*84CGL zue-gIQcC2J{8W|@@t7h->dwWt?i<tVha787ms<zx#?DeVOoAz(gW0o8g^*MiVnq={ z$TZQJwLBapKVU(RRR6I}Sd2q?@EPK*)h`I*EoMk%BICHYm@1+ba8gAyA;MfKt@^N< za)prws__P6(YI>fjxX89bW%;Q;0UWvmr~Aks&HhvD4k(V8xas4Ttp`eiahvguO-N` z6{O;FJV$6kAwEPwnTTZk>Q4#t5M9U$L~>KhpHDMLfB`JvhsrTGz|809$|c%9B(R9t z@4I;y63}!+4j*+t-0aSea#R!WxfH5)2<itqX;#4W8k^;4K14@Fq&b*U1^nz}QFU|o zopD(9bzsisHF{A(>Ax(QOVCyH8zw?m35qIa<WVPS2LEzpw8IgWJfZvfGX%YaJt7O^ zv;8YwUK8uVlfCq3cSpOg)!8_5&~c`ZR8pNrnkkG_T5O$ZI8PwENN%G<GP7ulWRv#L zB|Sae9iP@H(<vR(CpHC-+4Yqc1+VFX-ErI@JNR~1g}lP~WGshM#*rD)x1|oED$*zr zy!LWzx;t>){HDe6wK|4IWsMS?>J1OizQHygi|VYm*|wqDoUHj$P_j|<0sXb9R9OYo zrI1t|49K1uQRcfnoq_IWYfD^kCV8^Z;=+8V_FIDNuSCkbi0x-Utep^NH`Cvu1|4KG zak#dA%46~F-M;d|Hq&Mm{AyBC_J>R>MHg-fmEU?g7xL*FR0UZ@xE8?6_!`q{N`~U& z(8rGYxLwZ~nQ}pRuTP`fZX|GQ3^P~HMvl+^8^+2E;L;2#!(N4eeA6~H)GZ#jaZ+-1 zw?oR-?RI@;560yw%?-`pJNd+6U%){tq@d$rUWjfXGVx#zMSo2F7_j~F^i~mZFoA)@ z<PfC>u|WxlXg3Pct(p-)R9Dk;a&*&jYWa&-C@qx8Dj(t|*58%Nd2Gfj?}v)lB;BoA ze}^aSXeN~Gl#>USm?#ucaUMab*_I*AX1h9&y8Bu|CC)3b7~HG`j}WL!ej%*9h_m%* z$W2X0a=2RZK001BYaQNkr-byC=gWT0+>#;P`k38-(T@WGDJ3tw8oA$qJ)GBAE0t>H zheO+)WLDb$22Pt(X_yM0w8_uskmR99+o22XeHVT~le}W{MeLyu`5qO(Y^NY}7=&4N zs9w;l!cuBNYW9$kk|$I!4_#ySX-a$&0(+J-$=5}qy0?*H<|Qj0A*}G?bVd@@GOoR% z;q@|f?T$kW3A@SApt4Y_XEqceEG6GpQBt0mdJKF3n_VBIa7wx9vNdBBI+b#9HazYk zK93(x6N3cb*e0Pqy6ud|$eTE^ubah3fh;&ADi&fsnIowvJ3R5UC@|N$9-YjBqnGyD z_=PNmQF>+Uw;Oxxx$osSd<;yk4!$;ljShvtUIAYRxi9{eI~cBHGrBqDN~RUOZ$*Yh zC~Hn-J1C8Orm^KtIh(LXl}f|O*_-0PxmPct5DH;qU_4U|D_}aCd|gEGNn4CU+mWz} z!7ert>B&;<bkiR$wZjGxak}bGWqj=5VO)+N$rPW{`x1Uy8~berOW>Ohz~g`An$*Ck z#pU-ks$yZ$3fTq5<MZ`UFNT?)YTholKUmxTAT_%oq;;@ar6NO6=rbLs7X#o7qVMG4 zZL=A){Nmn$BgVuF%P0z#Eg7iFt7Bu=qjS%=anEhKwGARGYXaZ>#rlx<+mQ#J9&e`* zHHg~ZYJz9}lPVUbXyo)sWjO-PXa(lyO9crBqM<?sJC4+i51a9)`Ss(OBR?m}I}&9K zWnGldWdj(`O_vltOVMXRUQihu7-JKw!FqJ!uf|ut_ReU+AJ8=PK}7l>>Ue?xm^#3k z;tvEPdrD+$X)0QYYqM)~YsGPs0~{s0;-jj%V!58hX%S5A1X3V1wGCo;i-`pJzMO$| zS~bthQQPy*_C13`k<t3ZuI>(}S9c!6yuVjXPAm!WdUy=wN-2bb(eh_OInad*-mq${ z|B`sjvMe|x0(K6^Yi;G@tgjdd;*~dcA@P`~vthYr*whnUhQ?;9RFSyy$HSY@+STH< z{Q@cem6)1;BVis76vkN#RgL1&6N$kTF`wX;)W@|%I)D;JPzHm55GaTmL<bcE71G}8 zeq5FWz;p41;&g6fJaTE&T%hh|OODda3-vrmEV}XbmH<=3`F5*g7PI>KU@}s<yL_YJ zGquWmmBO2Po8E93kV-qzriQ7FDG3d2<#g-T&hqC1KsKjg%2*UUc&uWM#t>ASdCSYv z`nf7@7BUr9a@$a*hJmkZYc4&%_FX>_5?{ZrNX^FSv@e@ZgZf5ZBJ6kwx_t%RBROE4 zwzU!+gqaWOP){LRq2fYv){WqI1`Z;1zd-A5ngAd~8C%v~X$jo+YQ&BZAsUST6r#OZ z@(->m<hT!);yW*FXzDGwY9E-Mpjd1vv8oG^xw;aeWZL-Hu71O=s9NV%jqa+<w3Anr zE+vXGfGRr-q*cGgD2(0aArQQZ%ofLai#v18!W}H`^NR!u%Hy&9d4DM;s7Nt*?(4%7 z4ZbQYzl&75r#*68UVnI?6?uEtOmO<QX`wR$+2at#<Hv6>2w?$GW8I1o=$yDL@F?f~ zc4={TbXn5Fq&DVa6h;L!HuY!599z>ckv8pz5eE#Rfp&9D)dPd25%+Z-t^#OW`ts)M z0{N2{vVU)dg<Uc_enXg%gZB9A5x_Ob*j#CW<<&9xXT9YVAd!$Jjq`uivl)GI?DGte zhWoN=MM?V_(llv3Cs66W?Kcorqj$Q!C`v<@&MQ13r8Kfa=4aF3Wx!r$84&jV+!@*C z@S=z##Cy;V00gA3V|San%{Vm2oep4Q6?v6*J^u<AIrpm4rUP*D=4Wtw80J0KH9f<q zW4B^&`(_g@8q~#RL%{_L5BtD!q(L1}bnr6Fxt8USvh(H?H9NlNhU~T}<C>ReWE`7x zX=9GS6YPJ-S){RLrx8K!E~Wly=aDEW35e8KpyioFDkYAGqF;^EZ)dm-Melu_kKOe- zr`}xA`a@_f{L;u@HO*5=FeM@>|Fe8>ko}=fG|K9I8%}5%;qM;>uD>*RA7F~QQR4hw zsMltQ8`Zxu9LY`}%=i6Eoi^(i2gbn8Y)-4IYYcYOWhWBWR_wUH<|B_IZ_VWrg`R`w z$T<_+@#N?oY<Zk!gHH!g)L%R<Nen@Kw*jaUO1E-R{xvmy_IidAGOqJrVD)R%)cbo` zVclAERX4<?D-+ZBa_0+pkxUC$5`>AXV;ZA{+u?Jo&{A5o=odVid`$F1R&9)3lND50 zMf?@4m2l*Y`Qc7P_XOTgCgO8Yij5_foS`-Wftpa6u58j>Ohk_|d(roDFp1+H8H)BO zK+j-#?y5v6PY-sf-w?PQxt~3#{uK9n7&;Y2<{>F^1~M&VF7zgY75*T(M29B1u}jB} zg^O4Sc%!kOF;@pwNjs;%r3HtedK~7MaekIk%?u(G*P?FmBU6{c9JxdkiTB{9G`fzn zKIcQp$zRprU)vSeo6L>m+&hvcIWw8l!~HBXwC1PQfXO}P11>;o46epH<l=ziFPUjk zj2wtN%!=ujhjkTO&}b1-{#J9~&o~4-mSeD(5|)nT9zeE|1Lb4$zvG12jE)5F9?B10 zL8io}d6SliGF6tNCX<uXETf7ttR3&y^nr~{t;tR{QwRNEic%fu>Ao{r^_(_$_g4;7 z*mz9<_z*h0M7hsZKYC1>+SFWAGF_p^;zG`6Vrfyt7zKfYSgpYpj{TF){j)r&o^nxD zgYmE`Xr5pSj-4VdWDIj|cz?h({a;Nc!oYT`kRpi#IlQ~6*<ekpM1%Tt&f4W7x1u#p zjc8vosKL=0cde)E**W%=S6z<3LmV(`0dG5Id~R0i7%2Q6k_hRiY-BfW*?v4={ey1k znuM)Rg@Kes93WYwaG*kgdFhC9X-QmC-7Esd@^)LZAFYIIKmnc&pyZR0%E(5=R%AAb zs67Qrg_hck^}LP6S52(;wBZR&*Ud`n@$<BAG(~^C?Y9pQ&|{XrBl+deh$Qzxwpb7Q zghSTs@3ySuJZ~O9gI?Tcy_vue(ZPBjg5;ww<=(kirVgdT_t=*^ul&6F7l#UGtBPhZ zIOm%u10~YnA>H%N63H)%L)9G#M4GWQ+y--<)_Y8C{|?eIl5Va$(F{;x1}f%5w{OnA zMN$S9jdlY=%8%WmpqiTLP<1&cLbu8Ace$6C@qb*Azc$^oF0{%gW^XT>B4VbN8q*>$ z`H}nK`0rM%$#BA4#THf_C=rRM;gq>j9Xl+R+f0l8eoaCdSX9sxJl$+qVGdUc&Gw@_ z0hgi9ZTo1cBog3+QykH3>Tta;K_<7&m@sOl0$9A9MJFj9vGaV_ybhFik~_iAE6Grm z{ifu39+h7<GZT=JQG$<p@d-dF<(=_XyEp}H&%>6x3<6H4Ab>MWMgbb2sH;RwN^r1q zZ$Y)8ZkwbpOtev1v}QNC+qR`TW=+U)RdmLx%~Rbn-Zmi$Z?qmp!cf$_W~Ew!>^c8R zp5^q4oJ77?dCS#n@%S73?Mm-6AE|p~FE4o_8Waa{^mbtUp3}yqyC+QBj-+y&<tj*d za}&%K;j{}>@J&V!&DL(s?Ete=>rCBro7&qf0iTVD64p;2-O-DJNOviJhSP@I*Jvqj zz;4|sRgJJbXBi_`9)jlP86n!um#AGf%i-cphn5_#6D`|{I^W0Pd^Xe%nMT$u6udns zTSD?=6Drs3d%0a(jc)~6Gkcw<=EawIlm~=vFHgls{JKjxxji@K#+cm@CN{?}nikMt z3$XE>@V4c?RSmqDk}_)_GHz0owVVO3FS7XJ!Wt75<0K;IzJrJ(h|UeCr-R&nr<0T8 zj5k%bpl->i7yzoJ!Zr6ZX*@25TsU>=)Ss785oXC_<FsEd#)HvuoGE;N)O(6p_~tr8 zAp(c|u3B#1k#4uGUw8;|Z<vQGCYE53v2XOihPmLd3#TW7HickVH4XJADM*ofh%<?4 zmNT1inbVMQIL=LM$&Z>t=S!UfVVvN5g)&uH{_9?68_Lap^F^K4DgW-)KFfPlVq-ol zRG9G#F`DZkDdi&y#I1+9p+Aq`Q#CMLf@0^w@NcB{eoMf|+htAjdAX7S!S)H!GWiG9 zrETZMWgr_X2ihViYQHwBT#C{>Uvi1(uOOogdrSVCZl((HL5S7%(v?%rcU<3Zgfm6X z6pTe7@l{#C@PpX)B6a?7LH9>*S_#X2ur6aoWOWDp!{o-Q=Pgj2G!pw4eQs92xW^CT z*-wf0WgMwRCKKTRTQcKSLdbg&R3}M3p5#q>x)z2lbNo567*C?bXo0qDdZvbKUXz)Y z!{vVGs0f$gC+g~l9L;QRJ(vDk$4ij5;3(~>Xa?*n^ganen0Pu{NpMuCZ;btxp0DgK zRD2cR!|Y%3hLR)#S%<CD?pybkR;^ar_ZPAocFivnQdBds%IC^tD)Is<Bq*#cQRkBi z2X&kD(PloIStryHJAts(&z9*Zm)o7w9%{2e-=Up`4~elRWuwCE`DeFmARSnB@_jgZ z%=4>kx4V~2G`@%R=;jp@a?{rd!KnP<OgC1R%aK^nT++8xLRE*dKS+5bAeqR@AVG)% z;1#u}qw#d?t@M;17fdZ2o8^*}vmH^-g~2@xz?QU6hhG_nN0+;59hb=K)G>e6@agb1 zt!QuCIMgxaII=)={~qxQ!`ieDvkNYtfpn~==VUmzJ$Za8j~6nn6Xaftt@z&~5Htd1 z0mkFVDUswb4_CPoVZT$dRq*MF!_EcjrJ>^;TrTN{v;ML&G)Bk-S4-FcyHg32Kx7H+ zHLqVNUB)4B$|FF_WnGg==(7KY^GVh*DFZ*|H%qHuv8Up_Zb{00VVE>tc=S)}3x-Gt z+yIZhU7u!^{uK_pc+o-3{Hm7g^Ss9J>sqJYTBnF{5CeLZWiXEmZoWyPL<jMr`|c~% z6!{ZtrVuLt$W*BoAQkR$)>1e*yQ97B>Mt4#a@qop;$-?rn5EVB2rl2EOK0*Yizb1( zlMP7hz|V^(a1OoIA)xQxve%;{-Nsh?a<-lLF?;=7txyMRV*5rVjm;57Z>On^pXLB- zhVPA7*P)yVgVIMx`dF&|SBY;WUOOAEneAK|ogp$zi5NY{tpjNS^CA?ku2MP*#LG2r zo@`qSiHC5r&u9@{o~v(AWZWJ=#i-Wf7)I|XoTBTof)Q>n6MzN8M0+ivI&p^gFu!O( z(6$aIAVra&?foZy1M=XB<*|B#t!j!)jm>=Z)SJdEx{eyes-gqvo9cc<f|Ftb@7Wi@ zKu(nl+}N~3jWUMh(D>ICV$80pd;7Z*z0dU@<|aDs{AThv!B>sv*wd9c)?)@|+Mvu6 zDNn02A{eyP+<K>d8Je(W;$lax)tZLU`|ebp=NYlLv;LsYA!kSTLa*szMmDTd#K{=? z-{ZuqQ#TT(tkW<(#L4^7=_s&<@8cgf?ZQE$G&j{4kx;^KW#EKN6d#b7#GRdL(gbn+ ze8}j^OwG$JZ*@L}E+_Wc<Z_%T9yJ<kRc_-(lQJB3QY>mI0{MQds*yXg`9RL2q?e={ zQn(m+urgbFcm67omO2#TQTQ*9c{F1Ws<)**u%d(!6<iuPG2Gv3QuEx!a;)R=i=b?U z*BAllHj9&NA3fgz*K~iox5K!9dnY%FBw<@l$F^(_g4Gv(Ub^6$o__e1C+TUwJV2qc z1oRj^zi@z%XJyTEq5~AJcooofpYWD{&P%N<F5FJYf6Bx;E8@uFx&9{kvh;qO=VL>~ z@c?}W@TewFWsPfi-+Mvk<<ie=`jM74R>}?K+_{`a%~l})<u#3%xGs~_P%%2m=1hT_ zf23o=)b-{IV)8P~8nNvR)?8mbrg53HmOIKh@qK8#R{#h9236KsUT@l2_0Thb`Yq3} z1{9fbArFfnq`Klrx9&VU!?7T1-NISe#P9Y>n7Q9M#S`{<)YBZ?43=*hq7<!gy|ju| zwThC=PPnFSkz73N{u&2FlK24hREu0^uhN)cXwr(hpy^o)zW-a9D5-!uMPy;XmHb>L zyhO<Af?$~)OUCd<qfrPvbDf%Lv~X*x4l9STQ0vy#GZ$OV4uz<|7jb|Y_J#LJlX@J; zU1%?xrd-EC>BsH%gOs@*JeWNQm8tc)_T?rM$+a)PxlTk4Nt+aORzmBVyz;k^9w|%| z_*FDP9y0HTMJ<eiL1XT1O)fK|{c~AG^$fY`Hhh}x`KD!uGsg1RlW78Sf6<@U-<ACR zOI=OU(+!_iUpv^gVojK_LrNvT8>;F?4lAo2#bhll3OdnJWwR51{1LSEemT12WQT`3 zi42;H%r3V*dvyXr_fTu|fvw^EEVORMCdy4RvHVhs?e8m6^=}JhwmE=i*0Rr)_|Sx! zh^gDTSw?6gk<>lzCKHIP8`fCKwT=%6eppGrb39lxEtE@nFU8bsGT2Ku;OQ{Ca;l@1 zf>>^6l*GH?*PN_XCx@!ED*$oQdh|l>)y(3BQ*IB;ePPp4qfXHWjXA+#w_(r#l<+yv zR8HUrwe#x%HUvM?AjsXX@=|_UN`Uk9p1k{o8UJ9Gay3H0Dpz{nybXl`k$*Um5-e3b zY?oY`BZo)1UiOAg-4|HuGaKpjyTS^5Jp45Wa)`{vW&%@H&y%Mq22~A1+wHqYG1FIY z>l2<FrU4Kmh)BZsnQqM{BxLul|BxSE2@Pdd*1kW)HrVGJgdIK@Eo+u~35-=X88on` z1vn3(N_Ipch-?a&=bqV6`YT#p1V5Q@ML-sq^J^5xqq^OKMGWq9Oz_jER{aTAd|taQ z-RP(7Z<EyZS?8<2sA4oFt72l79yF#p_EWMKce+QGeaZY!s;@s(zWKMv_Tm*sbS&+W zg3T~+6!tEH@m9CI=zm~H&CnaQA8BSAXNQ(rK{#I+duv=j9c;NpQwHf%Sjp`@&I}Px zcS6pPDlksPrUuBfs(QV-fCS!8t;3lGaMZ}=eJ?$77d<aKahUl!$Crzewmr&q)wkJ> zHEg@$4|E{*83Qbjp#>_7zsoS)tHRL%z&yfc9pDPpDit<eqImACYWQ!ZdTh#B8~<SR z>TMwyjWX+d&X==!9D{P|cq74>fXJEuAp94ZNp@u{vl9^patg!@RiFlVqv#?R5=vm? zex+xI-pGw|TZxY%7fzOlVoM+L65U|<z20~TE&iaFtx>IZR=#4qB~P9*%iUj77389~ zpV>b<=5DQ8W+ZAQM_X`On$^Ip$H#cOfQ;3TX!KFuiw%gX8*h?~*>JC5#n9W!-Rjjj zUK;EAjuxs4@=Rt)op$M0W)d-2(8gmld)Ll*#j`Pw8pfZP+{Fz<$;jIqbJGa(tGVfN z4%PM3E#hB!$79lgUIW!_B(iyFz;f7uV{QHt#($`Kabc0Xw!X#)1h;V99mizp!2G}M zlKB^!!65qbpHC1{0u60N!=DS?2D_dw#uV6$LRrKf;#@~7FU%;r%Wsv&EqCFl;aqB_ zEui=~Z&1ocwB_!Mt8jeeZ()d^lT}!<H0Ywzt8sRfj64StMGwXKyB&?4js}E-J!bC9 zmEd~`kL?1YT>g*&>a+b;a>pHmhEii!4*t@WY?b=PQzzO>`45Dz6EAfWuq?VnW?8WJ zRDRQ$HhnSE$~i|v<Kd=Tm`p-Nj)WLNIU1Qk3PExWvD3A`;{PzgkAtEYXezr(JV0?X zZ(!(dqt25%3nbzh>Chtv%;fwiKXi~ZpL*BSkT3x-z~KwwjJrVLD_aH251KywyJm-F zhn3JZKQMV8%u%Fx#gNgb`|DZ_hu#@g_;hP%m$v(}D$X)hrpkV78`GUji|6h)HYqup z8+^PR*w~#fIU~s;IJi&LH%7@dZnlqKG_2bgIrSaq)6#eEm#9zZYt7}mlw)lu#Vy`k zZ*{7-b1}nA7d#;o_Qmo1dV~1ML>H76(Mk7YHeBTZoMoSG+`E&>YR$~ozggCw*yuW2 z9kkI<wx&x>LwphboZrseqxW)P<6Hf1Q@=w90H24UbKh{(XRnoog*%#x{Tkfeh<me= zC8)lNBH_lFFAAkxt&r#1#*;5f#ROs!f#iYf6+jyk@4V+wz;GC$<wytXPiH4&IU*^8 z1XN5{f&aqbI7J{L@=7@&m7#+kkblXCpg`^g469Kv=5y?{R_ytNG8`I|RjpPySthU4 z6sb%Lp66sEdZEfqyDJC4KU+HI$^r3ygf5$~0vAe3!*-)M88n(|x|?Tb2v6I?7_?c9 z7@FEkm`ENPLE%P$cTuB`))!w*gZl}-Ib}jGg^CSbL|m%vzVgw-dHp~@lGclFq=nqZ zo1Eqx25&zeXDG;e)hCG`;m%PiGx;YSN)my#qv#kaRp?1M@pcNbOqXLEt@#&<!`ob~ zH+K+y<U2GFE3&8vo2unZ#!_!%&UfIabMy#Sw}B+~qeOX9O7|3HW8OPR5-s#Z7C%V0 z7wmGgp=WI8E0Td?ElpLyyO`DL-0I8fctrQu6VTZ3;N<#j-6$3E&<sdgKe^J)7?b}w zUs>}yT(i>(LTf5Se*nVpv=uz^Ymibjmyj)7;oYG!6S8870=yro@H9HMh78UJOL>cr zO~55bzH79wlK)<r(z)Fn6eY%<3>M<V?F(1u3hEPHAe)XG+&+7v;3Ab>z__4RDBf11 zDkwD&be5uB%Y+fMxX76?bj&|f;}6^;!eCT?x`P{D=VH^-<F4;X<^Fat%;Fqxh74sQ z4iv3fxa8Z-=faxh_w|`}+sxcna#8V}<aQG}4Bz7LU0G~(u{<~sow;HrJbJyuOCUaU z04K?cBJ{s^$muY6kK=D}<#E#q_`_db8~%N<j$dNu^)ZN&<sP6`$ROfC!h6NS7vj#8 z?F2IrL2#4z1-rci!xK$rDG=XqBr6%BzuuAa%NDfs^%?RO2oCf@$FxgWY>Y)%XQhDR z>3iDtg=dET^pH5$%{b(<ut*?aDxN&Io)Z}8_N=|<-d$?jy?au}Wc;JC5Cwr^rz3nl zw#j?@MlUtcvpRTQtRFRc8i>)CD7~aR!cETxe@S~w;krWH**c!TC%smCD|)uXd4>}p zaNz!-PHdm&rqzzpL-q5%%k!i_RZj1HEr{qk$#B0EZ?<d8)0=1EM9!7n$@=zyT#hX5 zutJal5|QnqN@{=wb;6}uIzy_YaB^mQ$vJH8+wZ@gS(m14n{h^i|H74~Rj%T4%L;Q( z-H}!^znm}u-Y;L!5#Joq4(XyUDn?;ZmV|R$riL@Tjz~4ifU<&i8Z5c%^Ua9MY!8lx z!%MW_<vv=doqp@{lb{+AAQ%5oH?jlF&ND9rY#f8|5dGtVd((dX;MoH28j8S0Y5k>} zE;~A_c^stJuz-gvmkw;fz|$ufuL`09F2Uf$9CdBi>M_?`5!pPvA=G~M&Vf4jmuzBp zXMJjso<S4OwWs0mB(uS3wE1I2R6ifkXD$m@az^ie=j1pA<(KQdAL3)&Q`!ABy>q|& z4bP`E0S6&w#SPd&X<i!gUTNLG`#oMd>O@>p0%~28oR_H*0nvbdeYxK<+km7zB+bG3 zs0cPJ&0E$nv7&=T+g@>*%X2L6_r=B5bC~N&z|{T6fsS>+r#(J&aR-rESdBReL+kU{ zMOX7RAS)Y@Nvvs1;)h22NwLu?uw6vva0qxZ+eA5KShf273e=ff`kPcX?C0uT+wgEi zeb>*lB15lnT7iYsOu3Noa)+EpGt|Gg>$OM9n+7!<Z)c6swp8h*D|5BFfV8+(f@}=m zC2^d=p*)!)sU5YVXt?Y9y$(K0nn~HoD>_1xrCGjM++M*8OCg#K+)@*QpPiffBy4~r zGYobnW}Wt{z*&UjhNwJ@L7NT;vpSV*0{P8~47X7>8YSPn4RhB;71AcDj8tnu36mbO zqW{KmBh!$dZAN*3<5Y*`{M+L(I*@u<kx?Uk#>8@EEgZQ=vqPdm4>l5whKBdFk{Bn4 z*^YmfwE|+6^Xy2e@-8=_hn1-^rxa;1KCxWVxN9$}3}$aD^)gQ>F9v-S#F;ZnC)a`n z?GLJ~abT5#30wc3^iML!YiAJjPZo_C@Z@smoMC6_bQ<6GfuLpKkw)$&Go#0JFODP` zIk}~QCx!FvFGB{4pe`lV+h3l$&nADDjGF6*P^yA;>st9X-n-pct~RtdNM}_EYtJ^u zgGe~UmBwF^5fee7NF}oGBYMt^532q!UR1vO^`20P?Ed!y$0I(*l901R%H&InetDoc z-C<hW$t$K*JiAC&+UXBM!2rutI|z=K^zG%-TplOWj9{l2;5W`O`!>RwH~Fk*`O<#2 zaeHSPXb=s9yW2n<0dcF%1q+1Gr^>If3kNe(<1zTe>^f!EtCQ@I?2A?Fhi3k3*HGw? z=AHo~1@Di;Y*vpT`P-UkyzKfDQzs`WKz#h$id_$d6i?x3Qy3zWr>#6YJ@>9RT8#d1 zHhK(P2mzV*SJfxQ8D>R0D-d;R!=ihl!(Jc7DOM)OQxpsSa=XW8G=(}OP(spa5Av@B z+@qs(V@M#vF@)t~%aVL5KSeu@q4u_^>=$MBxMeOd1qz(8@t)<wZ4`v(uE+xkHsr`O zu5}=8r`LDq(MwFH_YQ6C1+L}q1Zhh`OhHi+aeSBK1V1f_$Pq!3`gH|phVHyk!}oxw zULhCjHRBEg1xpE_jPwPJt@w!z9+I=Gs?*a=Yxd60mvrHIglS8^WyI43OXqy%hpA1- zI89AzcgP;ejU%{ig-V1H6ikGOoMjn?J0V90OuHvy2Gk+bEmC--GK2tk_F}#rrgo<f zhaagb-{DJ0_KLvvwrZG#5K2;gO99b!a8jc$k|&(ejEw3ntdBlyN6NWVxti)snRpw+ zt^02o+mfbr3Mx`XwV_6Gqx&jr8Y4%hxBga7pbcB!HAn;Faj)~pF0F9rt3w<p1XYv| zV#!*vbyu>=_t2iFARc9eBwb#NgiT(JyA|J>;RUhbTCopgt#5?io0Hxk9~!xN?Q#0c zT++dNZOb%v%!A^E^L2g6@rcxeYO5L}(4kbzG+V{Ng3rkLuk6RCde~C-M<oJ>+UmJ> z?@{F=XSlKa7tWzR>NzIKIy<Z;B*@}&bJL0eF9$1an-Iud+Yq)wpJ|)c+%*ZuBLf>< zyDnF^TwI?#o~w+Et&)-Joa**>%1~`N8I<)6(j#kz`f)(Gc={e<QEZ|UMXTf%`NQDR z&fdwI*Eo|HR)uQfj()29nWidFIRpuEqYO^Z&yl8X2a@WLt-Dlv!94jO5%D`9#lDOW zNJXzV!KvM8#T;!<{`~Pz=M&IjjQpJXb+QjZlvd<1AX_pL0k1#?u<nD!Y@E{~S|`ME zbOol?L`#GrY7bbF&b=RHpn}b~AK=u=a+;vu#%*ln*e=en^9+Mpva2$h3-v(o+2pQa z82QS?Zwy+6?O%E6kBN*g6=H`%Md`4`<HNj3A%&EGmKP`h(T>KeIlyg4&|<-~eZZbI zb`eYl!EG;4X*PPo?G+8YOotNRIP1Ht8E+U&kY$LkgH(1WPZDz3x<JXpS0EGXB0^(0 z>A!KTD>|L?f^^J@#(0VYKG+eLH+yD`n2PgiMEcZ`Az}p{y2{OM@Yw|;VOK9G2bt)4 zdBieha~cY&sQ{TU%9Nn2l%V!6g?QQPe;7X;VjvBeS#hX-`Mg4StN{dQ+3Yr#oq_Ua zJ}w6?Nn^)S<tG-|#u#72%D;QUc2%A*bMI<qv#<0v;GKEC($~M7OPL{jx?6o0aFxG* z*G`K<ewlI*8>+-AarCj$lI+>?qs@>zeh%UvaK^l}--)1{#2JWy5}2E^6&u{f0wK3Y zgp{c(gOk;`m{IgnRlbc%1V=fGLxBv$K*ua$F4)=R>4qSk=(JVENfj&Bl>K|5_V#wd ziOX}mIw~%hV0$R^t8v4lR3L~k!3W$IkRxypLC&Z0U;>Of(zs(U0>?`z0ho*C#)hdj zMTxS){|dsP@j`6(c}f1&vfRi_Ri}e?6ieaG+=V|7dEZ*c2{Tt8hwR=aF(o=xo0Xra z@-X>FKi}r2F3YOz<j67C84J=?V8SYTZ8oeg6@@_+$*dX{D$gV2jMTv?=17|R>0nqz ztQ(y-+3|=H(K<yF+o2-*06X3pko)2j!OYaOMjM9Q(>$iPV7-?w{R!Rrf~fDN2eGJK zqwL?Mq?=>-bBZ1Mv~VSHcqkkkzu#|&xyu6?KeSdst2gR+T^^Q=SqBw|Z-}NMYTzg1 zjwdPEj|&Ni2=lhJzQYq|5m35RDZ*KHLS@!(i4ZM*@KywI!wz1^6O9iShD6c-mBWYu z=0ymw3(3Nk7l8&Hlg2wAxFG(KI3U@N8u_r0Hxg5&1#Y3R;bfQjL*JgYsFWU|3hm?) zGT%8&ot3WnZdrOT{K*x*G|j<TS}VtNv@BWRlRZSA{iL@IwNt(@{!_aF<>jUw{c8&= zcS|yPstsE~JPLgAhtn>{{Fv(t;PmO#b1aW+UE9s+q-Nc~Su5oFSU71-C=5v#BIYJ| z>W9<wefFTmXbTj%nARh#x;yq`xcz{=&)1%+9#0ex6@M)Ai+~_MK`cqPwMTdB93EC; zx3orj+L-CO=XBM2MrSK+!ftU6VKiV@a@YvK?HIPIG;|uAr<B~g1U&I<_ODY!o8p|) zPx(&#uNIPWa%7#PBJeVr9|Fe(e-6ypn+WH%#ON}Z^SSo7?#~MQCy+^$&r3<<e7xje zoE|k^-4I}ib3agyS<pdnFTMG#)$Xv@B1O>HtuH_(1-3#ZYWH9(JF(8IyKxU^TV4)4 zHi9h9nuJI{B&l}j1vW%w=pCx_paQN}Nydtpv+YO&6X+NpkI?se`QU7>9I(INV+>K* zVMo@losA)8P);TWp=_p(6d7B!`EBzLS4H7{5rE2&tqi0H;xTR)go;Cd`W!4Cdx21+ zDA%yHKj`=@BgD^>-XGlkR4wtj@1HdB<6rd$YopMbucz1Phd{MNfG4G6>~f|gvp{Y% z=7-&J<%4v+eM;MrDwR)W64{aQ3iJ$O3NnX@vdLwiu+cR)GGPf*o<*vtu~;$)5&d&( z*eQ7g8=KGo(u=~dW9Z|EF97h%=cr5E79Vow3=n7$Q4<0+_5^npT#I|)DIJr{&~Hv? z<!TXMktXja)#&@d#cdA-9!YX=c=kX2q&4g|JCBhXe%-lGK1ff-V@!lk7&-P8!S5Dt z%s{1Dd#P%YWaY`36d6?|TwqN0<k{_0Uf9h=p57hs@`oQO3n|t>MT~e2Vy7=x++I8T z-kv97)e1O-Mtab9GfUg9{R^U;W@E~c_n~_Da`u3nhC4JIH@xd^7aTy&M>?xeS;wPb z<6rB7_^7Q!cX?9mc}@k%K=XQpMBhs9Uo=Wea;F=c6MYH%m3AOo8=kVa%XW{r>M_$h z_D(J&DeY19o`Eolv97&=#CJnGksh_3Pwuns+wcA1{q@DzdY^`E3*!!%v_BP6mFHV{ z*rcG#*~N_G=@foN%b!THvwvS_*heLlg&qgper`C;)yi0Oq=Z+Bav4Obmh(Z#u=$N6 z)}3L9w=yb3g6^R#4=hA+uJ>0p5Tgs<UufevEor<T_E}={1ZqTSx+J9$D>@?n5`IEr z5<VUgNmfS(-y23rSo-6vBF{|a=#f&q{Klh&>cRYc%XGRcYaom5NY$=dxww(#=^}pi z+}@SNCWNPq$Q!ajc*t~>_0)X-x_txv-uWnHonb{Ne^Q?e#xIJtv(s#SHS6Y=hl<nS ziXjTXLvGGMms6=AqT5@ac|DFO;J2fSy0`3ljSV9}33(j~wv;8|u-OUjh(saLBhqFe zTk$QdY*RI>8=3V%TvW%J|B$+^?12<OLh8nUzIkpQ&F)lT??XSbrQLjJRioadI3=l* zQs-f_+81iS5c8;mnHkqoM4UV2?%thN_8K@eUA0N9w5%leqKecgd!3NK9DgqDP*0DX z@o5qizDw<fL|@G{#!Bv|b@e>I#cE0BuR3dzf}B2R9s5_31O-<T5bDvnus8Gg9$V~y zyY-Tj^cnQ2GG{&?v##d-?nZ*mTZ;3GFq&QIFd0|*nRBv-3xXkQVq^l=YXKO50kaf! zM<fLAOq+1FWJcTcnrb??V_t%se&oT7I8RhA!lc&2{7@|K2Kw_-tscm5hCB$^x1uVr z3Kst%ZHUE5S@6|2au-KTdU{PpHKAq9Yjb;AlBh=ed)Gn|aD*ThIBGXef2wGC6X>~* zl$E0*lh?=MwJ@=;Fp;QWB(OYQQhGFwk%83HMzK%Egn(7|*PJsxXKDYV{(UV7z;M?@ ztlo?zP}J%2oXp*c=%+Q!ZP=aPw_Pb$MP*lrcnjB;hPK_@pjK0%!D6G`eH%hf9_8$~ zlgyuyc*;-H-p*WD6`x84p{FfJtrt)`qtoZ~DvkB@MoaAUjqby#j5l?z0toAGO=CsO zTK%3NNEE1`*7no#@}N{<V+G)0Mp^)f${|ajqYc+j09xh=``0fD^L`R1Tg?lenKG5D z-HGgC^`FJ@!(Zu;XN)$R7cV|u`F6%ecY6{?_}~4N!r#;UbN$o}*y7NhGz0=AQ2oA1 zJqK|-zO)51x2mhZLpn#{@i^F!b@C`}_*`T+zt5!kOB_GM>3MyI7_^)1NJeu%hk`c5 zglyUoI|(?oqiGg^qq-jk){Enp2E48UGy+M3Doc<!ON=(K(wFZTcHHkIinqEx$A(lY zDVFTTp`m|SdX^}O(@T)`u(Nu7-6SWUe18qo%8x{a{k_sM_&VWibej)Z&=c|rP+j|& zB>Q93YuJDW-Ws#g!Jlm1r;Eh7DSw^xnX7e2CxNyUYmAsPc^~@owf}SHLt<In;6W+? z-(NcR@04gQ$A$w~AOQ-r#r>9CXp6jEZ~s&isBf|<tBFN+0R*#TVcx@R5;ov5k2D__ zwV4ZiR<R(8>2H$T&8OGh6^U4GWYnkaGpHj8eTaPi&2W9H%i%I4umx))NJP*(8Pp-H zz+nJYfg-){2Dp<%`UBC{y_mQ08fnFMWH^_j%N=#={CkI9<h$A7=+B3`S{%i5M{L%^ zcQEeFHw^b;cDjn<H!qk`>5}ii5Bc>wQ<R*K+{%6tLrNZdbOby5rHvnmefYY+fd6_t zeE?1Xuz&hrlOuo#zyxspH?oFF{T~kifcC$aH{cuS7}ysC4HN?OH)ta0G?+429@q;w zA-Fbp3<N1eAtWyp0LlxR2Ko&~4;Bk{4vrgc3?2?X_ZQ=@QUn%+M1&W_5X288Bcw%S zcw`&oT@+@NMN|>gMl>|EeROjSQjAE97fd<KBP?sIHEdPvH0%u=0vsuv2izFkGrUl| zJ^XM2CV~n=YQioe5h7n=6ykK^9g-N5cT!K%S27=RG;%KrSc*hSXi8_wVaj1D5-Mw| z8mcvFTxxsjR_a$8Y8o4wMw%~LE!sZXXF5K*Xu4&3P5LDIYX%jDB}Ns-X~w@y1We{k zJ<JTu^~}S}>&&PBR^Y$&`G+TB9U8q~JkJ0akpKEa0c#EE;QvqmwEuvEN(0~gi-Y{P zg8w6K8@zF(3ZM!oqPIV7ot;&kWLw*~NH;qmh*X=BB=1vX9>sgOOxkugg9GWz^1XXt zx&Vc4fbJQsl_fc>BH@xHG0!|>S#(9Hv^!(o%$z*QE)J`|)_6U?=9CU?eLUX5XK@t9 z_n}^qUK}o0%$9In!oTjYN9c19CUK2K4QC^jQTy-ccC{&y-v}p?xMtten7_=X7R1p- zO9^w?;=H^Av{++&5@tQCkk3$d<^%IQg8bRa<dZ6ZlIl|WIr0FdR9~={G)vS37_6aw zN1F7hoQ4V?S^URaj18C{s-VH|KyOV71vf|d41wr=IV;qVi|Ra&5-t<pyC0CeBHyKf zu09Ez4dutvgiCj`7k}Yl{><#Yu1dw!6#lXJ^?|qzVmC^FIVXpM6d1u^2Ep*l3#g}7 zp$q_U+c45M*4OuV&5b6E7(V@(`AOC6@+T_Gg9jA;0e}0S`-WT7O`-nD{{H1K63h%w z0_GzSupv=0umXMk03bM6*jq4+oh}o<06&&h6mT%`;MbpY)NgGD5yoz#zr}rhbNHjM zOWqL{*dQPZDTn}#6r`8`@*2wv3xNxue4Fm{&aPNFS~K0mU!vWEHjlSh*ECw=@AdX& z>b^7r`SEceK-1>|>+w9FaPI(9GT3#wjZ27<IVc7(H5K9?rj2w(EZ=*m)?UJR%Py<P z%PvV>FNt=u=<`J9LODc00v;E#W2}yTE_L0{`4-hxo(nre-=jnCS^ecc7QQKdIncX0 zP^&kPEHV)VE|Aa58?>ymtwU?ilzI(8bd7d)O>1|#tJ|e|re15ke9u3QK}1;zz#1_F zL6JFu_2n5Em>8Yz9UYwPA0MJ4pdg_kq9UipB_*W9CmY3?DJZBYsVS-|ug)zktjsSj zvNNzSu`#kTx7IZ^v>4PiH=5bmIXSr4J3GGKJw3eKKR^EK0ELE!f`f?-1JyzhU5l@` zQ`3R%(1Bti$C61&QB_M;M_^zkqzoivrRC*IB*X*+1z1P9ZB(fqXIzTG;)bT|Gqn-y zwy)b9*3&a@+AmB-?wX-r7w;awV1BYVO#Hl?^Ex!h6bGA&6~@QJ6hTo7SEU?M5PH0I zW@Pz}M-Rsu^;kSwCD<tR;5Zhie=$~tEMHXU*5ih%4p1#!=QOCKf#Ye1P;TF~8Jb7` zo#m{V_S(v978^8Y)%r`~(qweXc)z7{b+<S~)<a#{DJdknOQC(JSq9F1Q0d@<nqr5B zEO3N*f2NZqB<#RORk_*Fk-cG&K*FYNlVGmqc<+(mro#%#-)ssl@N7qS(gEL=57H;U zCdFHW38&MfwxkZ6kmzIgHsszkv77?pEx~zw_gkl$J$}V2Yi5022<Z~ER36<3XFXww zpkdT`P&j{6Ihl_Y&dNDYupXYNrpl_W8Tv=Z)}6;#LLer%vUha}Ivm0$gUm3YMLKS< zgq@{ifWn+p+c-9D`zZFMTcaif53>~In5(cENO=N{E#1m*OJsbxWM<<F!6;Pcbh543 z?lMpMutcVeolPs+aCAj(!GR}-L9`g@UTfvWZJ{qNY;+La0T%|Bgom&b?24@J(GA+B zL|O_Gwu*>OK7{g^@}5NE(QTtkCn9n~i@SXa`SM*nRz5BD@qTi+XTc?pU-d#hLr6Aw z(w>nu_}v6ZzI~y{d^-;#H5SUN&RGrlAuSSy@Yg!HyPU~dN};{AqxI{oJ=jk%nIPHk zy<4HqPL)uUI)#fQaBafMQQpah39W3V3Wa2I$`eXlg&`xZJw;GC5Glm()N>1I*$RTO z#RKEP<6E5%By+ClExC5QT*hG12O2jt_Zbw`_)}R$WcPj)=XiA4{}ZGKTlmosX`5n5 z<U6Jq7Wu9zMnryS$Y<*_k6Y#9E05iB@wLbA8O0OszrgrDFun)h1dJjbU=(=?7)5Ge z6nPmKMK*y^<P0#1`~Vn5&H|%IW9VVoYMw#Z>6Ai&5K1{9*+{?nhCP=(OwtuY)TKiz zp|iVMp0Oh>&`KU(-71fDmanvmx^*UsuO?-J*Ggs%J-%+}R*Clp?5t>i5KI_?AFNkr ze^&C-eyr~n*R3<i=qB<9;Dhn93bVT&-!lHWhaTTP&J<0!3iCTCi8|XPpSm)<pz?X1 zcXb!j9<LyOwlLARE$hr_#J_8R3Il?_(u7AscRlwt(Zd|R?rk5F-}k{a9f%ciD)aty zKK`jfRkDAo%v3KXfxLmkAqK3p)*VbAs<{U(fsOuSUkTGS&DAhln~Sl@Y^+mxgZAFZ zG$Hhi?x2Pm5jw~VqKuN#K|!=c(KtyVxsHaa@AC?el2Nk+fzP@AE4LjBCBH8m5H$6f z0SEdasCgi2<f;Rlb#zyPNwpq|zD!`is9K7BdZ-;6noT8~ZByQ8V)uOKw{O2t2X*kE zGFsaB(EvfXS(;S7p7*A>S<1&olP|1w@Gw72{KB_RV*sCQspod8pYFCk(B_%pyY7Kb z1H<>+9D$`-fOfwP%Hp#R({QW9!0uKHYP7nq?+)Cx@o3-;{}-*;{lB_T;AeLMOXCUI zQEw+1V2p%}&HOu(zoQ2P=)1d9?jq$g{o(8>nZr{)-^46_bcp`BDU@~k6yL)7h2c98 zFNL)Tyiz9zib2>{M%s~IV)@$i@XlcI1`A6TZ%xlCHGlP3*Jcg_Koo?Jp^u9M4IFbU zGqYu8E=$m$cUF+i^Y<qfI}-X!2J<J8#Sl3Rk;f1P%$GzFLzFN?8ADVsKN3|8QNs{* z4AH>+OEfV=3q!OqL<jRN(Zvuw4AI9BuH`+ahk3y8-lc|GA5tT|o@9K=7+c;;swWK9 zQ%d!WQaZQ1*OV?8N|%(<6{U1-d2cD*FqCe!pgS$--U=UYu9A<ZZ_|5lz3V6El)O{y zGb?dTV*q&CWANU=a3UyTBO_y9<Oar_91I)`$psr3*qk;=FfwR?7@N5{7&#y;Q8qA( z-Dwvy0|O%iCy>qIw2Q$3!e(Z0*{H(U5g4(-p(A1=bBfDGH4twXM*{;Ri$h0pNCZep zBoJ)o<zVE|+QIn0bpuQ9Mkb&>))W^2cUdIM004N}W55K{fiQ|8ifJ1I1JgFfTMP{U z?U+P?>@)vgF&$*Q_5a&{V-Ozz?6wUR004N}Ow0iW0#Ou3;dAeOGb-L|B4H2+0Z6ok z5X=rj09r?j7!c4F2GB}cM<DX!0iS~dY^-1azyPwbWRRW2RStIC<iwhX+>jaNw(8kV zyyQY}AGxdktN%Z+GzXscm5r^BvNQ8Z4$l6T6Z=@m4SgKsw(8mMaghthW|F(=|7(qk zc2Wm|=sfrB{=qy-QkFt^P-09_LqZ@DEit~CwJdbCv`u%V`uF!R^`a1s-kY1vWTvNQ z&Y7M4wbj2U%Gb)SQqM<8i+fRY((Xmwh%b$^tDNJ+nmjQfkMl$YhvaU5OFrgFs&gJD zdSTYD6FoO~_p#1hY%04vFS5m`$o;6OR)%{j;sGB}+FQ<N*sZCYEpm=jr21bB8h$z{ zADz~9t=8J7kNQQy`>ge&>E9Zk0O<y4n!}*BKII$ZOtmo!-d>5X^x7O=Mg_q<wXPw= z#nRWhG7KQIiOC&WpYw&O*r$f$@sww!$s6*tA%y(r+FjLE5PR=sz0o$)51C<^M)PQR z&Dsxw)cbPK>A0k_rFY8NY&&*mHky9@hc2j~l3uAIODjsr6VnkT)I#o2gmXe|sk#WJ zOETnyo8v16(*ntn4TV6)QY#9+Rqj%Mr+f@-0*)5dAy}FgP+X&bf_jenj_xTIhC?=@ zyQFitWZ;gFbMlfa_!)6gGV_4=8<qA>=cl||!LSMOW%wwG{$Fl1{P|8r=agzW3cJzd zq5p{dg74<uXVm(SyWe+wdSmI2oIU4|0x+%2#dHIPlRa(7fX};EV!9%YZu9NSC<<`q z(6i+B6k=3MR5f}kQEC_&GBzhVjq5Y&g($L2W~6&eQ{)+y!u_Z`-BSpG9KLp!l!Q$7 zsV|eJ4Z;G~^gx+l@4M0BIHYT-Jq;)T?r}=g9UMoA?4Tr%S|Omd+jfU$=%4;e&u>8n zUAuVNY{5f9GyoU?(D#i!zik`y+cvXxki4!dv~8Pjl6jK4K<`NayQ6CMGk}ngl#-T_ zm6KOcR8m$^b-+P~9CpM}#~gRUNvE84##!fFaLHv?Ty@QLH{5i~ZFk&t&wUR(RP)GV zPdxR^b1%H~%4=`D_1*^`efGsy-+cGOPrv;3$6x>aSEpWsMopTvXw{}&hfZDkB4OO9 zF`JemY1vMstXa2V#a5(EMkcb6i+mKK7^NtiFm1-X1+(U&64lt70DEa(rh$Q@0hl&2 zHiyv8maOiHxw(m~AsMM9iEKWJxk)LBOc9w(0hz49nd!NS?4bq4nK}7+%mEph%mKxj dAZ@O0ZZ1&T6-v7S02$pr=l}o#00IC101q0*G;{y} literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 b/docs/katex/fonts/KaTeX_SansSerif-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4cf8f146967e1243ebfd1eab7ff9c596be939f2a GIT binary patch literal 15732 zcmV-)J&VG3Pew8T0RR9106la74gdfE0E83(06iH10RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Italic.ttf b/docs/katex/fonts/KaTeX_SansSerif-Italic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3dd767131a57981bb2e065f4a34010ff3f3ad45f GIT binary patch literal 30960 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 W^w{Xw*!uc)w0<2USaW!<9se6ZpCuvy literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Italic.woff b/docs/katex/fonts/KaTeX_SansSerif-Italic.woff new file mode 100644 index 0000000000000000000000000000000000000000..9da0dfe39632bb169458e6ed72c84803a6c058c1 GIT binary patch literal 17572 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 Z)y>TXO1na7HvshXLi7Lu00IC101r~0ithjb literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 b/docs/katex/fonts/KaTeX_SansSerif-Italic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ce19ae03d50fade531801d77634f35ed06f90681 GIT binary patch literal 15024 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 G0000sx++@$ literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Regular.ttf b/docs/katex/fonts/KaTeX_SansSerif-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..f117cd619e99bf1d030446c40c725a1e79c57b71 GIT binary patch literal 29812 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 X9Hqzl2L}4G*Z%BvfN;&>#diKLsZO8M literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Regular.woff b/docs/katex/fonts/KaTeX_SansSerif-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..6ed98780a7a46c0c459d2f455fff7ee01954d3f4 GIT binary patch literal 16228 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%0AmJ9j#Q5RPd_e^lGxNtoUKL<R4&P{=H(Q<2H zi#&|%>~XB5B#oqTlN5~XRAg~f#0gY{39KB<JnV5i4Y?f56nrt9gd_=^xg03}CKo3s Y!6e!R&Da4C4}uRrdnusrZvevo2MbI9v;Y7A literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_SansSerif-Regular.woff2 b/docs/katex/fonts/KaTeX_SansSerif-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..27611491a1fcae82b54021dcddf3de913573b065 GIT binary patch literal 13708 zcmV;7HFL^$Pew8T0RR9105yyN4gdfE0CeC005viI0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Script-Regular.ttf b/docs/katex/fonts/KaTeX_Script-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e6f34542e296e006bd7f5b313ec59b1e42f12d8c GIT binary patch literal 24620 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Script-Regular.woff b/docs/katex/fonts/KaTeX_Script-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..4a48e65f0de679fce0ca17c32f8b52bd3de33fca GIT binary patch literal 13428 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Script-Regular.woff2 b/docs/katex/fonts/KaTeX_Script-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b0aed195ca3be06a66c66919dfd66564882bdd81 GIT binary patch literal 12064 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 KZqM2T0002O@mFpD literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size1-Regular.ttf b/docs/katex/fonts/KaTeX_Size1-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..37faa0f9fe41ddb1c9a15725f8ad4856599193a1 GIT binary patch literal 12916 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size1-Regular.woff b/docs/katex/fonts/KaTeX_Size1-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..0832f7a468852ced3080993f4a826e8ef608befe GIT binary patch literal 6696 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size1-Regular.woff2 b/docs/katex/fonts/KaTeX_Size1-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..483e7b66e0f49b65dcc2110d44886220bb2c3e51 GIT binary patch literal 5592 zcmV;}6({O<Pew8T0RR9102SB(4gdfE05YHe02P4%0RR9100000000000000000000 z00006U;u;~2n-3A7ZC^wu~?=S0X7081A$BnXaEEt1&wD1gB%Qh8-gn%5w?T0k`Ylk zxf%TB@y2lEq{%9YGW@!hwf>3Nf5!6pP%fLTiz2g~4m^K^#y-jYgSPeW?Jf&Tl!8$U z7+461icOn$_MLG7Mq<@vdKo`kFFL<7=l#f#ARD&1{a#eJrMkBbS0%z=L3kX_;ZVe5 zRP_i4@c(G*XGIB~=7tBpGl+fBtf7*inYE&(sMA#^gZDqcIPCntFC3QpSIDcvdz~RQ zk?TxIQkT=rp?@c`BZR6+5}r)-aIaXUTORYu^yWTt+f?jOR8)y$)5HJiEZaXL>+O1i zz98zL3-(NPsx-}bWGNbr%?GlayqzVMeWU}j1NMf9JNpQ|8^)qT=(tZ&Ls6!@cSV(= zE7O!Ii+l{L-}W;Jb+?G;ZX~7W)wXb(Ak9)pfqVSA6Mq&!#4_9n96&r1CjemN_o49v z42gi}{ucrKAcb$ggEvro@2m$4_IHxhq*-;Q6lbvj8)lKbQ?n2Nxf8))2(T;g^w&%I z6F?dkB@ioGf|=%;?|I@9@q{E<rD~IEi|VfGzFMXZRBO}`YMuJLMxeP9^;HZJL&eZP zOil{0v=Y>)I3S@)c8gV?(eSqTG6vwkKeOBukG*Y!^}PS({Ac`6^ua$*7e8I})c(}= zlshpw@x<>FKTWtNf}d2rZ+#T|DCSYLG!f8S6Xrou5aXZ-K$YBx%3V;jp6kvnGHiDW zpTJJ@??bSS0vty|Cnu`Ir?jXcPC!K*C+X=w5Z^w9ete3Uc4*VQQmKd#+WqGR1Mj)b zocBN#V7E~Zh&ePMV4GC~0WHLtS5W>_uXV^io{!&rj^4a0m7@4n^jMM;W}wfUCROU@ zZh>}WfYgWewq5|vc(hVk)AfMQvtqL>HXp#rketj3Ne9~F7@%28zT5^98VgFhh2Zd{ zkCXsiM(DgxVADXsym>7u=M}^dv4Z_LfO~I-OuX(;g$!?W-KxIZ3(CKa`IYP5q-e>N zwY-g+?YuJ_SX=`KaCxH#B1S-4+$c=|bafD~Rpsw}COAn07qYg7fb)M;@Dc#7B`4ZU z9&MM!L(d%p$Kkq<Ixq`nA<9>lMKgU#P?|L^nx_^COG8jn2Vr+u(liC6S~t>lUIcPd zgfC+#wFe>d>}~1=BhneYGoP;e1`#UJa#G)o8pHq_2_c{C!kr!o2@!}&k%*dmB@ois zELZ=i;9WFN#LOygacTssCdJctu~MH=x;VS}*M3+srR{Zm`u37?eph_PmZO5?_}-GR zLhu=oShW^FXsE&~68XWU2~uC#Srn5{qg+`d{n^CZya`1e8xUct-@OSTr-))$Et0b{ zlgU+9fD<+X31s!?@b+Q*V67s~?;;Wj8Y7SZUjTPd2A{vBi*+z_3&maKy#e&!a2Ha8 zFDm%lXi#vBpu`0DVG{f?1p$}?ftZFM%!OdggAmMkqoEcos}ci0B^Lfq=uoUtGgna< z3Pmj2f(jF$#w2Jk1>u+j5txQZ%!Me-11)B(xCndDKzx2i+GuEfG0Y3)Y2w=MN23Gr zMtstc0BNd%v{<)g<Jp;KbevCTLZ7Ea<C27=a-3ouZzWF?h6I(SYc)dm-R6n@OG$Ta zE@)PiCbxj{7rgy=MhCW4jgDS#FhP5oy6pgV_yIK}p*^$Lkjz6lb%f{qDX2uSyrj0d zfTqE_9+x#!BR!qs%#Dx3k<ceQs5p7O!3;nNrKe6?9fmiSMJLi;G20H1I$o`+7AOhK zu5b@~zvXrONt`#kI@aSi+ov!U_VTEr5pryjVMz8VJ5NWEQNISkyf8?1i^Cz<flD$! zi;51bGYQJuq}y@^j}r*}>c%cLYj4|zn}cUA;b=#TD|exMU_z}4YSt&a`Ak~rfwU|r zRVQ$KDjeI}8Y}60fhR%IglT0(vopxLCq`nI1?92To_Pm>UPW+XpQ2T)c)SQlGFGyY zjRM(H&#_EL;>?kq=jo!pnmO&sf~ifOGkFsgZ)&T~ksITN>;t_T!Xi|f_1>)m%qHDy z8<1*+Ran(rS~2hPpxC0DN!S|=d!rVmX-^Q_k}Dm9$~!LTP?PJ$v<aCXY?`_2o^3f_ z_6a_@YCEH1rUAPqcstP9BEF-XsG^sqdl-(^3BA#j9Fp9wyg>!*rbsHy`2$kEP_Q&m zj!ms5Me|Hox^PgJDGGXdQwBXgfTN+Q7@>$Dpkj<r5(03dQjAbW5KuWrs0aaA(KL)O zogkn}j8GK<u%l{>P(u*V42&={1mHrmFv4ttfZQ0t69TZIT8vOf5KukF&W5<rw0PT% ze^aAbo6>}`p*ao84YEK>B3%et1L@EfNEhBMdqR6d26O~6pfivGU4abfZpeh5KqmAC zGNCV!IZc1ueug4eQA@GKj`A8t$?)Q#rDav|fbd@p004<UsBtk4z<mH<p5SdiQh*mg z7y=LT6M!hb5yXj;@!&B;;^@rm$CIUel~P{Xwxq~UOGUpz5fc*;X$}cMifeC8GHF<3 zf{qTGR?Y?rSOK5mvSN``tcwn&HX{>o1k^o=a0$h#5n~G^$%2*P?>m8t`T|)MNT<+o zUuud{rX0q3?R$q43(PZOSZckT#$bS2VeqLOI4pgE!^XO)ef~Y-RtVzOiwM%Ai^*#` zM+z22!UON`V6^&_<F3!cAu7IvSV0BjwH>a;J26gNf-wb>9Lw68_>QdWdrlCVPukti z0!dfrqb&HICpa0EPg>n6lpv=T5OqP?q!AJ%N33f*U;CI&q2jL3-2r<$IHAVc0T*M= ziphK0>yjObQp8beS)u<zg!*xxoqj@W|E6t?Vd+0}JWlBGw{DNJoX#(gS|E23k|EuD z$M`#=p;-&UuIsRuFPC#zEYDS%<5DbV1*dsP0V^UFpb}7@A9BB-gahug4PZvYp_wjl zTMJJ2q98p(TC4!5#12G;?3+krq|7PE?3{3_lkN{4KRAV#FbKJi-B}LEwz1w6NuOW+ zOm7D5X_|-MZVoXhL+rcomgQg?xG1(ELhv<!j-6^tUH&SrRIWBPsQiB?=eSBGTzz;o zMZJ#{Z{LY_lr^ybQ%e>~oN)h+k6jnMF6%h=SC%JwzICyRBBmB3c}`z)hm&?-X;#gM zK`bhe>3ItEkLS3A+Fy358B~aDQPx&CZ;;{;#dYS%ZXkOaLx{B0;)L()axnJ9>Hsfk zXP?C3<Bx&drT0M9$2raw+ky1ZRhfwY3c6+k552h!>EUajQg)z!59Rg^Tk6t!`EQbY zHpX38+PYzz)4|7ov3Azm>ikx&{sr$7LiWnT)j+n*gfZI)G1<CJ>)PsD(Hw;|G3ZzZ zWIhhMBTp5*88CB&t3bj}`cN^=wO;bIIB92VVQ9<3k#3(x_36*ZsyS*eIqzHF@P4D0 zIWLLGstz_HO15TtfhbRhs|1}=6F0lD!&9V8M&%)CvB640<5hLYijRw2_~xxlkg0P9 zLKh9D(L7q)dxi1Sn5zb~gmJQ%bsf{Ai-<7UQ8x<fW3JU&qKsgWMsR$wzMq{;PMtcf z2J9M9y{;M!?!I4(Ew#0ptX7F}Vu~3vrq@?^=k!AtkRr?Qu1H5k;e{&p{!=A1r$~>~ zvqn-kb8s(XypPw;R~H|r^FW@{7o^!?(peZYlFEz}v7o{nKdEm@WtxYN^Kc0-2i*Ie zm}d{Y=#%vEV=cX?Ax5m5<#VRaJ(?);@y=4F?uF4KA#`VZ(r0i%^7y)H0B!)u9o}Ag z0Kjy6WxfK0fFx!Y(tB76A=1p9zdF1#T>NU__LV$LWgj6Sm-@-?MihX6F7R8gvA11O zXE2T#c{~2TI7i>*4z3`n-S6$b!S^ojrpSt5g{pPX`oAv^Cv>|-qF%1=?sv`)4O6+> zm)9ffOlu1Dam^JlH~2(g`66?T2pA51b;f&Vv^!_-)Y6QTe}fU#m~V&R-io+wG8*e* zv}b5P9rTlF6(VuICphz?E_mKQ3R?Dnt2^Iq*`Fj7nGS6E?v4&2O?h4%<qt2+|HR!M zqCkSs(}cJ}Ebl&E5dYSc=l5`?e_v7HzyGu9u#mt4&YtI|yj2i?yjxDwpOjcQ9FZRQ zP<_GwmX~s(M<(w%!OKqF<?~%1W%^%5T5yF}gc|4liNs3I?me7>z>w;&&-T}ZW^4FJ z8D@s+J*DW8NoeI>n(oKN3kk&NfZQk?nW9o`FIzmr1UB9I<Yz6$ERiLNe#35I(rt-5 zPrgl%uUs9s8Z%_&E4trEwD5`7eVq1HdD#8hjn%Q$`)GZ<oT<631^vqtta1B2;e~nE z|BU_f`c8XKyFbplB$iH@4PU%yF4u=a&vPwIHW$74Z1u{oFT}EowmMZ7(+)UzD0bWN zpW-aeoTIJBx5avQ*peLV)<taWg|An(o73=(mu;RyaBzpk<W!w5q3A{Bznz1%CD8_U z&%Odutj*Gjo#fw9o-JeW9?~2e`L}ZeYg6~CR-sBOm6CNg7#-VEyZfIho$-z8jn}p9 zyW96FeDziCX`j?K(03)PP;Ebfl}T;y;=_p#TgZ<(!^~CBozZ@6eq;PKcwJS(B{g_> zyvAGO#iRcp`JXSP=|8dIyT<Pz>_-*QDwL{dr7Lx4kKMKw8k?iuf4%?UKOqe6DoCR@ z)n&0$(TzH}Ovh(bv2>a}%brHZs+jwP31)v(GWmPUE8P@x!Z&m-^lVi4hT$JSpDXhZ zO2oHBCnbvmu9^Hd<mfg$BtGPP{6wcbF7V&mRL}WBpo~jAA*^SXi;hc{_`J16=<?p= z?hVbII1r?yRZkcQ+6O0wQzlT#-*4Qq$M7G&4Ia;4w)uNq&y}Bm-}!&V=e1Vj1+Yz( zxr1ueU_fapYyE`OGZd3QpE5UjEVGf@Kr|~jf=WGo2!C4Xsjs(~{}}#iLPTKWct)W~ zmteQiWDsS)6c`)ln(U>?Ku(TiB%DM4KGT%PcmL_rETaiOUvjJb<pAYbpBHWveO~lg z03{ogE6>U`4b2H78)bExX=mx=^YSLS@}5SvB{JikRzu43(Zm19&no4EGAdw^F4O#J z^0}iBzgC}meCu1`&80tR`!IlB^ZpM(VIztR3s<n@&^rr+76zDVSNt_kfHS9}k{RM! zG87|Wq3?O|owi_6$#8l3p#ACE!g{_X7;JgxC%dE}Z-(2!IuKVb9G)qZ`3#nq4~t5J z#WJ^o4J?OdhAS%b^K1Qv>s#vlI^iccOxy7@S?eF#+bd_RmOtLpu0$xyzpbS|HLcWI zmP?zfW2<SuvQi%zcR}dFFhWx`u1qGXQ7%?52;pt?{F=%(n!0b1`Hbe1u2LhCalScp z@3l(xR7qF7vd`!AdQ(fpRKZMsRK2P!!o6veJ7T)pU0bf^a`UDLcartBs)|UZKg18c z`TC<zcFPvUD*IL_PhXU6WE_liRaNbb<`KW&qEn9bhrBuJJ9qXsTK28b@GBbi8;&>l z=IZ|L(lI2ZO-z+#t5<Q5>hpH)<5~Lps5tHxZ%L|fe@s=;P;PbhhN4pDdONj0!7W>) zS8hp6rX51ReD%_)>Elb5F2%5<XS~Yx(e^(K@r^htg?)^J`OxIu@#`8?e=l<^u$Q7< z!7R}|mXjmFWA70f^o9@ZT#4h7dic;^yc+o7P-<3QYhQ(Y$>zMQ)ZwzZLH^a-EGemJ zM0~AOTpLfMrKVW6RrB;4$KP~p8mw15uZZ8aOB(u);>eL0ycoqun5tcImxlVcO5U!G zhDER$PRid)1fMKDNK**JCU(D+#7B`41_=V;6NE%1LD3QsF{DBkO*qJbB?2TuRAeL= zNhl?xUQ{~@a|IV<dp3}Z>FiKpa(O)m!aaQb%@~n|OXu}AyY(bi3?zUcVk}pqKX+23 z&o<Hd4%;$};XX(TRV+elc5+gJE?U*coY7^4_Do~%!GaVuZY)7;)9jn@loIf~iaxp? z&X#2S#uJcV0sy?fZx`dO_P-ndzsT_?O96m~05hpfRSDJE^{&Rrf#RHk-4p!{yC7b| z-_7_AJL#O7ut7?C0j~9Xl#v51E*mR}#pIuH96<2jjeR~O`G~|G=-MH}htyG{kWw?t zf;sagbfzh=X*IhE-`7#1wLa;hN~_CM{924F``g7HG;>cJJtaFG%%{zB;{e<s)C=6$ zsC&Q(0<jpWK>%!J2UJbUB&GsN43UNeN;Cr342`5wr%`;Y&^SbPmD6fXloH`pO)M~t zmrKnkGK+*@mO0}^{KeOhGPY?1alECGTya#RM8>4X5txZ6r+rNnjbyeaX3D)%F7>f& zzJK$Ou8Y35-Hx|!jqsxlJEh1sSQVW_45q2fY(cZ9<4(5)1)lPT%Gvh|iW#0w#_ozl z>DTOOYG92zJ)6dkjKzngWQOhXebMXmIuqJ+j|*K^o5Am5Y%;giGsKp={8`a!Fc#o# zqo^l{ERv4n^B~Qenb^vh-T3_Yb2>hsHKuEsUt?(UUgu+kxn+5U&qmNE8Sh1Au03hc z5q(h;BEh~xvE|vOwwAD-zBImltMS7h^c7x9wd+{qDje|+fnBFA!+Nx6(yR|lt4=hQ zk)B?vcmX%S<Sd5<y>obnm@QM6Py?@Hy>VE1?)B5*8`#Fcgv`9aD?H%Hn)RrM?smfp zBviilO0$(|F{j(0R6|--KQE6SE}c5`^?8pj8cYct^6Qd@g&HD0K08M9X`<+%Lg~!u z>Dm{hiP8%yE@p7K41=w1opzp-ZPv>9kQ~|iKO{>u`Na&!+D2D+q6HD~ILHlZ!6#Va zL@!-@fHl=)4{BW2R37bT>TuX4#Q8dJRw{b|c$t72`QU|mV(dun<)V&0)C=`N{h}U+ zV3COikZhj9<QD0gv!H>>LLi7Me%N4OZ3AIR^W7|ufhbxomryNlIZKW37|vOhh<f{^ z;U5#tyeshplIvA>d{pLrS?EwCyktM|6BKbNoSPvuPIa;=qj(}C`!hm7NG?k@i&@-> zfcIe>Nu-d2G;)!L4D3ce?1EQNfI`@dA{3(pr6@x=`k*f=P>Fu%j{z8nK^Tl77>Z%Y zq6*dA{*D&C-m33EeWKpT^tE{F8{Qw4DHvyZR<5U~r*p8si*eb9B1Gb3t#=p~=1i^6 zJl$x}_ct*ns!6I{bp)K7*!8MGH7bc)CF;#;s%j0`Hgf2<N^zQ%)UB*~y=qX6stG6W z<aD_ls&jQND0qb{rIlld>#IhQ?=bu07|@3?fEf3{yt^F*04A<Zg5xC|SCv$bT2@tx mP>&-$$9RhXvm>{zyi%9nq1t00oE^c*My(qomR(N00{{S>s=fvQ literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size2-Regular.ttf b/docs/katex/fonts/KaTeX_Size2-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..cf326236c0e940c533606031140a116d7ab707ec GIT binary patch literal 12172 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 ND0_ALP~7f1{s)AlT$TU; literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size2-Regular.woff b/docs/katex/fonts/KaTeX_Size2-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..14f6485abb4e1483c0adf14e93c7d838efdcebfc GIT binary patch literal 6436 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size2-Regular.woff2 b/docs/katex/fonts/KaTeX_Size2-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..5ff7060676d81f040dafd77ac0ac9e68eef20767 GIT binary patch literal 5392 zcmV+r74PbIPew8T0RR9102L4b4gdfE057Nj02H_Y0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size3-Regular.ttf b/docs/katex/fonts/KaTeX_Size3-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ff7e2b90106baf6920ef84d26890d21617a4730a GIT binary patch literal 8120 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 Vqdh&{L<fu(V2^1R#}`xg{{euBHq-zB literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size3-Regular.woff b/docs/katex/fonts/KaTeX_Size3-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..d3626cef39b774137c67500ed0bdf3fc93391fdc GIT binary patch literal 4568 zcmY*bc|26#|Gr}wGS=+N$X1B5?@MDHlzojbL)Jk)c7^Pey%5S$M3FW7z8l7pED1H1 z5ekj$Bh7dEe80axe&@WNd+zhR-{+k7J+J$^&kZrr(*r;NxiQZJ41dQ<E&t_z@Be=@ zv5=Gj0O}WH)tC&VZkv6Six0|=tZkF|D*ymhkXUmjT!I7n0e~TntkIG|M+V^L=k5al zG#LOul>z|JXS1w}1b0*bc`ow@a+v0SU~=~kb0e>#L(VfKuRGCU{{Z6Qin<E`kWF%q z!e21O2JU!}MRGix%rB8441s`-JbVH}$r?YI+mqp~PIZ>=eb0rQ2dO1<rN7`t^QQWs zLdp9=GRXYzTz;Sidg_bvaV6JG)+qYOv5i~U&1S!Q0fFTDEXg_d$!Iexxhv!6?@FG_ zz)R*HWISB`psy0TP-W%vLU@E_95Xg5A#bR@P&enXyN#^sb#C~x*yOXQM7}&!u}4?7 z9SOFd^?znwzt1M=r1Sd~&1Fw5%r!aw6#Z9DqDo9E0fs{(N(Efy^5^O4uczfieU1BD zDKVWZK&QECsvOflUD~K`tOQrIJ3<5nYxVAp4}Y0qK-I-MV{qTs>an93cbK+K;3A!v zJG`qhT9Nh#QGCNEe1R5`w>;Y^rT4%(Vh!Ejz%z8evK6`1RTXM=aJWen5+*KMKj%k7 z>xVKM8E!|Uz1uOse3Q+quw>g~g;5E(qHiLuJMsJJ9nh;6!mTmlbDIixjfC+jz8)%L zUdxIqh=t9A)zp5cGvfDE`l&5_>>-ynL1@s5hI+mq!^ZziGXsk@`}nIgW3tr3Tq@f0 ziF0E%Fw_NaEZvDZad8RUN!Vn*-VgeOpOIR~+p+UKSX&{a6s$P<zQvrGQ4M1*3Tcbg zPxCgY<RNbi>~^X0N@>hDcXycxxZBK`#$!U2v9B6mzXz-6i;TbgWobogUOMW^C^8<0 zpAzDEh2|mUG`PfQ|A6E!o*WZctUs=fEsW?Tu7tF~${~!$yL6MBX<aFF)=I!^cB7Oj zdPH7zyfwu|Q(_^a`KYxkV)$swMBtZ5%lAos|I32U*)Cf4s`|ND7EHZA4S$I*tTc|l z#g;?)0N#qUgt@*jyq#^h^6eB<I8&VlI#2OPdbp`t4e>ZaurC5xKLXs>U82ewak<`1 zGv1Zw#gjJ~FJ5vQ@iw4$--a3-(o>US8-1?O+d!rF&cK?AO0_+A_SS+&@@iziE_H?u z#8E9DP?Gqb3TBpOwg36Wc=EnvXDY1_xz~KfqBolBX?M58=A+;V-j-?90T>(c%p%`% z_Qdlt_@*=%R>AG7fHM+zR?CV`GfEQ7#y&>Qrgh5MkEGaZu+d(1%2hll@@ir?3142U z2C}xpD?06DmKR=(Kv`IeXpR>z=?^@OxoJ!?1G74Rt~fG~jUfF}>3DmJc;VIU0UyEb z)O=43*7-EhEv?bJD(LoVm&k=x94x6UfpuD);|fGu#Nq`wdk8;yiH-Qt(?fbA`GnJz zH5k!qBcwm3WK>9C+)G5(Sk2PY-*QC9{$=rAilpmJdvx+p-S$%4<egxyaQ1?Ls`>ug z2e?gAN<dq5s!^Vb3Z(EjIUcHRvQEE=Xjt2{{+Pz>`WB~(-NKY&4X~wqZ51=3^Gl=l z_NBFB=_lR44a+JQ`G_k-<!RdCpU~XX!|6XBHO+~U9bM?Q`|ukd$MPG*F2D|s>>R3} zjN73nAQ`aNs&{WR<hWgU(8?DIQ%{`hy2B(H++D}7)bJ=0uYcgLPG~ydCF0NcX)ql9 zk?hVKb3zXa%IE9S>t7rH*xZ(2?eGrMvO3jSt&OtS)czn0tG>X+!`sH8b=4cCy$710 zQ01jGj&`lOpyhRcTNTIPE_-f~?`ar;Gg>m8T}*VqrG_tH%dOtOXJkiE-+b?dbIDe& zH+<=+fv|%?*^jf`lBRw`>SQ>5oG$6Jk)sj#L+5C%^`4#N>x>V5<kobwW*GY-iPSo; znby6MuHx02OVwyN57i|JR!5&16}t7S?v91=jaM0e;0RG}C_37Y<C#vjVXe71Sozq7 z`yY{B&u(WDP`#I&oriw55y>Fr&c(3g|43S{#5}wkB-8~Y{Ioq79DlAD%f}$c87Sbu zze&+1s^@vNH<^P?(*H3ByJQsQOG^5(C+SvyYVA%LRmGoOkZF1N&KvKu7A1O?+n2q6 zd+1GwB&dxcHv|Ae7hI5ck;n`;8YI#gppX*lF=Y^b-BKP)39vDN8pxkqfc<~n3`hhT zfDwQQVgzMSfXQ8g0!@)cF%5=-`M}a(9k2~J7F-7&1b+r^{Of6Cp9@K1K6heNI|QKM zzkUMD*XRB9sDDSI?QcV>a;ge4r2#1Zs{ht=GKd@iC{w!V=->+AX>dX^{M~?HA96yt zhXB*J2RwbtLg<X(OLXS_ZFth9xyy7ZaC&AqFZOqgK+#CS3J+6&wnr^dg+I+@289Gr z39JwZV5r2|`+v{sa5$VS@Hl9sCW&;<_8C~$G+GX*_yZyR8+W!FSBj3#j*fnt?8yiQ zYcs~Vh4&yan1LutZcfgxP^peE=o)otYnhailxPQO@*M#eLtcz+Ux5me@JGq5g`Enb z2S`#fjsN#-k;nvRE{Gn4$(8~y9djx{2ZP(CX#%2s!^J(tBgK<USkrM+Y;DvQ=QPaC z?XsAw@8PqbBob+|2gv#aj3R_aWI^l7Gfo{Os3qZqR6zPfsyW4xx_>^La60bRdl>dJ zvKt$uZ35@^-~shuAOlhgEEz~G8{iiOWo>PYm;z)Kq)IK~7<L`v3-JOpX$G}p*c~-E zp}?VqsNLTh-&w8auVEOjCFY|z!!--@4|bkssgHRNj}YV{i_lZP#LkX0x=3ziEa_<K z2xAa%?5}P4+S~wvFptj`u&y-le&l#wTE^k@kIQLi+`Trnd(+oOZCI-1-WMTe-`F{Q zLRW9p)xQ(`vL%kt_1t{#lKb&mk$8ea(!<-!3ri2q!`#&#e_DHe%wqDuw~$FvLYVJH z<Mlm<+g}=U9!Hxs_3oR}hYZ-P|Gp}*A|VikkzL)37miQBd*88cb`4SuvOrg1jTi04 zy#zit%xzQ%oV=Bn1u78G3!j28^e~DpSCPY3H#xs4Ju@e$fia|a{izjqO+e$i<|Q*@ z<!}z0A4a8(xusCwrn@XY{}2O}doz<un}p8pD5_m-Jgbl?E{m>WkgxlQ8p>|G7CgsX z^y69)g6;z5k;i#?zMarskBNo&70+@?WiO%-%hkvIInCNfxt~q`gl<tO$sJ&nUMqd# z@}bK!udW@zQ{WX0s}+LgxZ-k^zNt73U5mFzIONN@YMnpY^Li0ledLYZ4;y98HXP%* zJ-p!w))p*GaU2bj2=oXu48A#Ugxc7rTN=yBV&b$#+$l^mAGKKZ%=otdF0AnhYh!9! z=a|KFB2SS?u1)et<$CnT!{QrO-%awh#&s$Uq-9y^p2n+9=UmbKkuX>qerqMR;C+6^ zu$DPZ7)D_%;A(%9S}J-G9paZJej+K)o_7${O}+ES9A?p@_11#hTlXE`F>F{29W?qz z%W5?{&OK0H+GI0hxc45n`C|fZaze$+E*@3LF2dvUr+&wg!l*oR@om$j`>mBEHE5x3 zp{%YIKFnf9$5Bex0k3Nzr_)x($$Tnlk@EO=v)GW%ADn5=M50FX=wP`<Gj`BjgETB! z=tZJRAyEyHdK5t=`iz{qjud*C+~fhGmuZA#Ri;i6B$2TZZB{CMQ0py^<KOl+%p6j9 z;t#LSjD*si`5Z|b*wC4!r@f7v*!2n<ikzmX!tc)({8Dm1HLQH2>b{DvxW>|9EfcW4 z7o^>6*rABr7)Xpo_A&kT=u|l?cv3Xs64BhU`$AJsG4sY@S}*qM=q@oFOS!>E3_m-* z_4&~pDw6uLnfr5A)Aa0|iO9?i^)J(0m(iFa*sTMfVdVhsy`S90eNoFbaw~OjI`gRW zH&F~u$HD1hoF~iq41<rAvcn#><%Zk8yxNMh{#L=^BYJPyL#Oq6RS?H?q8A_hCenp4 zZ}TcpJ3xL^f&HSl495@4uf?$;GDbA&<?U}?>cw6E6(d?w8feLM%}mbNT)(>H<@dyn zzLL8$5}L~aD8lH@kk-ggC^e-~gfAvKlE#ZKiM#bXle@#0r%3av#8;9moH7;GPons| zCga`gFSxg7Ddx|2&Y$h5iq4q9)ZV4!C`8KXF!?;Yl3lYAE9U#`SKSmV4cb9%jM2xG za>A2t`6=wM&+%~jd=9aoCL7)yQ9AvB&>5W8^HBo+U>@RnK`F|!VH$-DDW{URQ~wZ> zxT_K>M@S&WTW_CRd=VkMZ;yJt7q|Vk**%fdMZiUsRE__+B>*Ds{>gfve>#R#Iy3x6 zu<TKkPM%Dk>XwT4H!fPWx9ye-r(VsjWskM_{tdo|PCty|)dVO_<%ZPFZ*5T2bv7xZ z&un9%z9h4~pxH~)xb1E~)%=H}N%O=)Gaa@>{wqf-pd@hy&6sc6nWZ2x?u;@JS3ZYs z4g~K3FNCmqd}Y<;Hhl$h<u-kJYH8^SPle2Q!ehT#O_hSKaepWSCFC}*c<xH&rdL!~ ziddz~_7mP|JB_v1@=3KEGPNGkv~Jmsok<l)wQRArZqc=F-5E=iav-ydty{FMTXrw7 zPfM0Jwhk9Mq{h}NPp+SoEp3CCy^WA$FD88L^%E3z77}uC!b64%c<FfQMyPnW=3gkt zNO@>_FtFYi0fWIJGRb+pt|hcC2;=}HF)b-|Ozb+(EWE>km!2*~f&n%nz|4O+6JDDc zjX+bck>=<nI(hP<+H5WE0J-|YJmh`gWV@yN>(QYLx5gTywNOly%cnaGA&hkp{vm;f z%FNcZY{de;Y^Jn98EdiL2UYY@Nd)+^6+0ZNgqpfY^7u$A*5H}SPi%<hu^|3*-(^>R z=gD|+Rp%`7*ZE{P)yo~=+v+W-4n8A?fB{#1$UxW$iPtn}YPoK97q!|SS!N#b-?MJ` zDS{(mYQEZ)^c4<7^Ak0WZN?A=qvjcmhA9`6wYB&!4sv$qHKqhMW|g&@9)7|p`SC3o zOtjxoSd0jZ*#Ek{T_0Yxqy`PlQh4)JxP09Bqw!`;=W`A@BN&I+u$+WB%lTE`5Zs`M z+^Dza`OHM<o=Ux9c*u>Qd*R{Z`&Pb{z4bJ)uJZ2RbK~welz8}FE;4Pt6V@ytD*b4S zZ&T(^gOQ3#<&OmkZ%pBKawdL%=$RdkT?6W>I98^V=EWh?<~BK-?|u*|(AEI`+WGj= ztLI~<<C9yzq-UC(FA9c;aX5}z52lFIa!m&+l^Ki->ZyIDaVGw=-sH(6eRlh1u#bc9 zCI2fs10RbY>M9((l8CGS<aOHM?@AbI|J}JhcUMZ|H#M$S=aG%2U#^fgutFoo?H`<h zQPH(9vsYN5psQJ+h!ZpKl~BsQCoyJPr+I+bP-wd}A<~zs<1*WrZux;OC4MHI5=Yb3 z--%c5xS%m4Z+piEgSw33zfBcWm`!-crKh?Z%@8Vg?9lb5jSBmz-}8z=!qk^@?Cl|^ zqB7!(mvgSQQMybd5oIT2Q-}s)?S^s0jlHnS`Um-6@SK^V1xLCn(lr5gvAQOgBz0Qi zvb{7;uIjxIT#-fnc3ijoP*QA%i`KN)z3yHSu)o-ly`1;$Y4CuZ`he}k1;g20mGom? z=MVR4mJ+6_3L^4v*4ko&`|Q;)HZ28twnp1)hX24BGYOj3Vl9`kLbD;_%#=JvlQ+ti z`gFbzlg#&F`;Tn5{=UOL27{nJIMa*PepnZ4bUT`(rQHU8?SOI|Iim4ihL*}jBSaV_ zZwAE0ByecxnCj989Y~k<u}FF?5Q+;JRJL9{=exgEf(u)$S>4lHTDDZ%>Z)#<{*15X sAw9i&wUFaFjbhw$$c9Q7s^n*2yU$9R-!b&lnV>)UhZO{XNF;#qKlScO&;S4c literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size3-Regular.woff2 b/docs/katex/fonts/KaTeX_Size3-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e45ca49db8c66ca43ce41bd15a219db59b0c9350 GIT binary patch literal 3728 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size4-Regular.ttf b/docs/katex/fonts/KaTeX_Size4-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3034091cdb7b1c98799073e1770eb57c24747d70 GIT binary patch literal 11016 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 be%&un-LKZRHeD~MJ}%HU#E9apa_0X72#Z!Y literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size4-Regular.woff b/docs/katex/fonts/KaTeX_Size4-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..93c57a6f97f529f7cbf7ff49678c423a54aa34c0 GIT binary patch literal 6184 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 A?f?J) literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Size4-Regular.woff2 b/docs/katex/fonts/KaTeX_Size4-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..53b65afcff022dc8512e61cb25f7075715de8f7c GIT binary patch literal 5028 zcmV;V6I<+ePew8T0RR91028DD4gdfE04qQM0259C0RR9100000000000000000000 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Typewriter-Regular.ttf b/docs/katex/fonts/KaTeX_Typewriter-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2fd85294ab68105c5ae44fd65332fce36c49f8cd GIT binary patch literal 35924 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%0AnSGBJ}5nw;K}{-J%^|HlSG_ z9?WAk3d`iH&`h#P8_KpBz~O9%?YZd=HQE;c;6=Tq1CS+R>)7ngQQw(`$zon*BSVlf zhI^A~zL1X`ik)7B#X5eRX_He}JW&6r+uXHY%+IP1-6!cunCqisZsa|?l#!9`m<xO* zKXJdZ>p(!`u@U*|zll03a$CYr+4SihO<-(rqKWX5BElAL_pWH&D_vertKva<BEK2A zN61w|Qkpcac6R5zo{tSKux{ZwKnf3-RVbDb-sK*!qV4M99&E7Yo@HUf2Ff!B$Tfq5 zqZx5;Q74C&TQNrF?+as5z@Ebuin2bt8nv%LLIYrPRterNBt67-drSAetZNTK8GifQ zQX|gf`n|XU*$imC=RL40gvixr+4ljNhJf{skcbN1m{dy&6KP1CB@AIKgav&BG(j>) zz0@p_tBVm^&`>mpA1#-Ij)0#sc3=>_$SVS7VHgBRci@Br)8H>HD;BFaMI+>Jc(6L8 zAvWt+S|FqxIYRjx2)qsqn2`Z+A<EidPm)k!?|DKFz)7kE<R{+4G?++Svql8<@yBQP zJ-#DL!-ugvi?M79i|t)U4pd4Z<PwuFk9;oLMP#cOGN`4yb{28Rs0M~{;`pAG_AFM0 z8%-y@Ze0;J+2###2yXQ7II;;lp8weROxzJlEai2;@6k1kIL}n`T8MaPPc;Exz**=D zBqu<H=`S_0s-S$^A@W6Kva*s!iU;_I91aFFmku2V=LwCAEA<JU8JtLpSPTJWOr~mB z#0PcFjH7nR>rH6vjGtCQBw;17Smrn3SaxO(#w6V%A=ksatpKj0O0^GEkhDT=z=BOb zWAAyN?8HJWnm>A1MAIZWdEV{Onc+No`+Eq{86slHvFA|j)>a-nn$M96ychjtdno@y zg!_;d{)E*jTw99e&eMkuuI^pfH921EA1`6K7o_FYi5&n`ge$;`9z(Uw&72vg2z?iW zx{GxJXgTED+q6Jwn8bpq@)QOsd{l&nuqBeHYnX2~!tv2urdw-y<S^Ns32kgxO9j+J zM|zxuU_Cs-Qn41A(%LN_u-rjs&7-B$iWc;kA8;Ebw^vs8>|v?G{C4prH$C%^dW%fT zllN~&cz4$>v69=3fQF&F$>Kxp44V%*hKCvDXzL0O^v2+~AWFNPa){DUA=kJPgfIrE zjUn5CFX)Dv`mWQjP_CKNeRWV9xz1sM-#}bIEDbCJ?g*YE1ITeQL$OSE;Z4x5jeZ0& za`%}aSBod>erIgK7mq<k_lje5e*g<P%1f$bOt_HL;IrO$4PKELQ!a^>-7xotu?|ow z0DVKNN98DEukKWB7PDOm1NXTacKk+8aSnc{40#qwU}Z7*WoXG3M8ZYi*6qr<s6JK- zC*!7s8B(SVa2F=zfM;I@eCCCBU(0`(?&d#`fBDw=4`5K&=RZ*5Y60&9ic+e?&b)>j z+Ctv(W;f+OT;Hy%G|db16R@J*Qi$2rqa05m*sfax1+1axC!$&%ER{ekfcvhy0uZc3 z8>eJUIef+XY;RE%O4d_?v23Ac{x%iykdmW{#-;nJUpI_yaUx=8ipLB<r3!!7GJ$85 zwKqJBtUY8wtEy9TI3!InUU{Y(&tj!s+^jdcIS`{*Rqe~j4VV*N()Nvy7IG1+!a3`4 z!GT0N0$HGWK~zvVCtQT@1beKdf-o95z%h!Zkzs;W53!Ulz)8s`-L_z@)b#AlO?gml zWT+pD=?4AasdH?O2i15z5L{>F(vWDc0c&#s;;F<~Ta-kCFbe3MGhHM$0boe5ChOH= zABv-BCR0h?8$x)-yDB$D6jme2R&2_vUWtX?MyiyK9L~f8UWC;ux?X{&$W3lv_(^5o zlH=TYC2c@4mmKgXC=MJjBdwl0k8mj#8ZnIYxijiuL{TFH`dw!;81^@iSJ4O983I<} zGZb0it26^9*DVPVvKNFTQSK*A@f)<jpKR1z;yYroD^)c}#W=!9cOF&*tY|PZ>A)Hu zN)x{8p>%>Amz0=-tU9bo1bq$DdWJ)r)`TUOj4U14HCOJB^<}ZVft^x8)KA&AqjW&J zDss?CQ-x?IyxUGB90gn<mys35p-pCy%`BQra<-hDoJD50>!6g3iKu0Dh_R%!AK4FL z=-xyn;noP8_rZq`aLO{^DS7Wp0LH5L5rEVfsN`OgE20pcsY#y(fP#?}aLKXc!w@tq zGNBuB<?LPhKm-a!WKcbGPL3OT$TTQ(<e2a%dzvXiQ5bFyU@aUW2a1Q%kMJfthP+x% z!GH&Y1B+(uz-XZ~ngaw7ZR2_q>5yZDF0?GG#$^iW@OeH!VALNwlzhT1dxw!4>_EkJ zcvm&(4g~lWm+Yh~Ge`>R3s!v|<iUk^A+8<kolL<4Z~+S8d*E+&K@x`BA@Ud6%X7O? z-4JbDTQ7!YfW8YMNA?9Q3|zo@=|Tb;PBCd|txtu0w2GuFk{Z{nq}V|Dn===R4f><> z+K4q6CojnV{4YMu{sGXw7k=i*PQMFs!U^zTuviBr4<&YcZ5(^7fVQ0%z*8=s9?g^^ zSlG1^_P-G8dB$QMZ&VyiWF!7SB8x>LTz+57hiZM{?N@8;DMVYVLaCik6PLk!3+!QF zzpY6GMAA2_LVqG&k&$i_wks}(KS0YWl?Nq`&3yiQrFfYA{8OlZ!V+tR*>tfOb2xQq z?LH;wMIIk>j+?q@J(!sIyrOvBA(-6Ach&tq%I4Nw*}UWhBXd|zIZ*XQ{rZxNG(|fy z0-2N$;5i0%QtlXjle^f5oL68c1x8@6Q#2$<o@1JP?lR+QFj3`>_{Z*BhdoLZ&%H&k z137^d>%jMi1*~?Jg`GbLuK~<DFbO1}3s?t*EHumySJQb|I!5mP{z}>k_>k%`%!c`T zUH{II#@Qb;ka6<Ij9>hJBR}je_6ha@aTjnr+O}Z3!PWsSi%7u}#d{(}e>YPNISRI4 zfOAx}8?=AeVQ90{)6<$;lign9m4VkNUYFQ%M8e^)E~OlnA`c8oie;pLH+!JzeG*^t z2w26Kiwb)MRUjRfPz7##1W!Y<YYWTQ45kg2gN2kQ-eVvJ!@jS0{8^}QiqDyF;GMsR zDyX+WBm@Oy4&ptu8Y-=YDqc&O;%*J%Ld5;Q;SG9?*D*s1ST3yFzngut;6wDZ4-p8T z6Y&cxOc?$s-ntiW4GE54N5Af=G3u4g!MIG}-Vc%*j|MzUmwo}c(_Y8!y^;%N;)K_N z?+045uYd|Dq@dFq53qk`zk@d>1;|S%2;kaxVnqv{4#<$(h<rU&NU|i>!^B#8#I@C` z*ab3Wdhxt}qIC9_5(`Aya~{v!Tr?02#Z}22cw4{?38ji>a^axqaG1epz#}2s64lAF z3mK08BUyl=@IRLi9z$-^(|}Y_*xz<slYMsTUKos}D`&V1kNbwZ>pY{z2%_-LYb|7W z8jSxpB%`?boRabX*Jf_kW_lIZ!-D$ZIAg=4qYzG$X2uE0F4*ZQx1g$<E2IDTvpcJG zz6`e*+ym!tk83y8MPIiL303sNsz*-f^+nO(Z+VdAUyBcd@0IY^QE~|^DU6;WC4qvz zVJkq2V}?xeK&uH+i<EA3cMP5TE67R>88a%}3h(uaTc<_ednKa*4Udg3$^XnM_&X{P zqe!2Wul%9cp(7>3;T6YZ?;{dCa^B8=H^kR}5w4``YG@CD)4%`@VJSZjpPprZtzfoW zNlD9yQ<5i7kI>*j<ot*iUsI3+z7$)D%~5~J@qc4QZFIYua<E<=hypk$pL|*ghC+zN z7<$BXV-+$JwPXAQ3c!ukw#KzmO3PKK)nfapc=Ek&gQ4skK3omYpY`BRsn{nvxAVdO zu_nQT!UAvr>k|y~<psdbuqJvl00_f|w2>kZFfFdQ+FlLZ!PsEuV<PwpTU1VJk6Eiz z@rFOZ#wR2_fNTa0buHrLI78a3-+C8P+oC~NKyEPBkOQt@RD*NKF4G`862V8*(|R;{ z>)*DvphHkyE&eD5rb}e?!~TCOt4u9!`487|PMNOAQ<sD+M)9}cP5oG3<s4@0)M{hE zEnvo2O2UfpAueRdhG=AM@Nb)OUHey}QSpde3pp*cUJv#*CBTQ%$kP&9W)_|a%s&^j z2r|&nTg@i>V@apHO^xdRwzd}zl{754Y{rYFq3}w$<flKE;V*?R?Kpfi>}R>{#hlN0 zu<j0OFVCQ8Ns(2j(*aus*%Cfo$IA0$Jj1nyg%Y=(3OZ3UbP6Rz&LHX#cAUJMc}yo? z?-?uoz?R0^1G$3i=00U;vg}zvh~^oqI?+6LT>{tT(O+BAWJhT`JXT!|7P{h&>o+W2 z@|C~*A6lp6=0!=^r><QiSzW;xzJW2!VGMPR^Q&$D#f@w$#1r^=K?lcXlGMZ~cx;IN zBu}YS5lu+-M7}&|J+qUE{nIXQE`Ut_mwXTQ;I=no>mf@Nf8nj7IESj)>v)-XceIrh z-L5a?QXY+aG9Cl)4HFBwKdZ?9M0R5dJ^vO~|CvG0^StL^3FJW_M^~30luBd?;s-$o z(30&t1pPJOdM%guEogV-Pkc*b2o-%|c+c3fAB8ALyZ6CgiJ~9Yq~wm@rQD8&s29|L zVqz`oQQ!1{c>m&z98%A&)gy-J4&x8acu(e^eD}_zk9R*QzW`uxjaVMHHL?t>kpxPg z{W>6iKsYVj2f8}Daq7^p3c8}DRCvLCFA>KagINz7l1wcPeq-d=QAILO)3LpvSoQ{p zg#TT-A@vWV%a@U3+snw~F$KWE{zDJMN*}i{N0F@eEKkWvr9}8o#PK`bNEALjfkRQ8 z=66128SW{ZLCxbg@0`G)D5jEAVsHZWwEl62ES|+C1||cVpRd@`%hov!Nib6}ygt2e z22$blcHRyw9lAH&ulu}4-!$E0-<pg%oGynW=8ztiCa0nyH;xHA;htmP2CPnFj+afs zPRubvl>`&|3lrBnM6py7((GEm0dmK8r3IgULID`&H(~)v5B>uwj0g4=plyqR2>faB z7*f+VPSe!Kkqw6>tl&D&7Q#9LZUOkmLYV_tfnq0WD8_+I2(}sXyRcOGms2@0?id*L zIo|LFJE_Y?3-*RLID8|6j<}dhA&D!5=#=UW&6XBJ(#B52*^okurP-(i-9Kb{0f`JC z@f~10T*DvEhy)4p==K;VMu(w;!|u2Q%pk1bJ~eJL0|$6JkSz*vh;RXtQkh#HqhzG^ z;-i(9o{Yx#oRjrx91)se!B1=BqwIXo9*-*eV9Ps<_K3^fz@Pas9EpI>*S+gQxX_5^ zLqUflJ_0Al&#k{)@jh}W60YAhn*wN^1$)(etl^@#WPf^Pu(nh#q*|+nI5+I}rwZkz zf$Cz`556p_zG6uU6;KM6P0c<u!|jUy)fGDbtNtR+pRoOG0{VnWRx8y6avuaw{lOoI zqk5$6$}yW!*oqXU|4&d~;FDD>e#Lb{auSQvSQ<=syEaZD*h3mj<m7F@uNqb?{;KfL z$Z~<sLinW+UoV#%(_$0V=-`kX-H;$jG8-3ht<@{)V_((XTR-XLOO3#p>UM%{N~{g1 znrL867RH|Mmo?7^js>Eo%Sme@z|krPGlg$`D-a1ebqb=wnP_;I$@hhStR|cVr=-8+ zht~9${6-pMP$0(4v-ZCqza1=O{5#9vr+yfqv)_f+&~>`|D&Zw4jiP@dsBBpHNgO@o zd^5ko3*@Ls4k>R>u>VcmCw^P{fb>ndUp}uCm3x)<D$gk2R3qx$>ThbhwQuXEuq^Rg zjDK)E<_tJjoUeEODVF#>>-rn_^W0mW$30u#qrTgG|LT7-a4_)Bz_Y=}LvrX{=Aij< z^ZVA@!jDIWB2Pt!qaTPZ#{N8hSHhDxns`Ox<4Gx*P2QDSPJJhRPbQVQl%35bbN@N_ zkNJxQZ{fCLtN4x5K<P8(cKK_Sm-YGjez9Nb|4{XC^=q}C9T*(AZQw5k2M6!Q&$|Y{ zI8+<DZ|Jkb!QtJ*4-fz1@G~RoNMYpU$Zw4<j{eN(XUCSu-d~@rzoY&yjiZgfZC-5t zJ;HPkwBFVFX6s+Z-Q#f%E5PfGbAR~GJH69C5S-Fq5sbd{)LWr0+WV{DzZw^_@RVc0 z4tpkX^sn&q;U76YL^t0)gZ*RJ3fLyG<*_BOJ&ElJY{#%IVk=?$5Vi?ypTI_K`>~zD zR>xMu1|@+X4|LCIKZuRm)BEUIdJpYqvC%X1EkCvjHXpViHVYfIqx0R^sC@ui6q^fM z2-`R|YD;@+M}3Hi?yFa@r|Z;?-c4<Xv3&s>^|=q*1~z(U2OHg!vCU&kch3n1G#0u} z-={WiY-4!;_pztz^gY_=utl&@JL(@@qjMiNdI$Q%+l*rW4QwOW2&NaX(HzkJX>4!D z*83Lq?=Eb6u>BskN3k8iMt!EX^gO+XzDIo}7^JXevC(%c*a&_!)*`kHwhlI$gFnHB ziZf2vM2np6#E)K$3nyrFSHB~|)*u|&@$Xge$hq0i5@m{)#NWo9*V}t=sS!BtcKmyp zAbig!eCNBpO9#&{ofX<b=6hGA%++yx<D<eiGlK9lrzy(%B(w<ubWY#HIp46NGLGnP znUeQNfUj2;K8yV@+R0e^@TJ1Lg!c&@;bUw>Y>EG;_$l$v#J`Y!PI`;<cIlncyQFtZ zPe||2L^Gp>{P(V2y^1F@*FEu!_<xCC68~I!(~c+JBfT#Z;ZIz_BVWJz$*aG8_35i0 zy4u_tx$^ESzkKBvuKfJ7Uw!s3p8fK(|Ml4~J^O`c|Mc0<fA#!V_y756->bU4#b<u} ze<)(wevCu!Itb!D7&j_uRZTabiMc&qi1}a$`3{k2ES^ZF(wS@ynZBiRrLVtQ8yFlK z9vL00Bj;g!VsdJFW_GT<YkpyIX?bP$p1rGU`}Q9=c<Atv+m7CT?D+bLlc(-DedosJ zT|ct7=dQKj-1*w6fWJh#;~!0-{qH3&?HxVG{_cAo5*~Q);`xV#pAayGwM<7idVFa; zlUe<k;5oF~QIDKB-l@ksmGzDDnM+5Hcf{i6Z|GoOXU^pBif6MOVZ9?P<QJa;gcdgD z2Rf|Q$!we-=!mt<*-Yo-2Rm}<#8Vagv4^EIONUP$&*ro7OUE;vg9ndiJMHy&rZY}Q z<Lm30%XXv9vz-bqb<Z-LVY)v|EkAzncm|!gw3(s5{IY>78M^17qb40SH{u(pjf!_z zb$vbG5e^<dx4u5mk!qQxOh+zmqC?8U!Q&kzKi^UF^XUG1hiwdW<XRs6%ACEd+%=z} zJNC!vH|{T;=}7%qTwlms%3Q*`FApn44C~PGjf3&c!|TWKH*_+c_L1Yb6{kUUzdX=U zY8`E%`V?3_2aSr;{CplD&d+al#JkRS*crUPqx27Sv|5Hb>0CJTF<H0^P0-Eu#yU0G zSmd45YfouTVPR>$Kf4WVqjo*;9rjyT6<t`sh&M7zm-3r55gbxNoS@ms#L>xKFFR5( zzqx3C!ucGJcM5n44{r}`=R+>Snf&}yPKUH~JR8qv*ZZ>r9e3@rC@yu*ZY~aVJT(j; zlj*n?_R_d;ke^@gxasUL&fGW~=y>rO9|uVW5IBQwcRULlnM)g)jt4*+==f@@w;jJM zpIuxpblm6iFBs_fYpaKjuO6{4#<RE{<kthW%Yt{|=<&;5FXnG^zT>SDk^*(-FT3cM z8^1ct!c0iTgU2ru<^ovrmoUBb+5T)EkM)k@_I-K;xKDT2F_IPZe`N!cc->Sz#{^zR z7$Of4FLZ?2r{Hel*buA<_+!gUw;k_z^YfXdjuS}fz=t>HGaLB)(>@<V5M+M-(#GY0 zTJ1cq8qWbtA&f0p9q5>~%Z&CG;7t2)?XpDsNbRyr`)KX5Li<?lvP%1S?XpJuMD4Op z`(*91LHj=ZHLDK0W1!Q|4;~umRQbWf1D#Y&=(wx@fj*|u#}wY3K_6+KMIULOLmz3M zM;~clKp$yeL?3BiLLX^gMjvTkK_6*ft7WD+2@KQ#8sA1{fxtxbxru!TfBto#Gg#{k zR67Hpq#+>j3b5^#q?F$r&l4N_p)G()1D)aR>0wr9sQ<FU%%$UCLo^8^J0WxP?a^AM z!TURgc5LZpiUy;&W%ua1V10zM>c!do_~lV%VnlU}A058sBO5^M&GCUwqc#|x9_TcG zR8t_~88mNUN(8Hz8O*E@@dA{4E?rv5uYg$`2U`bQ1VuNo_<ac99|xDQKp^<fn{<qY z>bXmU`AlZ|65cg&?RI9+en&^fJJBlB*&upvA3FYFF(YN-9~MheY<-@1pbl<|@8OI2 zWl$ex^LkPO-LtCA?8SwRv-ys+uz412NL<*A<9K5ow0q+-o9Gf4bAEYqJf6oFmN72u z_*d}0TYLl^A!aFq0RZ+25DKjQhMwS!n|Qd$JAvPjG{ohueG3>dNifLZ>q@B$gZwl= zGsQ1=bP#wZvz%X{kI`&RbCA&hx)2p^J3g41hHRuBcQ4{)*FdfoakdAywxzE<Q8y#k zM!q{U=;J_VrrU#s-qdW6c)oFny{Vk7<uii>wPi5o>Gi?OgDeCBoZG&P*}QhSy?vRt z?mc>4n_acec(r%ux|hz^Iuq4Pz#&4GOCZaeK8$G@><r@j3mja8fIa9Ea^*o4gCG*S zkBeX$;Njbt+8ghWM#Ys|A;y0M`v`8tU#9cp@$8QI&aQX+xJ1C{0d<+ipC!ms7rW!w z9`Xu?Y}&*LX%9LK4tDCG#of<weGhmp3k5q3wAowhw6I$xs4W4mnPrHz9-P){gpQq6 zfOTK(DM47o!G0VtIyg{!3agIb;2;k8l|$5K30DqN8#*{bZRp@OYC{J{sm(4N+)i!i z;25=`gX7eO4%Vs70uD}48#*{iZRp?>wV{JMsLe7CPE#8?xRct@!3MRVgH38PkAu6Y z4IP}JHgs^7+R(wdT4!<_%;)K(GmFc2^P@Q&UEuhH%Wa(8Q|nA^xBdw_;jQoGN7VX0 zend_0uXU!kn?67%yy=7dh?-vHN7VG8T4!dv>BDrwn?AyisOh8ph?+jH_F)65ZC&MQ zzS_~xb)^5NwQJjLqX@$@cI+e$IZ*|K05L#Bo06>8PEZb%Qm2lqD5i;Hw@rmmnXISY zq4s*&UB}5CQdKE8RJ;KaSLKdSQK~!vJV8O6)Cb@NxC!6vPN-vAQV_ec=il)^|7rf+ z9nayP@wM&4SI!h6XpDcij)n@ojVE)|{C4+9a`9_KEI6%zy8{Tv7C~cJT@R>taUcKR zDCq~>x42KvMR1(t_r~dN39ZZBjlFm^zlWX1N*nuqpI}1Sxm^oQETEtfef91jLTjJC zPY-T=)$=Ld7WutIH6Om3y1yz|b&H^5F$+_kEzHHCSkHw0pkof=Ay(=AgFg;lQrF?} z936rn(NRJY!774&NyAu#LAZwF7=N)f+=J2C&orFCJghq!PIlxE(mC>t&L6_YzrSmE zq=WuwNFV=Xd-}ou3oHVOV2c0bUurl;hGO4qI8N4L4>a6E%=k$SV`Jg?6%8jl@&{vX z{9~OzL}n5XH9XQme^k*w-*k68S+fIn%9>zV)105mni-Sjg)eIkD_c@HmY8JAj%B2} zkux(2d5*2v++jJ3S4CrIg;&JJwaShw+MW!Aw=Qa}I`;}qCnz+%nqXPOWDBetZA#-k zDgUzYeA#rEX_&@Lw;WpN$;V2~PMvAD+eU*2_D6i%fDC6Ro=Ov4@CKCQk#14p$y8eg zHd`0I@U}#iDb?6T-VlFN%1EUuw)7*6a`Sr7=AK~4s7p&YKFYS7s_<A~3szn(vQ<|& z5ppp?m}G}~XABHTgv4}N#<#ev^Ub<op*WlsawW!tg_Iox?m{~4Tb^_S-|%H!$(>#; z6rcPHhDV!->$`(VR+7}<?g3$NdkX_$3|)gJ&Uth@D<~6ox#Th2?1#7xFv`G`$Z?Pg zgj8bKIz_<-NgYc$gDr{=DbJzg3b=EWW+Vp+99>a@G$7du;0oXk1VXOh+6CMW<qZIN z2<ssrc?-jJ99ISC<3J0QddL@`mxVm4yJ#Wg^3T+!y67Fpy&L~>D3h{~l0$V_E8Yfl z{dbmDwtQM!s~S54o2b)(%{cV8(WS!4Hm)N(oJEcPe|2({;|*PN{7AiZa-Ib5Hf*Bg zVxfio&`w+Ut|I5K))=`6sT*Of9cOvvV<~uq4Z8jj8fEBw9lYA1M<@@7FzQH^pE|&g z__hEmKl6a2WK+0h_;3;FYUpXI4j)NZJVHU$QFnc3U~%>7l<X*92C262k)g+K;#+Zj zWE~!I%;A-!FavmP3X%mlUBGFaKD1ExtBX7zyp*@q|ElRUBriaN|565>Md5L96F1G{ zj$6p2z{0u>^!_4%Bs$~zA>aV!Uk{?kU>NVZQN-duff<Wq7@2t<@5dJqq4`Dh+rEt5 zy<UM*uaeiu>tr0`$8V66aNP;aVtEq}k142-#?6ue?`hmHZ($$VS;W_xhuUw$!QVmb z{CClm$YBl2dE6XJQ1b%j054+>%nB5K56-iSaN{L7SQ(CS36*mNkBzGsA9^1<-F$$N z>TBdftRKUqVse|@B)^gew1@ma9%9B`5=(x5B0rN~$dBYV+DGotewv~K^cgxxhv+aJ zA)k|b<XbvQpQR_T<Zi6D<w(<<EktR~4AboNY?Pj#*J)0ti-`-o(cp=SEdrh_^2TPB z$2MeaO(x2+*5JLDTs*OxPJGRl@ikv28`5b7LY2<WWT#cSR4C+ix};MK%Ar0e`n8Au E1ndY_VgLXD literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Typewriter-Regular.woff b/docs/katex/fonts/KaTeX_Typewriter-Regular.woff new file mode 100644 index 0000000000000000000000000000000000000000..e90fa2bc7ff9e2f81e57a567be2a8620ef9c8d27 GIT binary patch literal 20260 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 literal 0 HcmV?d00001 diff --git a/docs/katex/fonts/KaTeX_Typewriter-Regular.woff2 b/docs/katex/fonts/KaTeX_Typewriter-Regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e40ab151617cb25fce5b4e739aab4f44ff3a652d GIT binary patch literal 17272 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 X7IHyB_z7@g-T~&)gHWZ(YH|VqvZ`!^ literal 0 HcmV?d00001 diff --git a/docs/katex/katex.min.css b/docs/katex/katex.min.css new file mode 100644 index 00000000..c0cd1451 --- /dev/null +++ b/docs/katex/katex.min.css @@ -0,0 +1 @@ +@font-face{font-family:KaTeX_AMS;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Caligraphic;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Fraktur;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:KaTeX_Main;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype");font-weight:700;font-style:italic}@font-face{font-family:KaTeX_Math;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype");font-weight:700;font-style:normal}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype");font-weight:400;font-style:italic}@font-face{font-family:"KaTeX_SansSerif";src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Script;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size1;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size2;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size3;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Size4;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype");font-weight:400;font-style:normal}@font-face{font-family:KaTeX_Typewriter;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype");font-weight:400;font-style:normal}.katex{font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0;text-rendering:auto}.katex *{-ms-high-contrast-adjust:none!important}.katex .katex-version:after{content:"0.11.1"}.katex .katex-mathml{position:absolute;clip:rect(1px,1px,1px,1px);padding:0;border:0;height:1px;width:1px;overflow:hidden}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathdefault{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-weight:700;font-style:italic}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;vertical-align:bottom;position:relative}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;vertical-align:bottom;font-size:1px;width:2px;min-width:2px}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{width:0;position:relative}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{display:inline-block;border:0 solid;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{display:inline-block;width:100%;border-bottom-style:solid}.katex .hdashline{display:inline-block;width:100%;border-bottom-style:dashed}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .op-limits>.vlist-t{text-align:center}.katex .accent>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{display:block;position:absolute;width:100%;height:inherit;fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1}.katex svg path{stroke:none}.katex img{border-style:none;min-width:0;min-height:0;max-width:none;max-height:none}.katex .stretchy{width:100%;display:block;position:relative;overflow:hidden}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{width:100%;position:relative;overflow:hidden}.katex .halfarrow-left{position:absolute;left:0;width:50.2%;overflow:hidden}.katex .halfarrow-right{position:absolute;right:0;width:50.2%;overflow:hidden}.katex .brace-left{position:absolute;left:0;width:25.1%;overflow:hidden}.katex .brace-center{position:absolute;left:25%;width:50%;overflow:hidden}.katex .brace-right{position:absolute;right:0;width:25.1%;overflow:hidden}.katex .x-arrow-pad{padding:0 .5em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{box-sizing:border-box;border:.04em solid}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{text-align:left} diff --git a/docs/katex/katex.min.js b/docs/katex/katex.min.js new file mode 100644 index 00000000..906ce128 --- /dev/null +++ b/docs/katex/katex.min.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.katex=e():t.katex=e()}("undefined"!=typeof self?self:this,function(){return function(t){var e={};function r(a){if(e[a])return e[a].exports;var n=e[a]={i:a,l:!1,exports:{}};return t[a].call(n.exports,n,n.exports,r),n.l=!0,n.exports}return r.m=t,r.c=e,r.d=function(t,e,a){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:a})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var a=Object.create(null);if(r.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)r.d(a,n,function(e){return t[e]}.bind(null,n));return a},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=1)}([function(t,e,r){},function(t,e,r){"use strict";r.r(e);r(0);var a=function(){function t(t,e,r){this.lexer=void 0,this.start=void 0,this.end=void 0,this.lexer=t,this.start=e,this.end=r}return t.range=function(e,r){return r?e&&e.loc&&r.loc&&e.loc.lexer===r.loc.lexer?new t(e.loc.lexer,e.loc.start,r.loc.end):null:e&&e.loc},t}(),n=function(){function t(t,e){this.text=void 0,this.loc=void 0,this.text=t,this.loc=e}return t.prototype.range=function(e,r){return new t(r,a.range(this,e))},t}(),i=function t(e,r){this.position=void 0;var a,n="KaTeX parse error: "+e,i=r&&r.loc;if(i&&i.start<=i.end){var o=i.lexer.input;a=i.start;var s=i.end;a===o.length?n+=" at end of input: ":n+=" at position "+(a+1)+": ";var h=o.slice(a,s).replace(/[^]/g,"$&\u0332");n+=(a>15?"\u2026"+o.slice(a-15,a):o.slice(0,a))+h+(s+15<o.length?o.slice(s,s+15)+"\u2026":o.slice(s))}var l=new Error(n);return l.name="ParseError",l.__proto__=t.prototype,l.position=a,l};i.prototype.__proto__=Error.prototype;var o=i,s=/([A-Z])/g,h={"&":"&amp;",">":"&gt;","<":"&lt;",'"':"&quot;","'":"&#x27;"},l=/[&><"']/g;var m=function t(e){return"ordgroup"===e.type?1===e.body.length?t(e.body[0]):e:"color"===e.type?1===e.body.length?t(e.body[0]):e:"font"===e.type?t(e.body):e},c={contains:function(t,e){return-1!==t.indexOf(e)},deflt:function(t,e){return void 0===t?e:t},escape:function(t){return String(t).replace(l,function(t){return h[t]})},hyphenate:function(t){return t.replace(s,"-$1").toLowerCase()},getBaseElem:m,isCharacterBox:function(t){var e=m(t);return"mathord"===e.type||"textord"===e.type||"atom"===e.type},protocolFromUrl:function(t){var e=/^\s*([^\\\/#]*?)(?::|&#0*58|&#x0*3a)/i.exec(t);return null!=e?e[1]:"_relative"}},u=function(){function t(t){this.displayMode=void 0,this.output=void 0,this.leqno=void 0,this.fleqn=void 0,this.throwOnError=void 0,this.errorColor=void 0,this.macros=void 0,this.minRuleThickness=void 0,this.colorIsTextColor=void 0,this.strict=void 0,this.trust=void 0,this.maxSize=void 0,this.maxExpand=void 0,t=t||{},this.displayMode=c.deflt(t.displayMode,!1),this.output=c.deflt(t.output,"htmlAndMathml"),this.leqno=c.deflt(t.leqno,!1),this.fleqn=c.deflt(t.fleqn,!1),this.throwOnError=c.deflt(t.throwOnError,!0),this.errorColor=c.deflt(t.errorColor,"#cc0000"),this.macros=t.macros||{},this.minRuleThickness=Math.max(0,c.deflt(t.minRuleThickness,0)),this.colorIsTextColor=c.deflt(t.colorIsTextColor,!1),this.strict=c.deflt(t.strict,"warn"),this.trust=c.deflt(t.trust,!1),this.maxSize=Math.max(0,c.deflt(t.maxSize,1/0)),this.maxExpand=Math.max(0,c.deflt(t.maxExpand,1e3))}var e=t.prototype;return e.reportNonstrict=function(t,e,r){var a=this.strict;if("function"==typeof a&&(a=a(t,e,r)),a&&"ignore"!==a){if(!0===a||"error"===a)throw new o("LaTeX-incompatible input and strict mode is set to 'error': "+e+" ["+t+"]",r);"warn"===a?"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+e+" ["+t+"]"):"undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+a+"': "+e+" ["+t+"]")}},e.useStrictBehavior=function(t,e,r){var a=this.strict;if("function"==typeof a)try{a=a(t,e,r)}catch(t){a="error"}return!(!a||"ignore"===a)&&(!0===a||"error"===a||("warn"===a?("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to 'warn': "+e+" ["+t+"]"),!1):("undefined"!=typeof console&&console.warn("LaTeX-incompatible input and strict mode is set to unrecognized '"+a+"': "+e+" ["+t+"]"),!1)))},e.isTrusted=function(t){t.url&&!t.protocol&&(t.protocol=c.protocolFromUrl(t.url));var e="function"==typeof this.trust?this.trust(t):this.trust;return Boolean(e)},t}(),p=function(){function t(t,e,r){this.id=void 0,this.size=void 0,this.cramped=void 0,this.id=t,this.size=e,this.cramped=r}var e=t.prototype;return e.sup=function(){return d[f[this.id]]},e.sub=function(){return d[g[this.id]]},e.fracNum=function(){return d[x[this.id]]},e.fracDen=function(){return d[v[this.id]]},e.cramp=function(){return d[b[this.id]]},e.text=function(){return d[y[this.id]]},e.isTight=function(){return this.size>=2},t}(),d=[new p(0,0,!1),new p(1,0,!0),new p(2,1,!1),new p(3,1,!0),new p(4,2,!1),new p(5,2,!0),new p(6,3,!1),new p(7,3,!0)],f=[4,5,4,5,6,7,6,7],g=[5,5,5,5,7,7,7,7],x=[2,3,4,5,6,7,6,7],v=[3,3,5,5,7,7,7,7],b=[1,1,3,3,5,5,7,7],y=[0,1,2,3,2,3,2,3],w={DISPLAY:d[0],TEXT:d[2],SCRIPT:d[4],SCRIPTSCRIPT:d[6]},k=[{name:"latin",blocks:[[256,591],[768,879]]},{name:"cyrillic",blocks:[[1024,1279]]},{name:"brahmic",blocks:[[2304,4255]]},{name:"georgian",blocks:[[4256,4351]]},{name:"cjk",blocks:[[12288,12543],[19968,40879],[65280,65376]]},{name:"hangul",blocks:[[44032,55215]]}];var S=[];function M(t){for(var e=0;e<S.length;e+=2)if(t>=S[e]&&t<=S[e+1])return!0;return!1}k.forEach(function(t){return t.blocks.forEach(function(t){return S.push.apply(S,t)})});var z={doubleleftarrow:"M262 157\nl10-10c34-36 62.7-77 86-123 3.3-8 5-13.3 5-16 0-5.3-6.7-8-20-8-7.3\n 0-12.2.5-14.5 1.5-2.3 1-4.8 4.5-7.5 10.5-49.3 97.3-121.7 169.3-217 216-28\n 14-57.3 25-88 33-6.7 2-11 3.8-13 5.5-2 1.7-3 4.2-3 7.5s1 5.8 3 7.5\nc2 1.7 6.3 3.5 13 5.5 68 17.3 128.2 47.8 180.5 91.5 52.3 43.7 93.8 96.2 124.5\n 157.5 9.3 8 15.3 12.3 18 13h6c12-.7 18-4 18-10 0-2-1.7-7-5-15-23.3-46-52-87\n-86-123l-10-10h399738v-40H218c328 0 0 0 0 0l-10-8c-26.7-20-65.7-43-117-69 2.7\n-2 6-3.7 10-5 36.7-16 72.3-37.3 107-64l10-8h399782v-40z\nm8 0v40h399730v-40zm0 194v40h399730v-40z",doublerightarrow:"M399738 392l\n-10 10c-34 36-62.7 77-86 123-3.3 8-5 13.3-5 16 0 5.3 6.7 8 20 8 7.3 0 12.2-.5\n 14.5-1.5 2.3-1 4.8-4.5 7.5-10.5 49.3-97.3 121.7-169.3 217-216 28-14 57.3-25 88\n-33 6.7-2 11-3.8 13-5.5 2-1.7 3-4.2 3-7.5s-1-5.8-3-7.5c-2-1.7-6.3-3.5-13-5.5-68\n-17.3-128.2-47.8-180.5-91.5-52.3-43.7-93.8-96.2-124.5-157.5-9.3-8-15.3-12.3-18\n-13h-6c-12 .7-18 4-18 10 0 2 1.7 7 5 15 23.3 46 52 87 86 123l10 10H0v40h399782\nc-328 0 0 0 0 0l10 8c26.7 20 65.7 43 117 69-2.7 2-6 3.7-10 5-36.7 16-72.3 37.3\n-107 64l-10 8H0v40zM0 157v40h399730v-40zm0 194v40h399730v-40z",leftarrow:"M400000 241H110l3-3c68.7-52.7 113.7-120\n 135-202 4-14.7 6-23 6-25 0-7.3-7-11-21-11-8 0-13.2.8-15.5 2.5-2.3 1.7-4.2 5.8\n-5.5 12.5-1.3 4.7-2.7 10.3-4 17-12 48.7-34.8 92-68.5 130S65.3 228.3 18 247\nc-10 4-16 7.7-18 11 0 8.7 6 14.3 18 17 47.3 18.7 87.8 47 121.5 85S196 441.3 208\n 490c.7 2 1.3 5 2 9s1.2 6.7 1.5 8c.3 1.3 1 3.3 2 6s2.2 4.5 3.5 5.5c1.3 1 3.3\n 1.8 6 2.5s6 1 10 1c14 0 21-3.7 21-11 0-2-2-10.3-6-25-20-79.3-65-146.7-135-202\n l-3-3h399890zM100 241v40h399900v-40z",leftbrace:"M6 548l-6-6v-35l6-11c56-104 135.3-181.3 238-232 57.3-28.7 117\n-45 179-50h399577v120H403c-43.3 7-81 15-113 26-100.7 33-179.7 91-237 174-2.7\n 5-6 9-10 13-.7 1-7.3 1-20 1H6z",leftbraceunder:"M0 6l6-6h17c12.688 0 19.313.3 20 1 4 4 7.313 8.3 10 13\n 35.313 51.3 80.813 93.8 136.5 127.5 55.688 33.7 117.188 55.8 184.5 66.5.688\n 0 2 .3 4 1 18.688 2.7 76 4.3 172 5h399450v120H429l-6-1c-124.688-8-235-61.7\n-331-161C60.687 138.7 32.312 99.3 7 54L0 41V6z",leftgroup:"M400000 80\nH435C64 80 168.3 229.4 21 260c-5.9 1.2-18 0-18 0-2 0-3-1-3-3v-38C76 61 257 0\n 435 0h399565z",leftgroupunder:"M400000 262\nH435C64 262 168.3 112.6 21 82c-5.9-1.2-18 0-18 0-2 0-3 1-3 3v38c76 158 257 219\n 435 219h399565z",leftharpoon:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3\n-3.3 10.2-9.5 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5\n-18.3 3-21-1.3-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7\n-196 228-6.7 4.7-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40z",leftharpoonplus:"M0 267c.7 5.3 3 10 7 14h399993v-40H93c3.3-3.3 10.2-9.5\n 20.5-18.5s17.8-15.8 22.5-20.5c50.7-52 88-110.3 112-175 4-11.3 5-18.3 3-21-1.3\n-4-7.3-6-18-6-8 0-13 .7-15 2s-4.7 6.7-8 16c-42 98.7-107.3 174.7-196 228-6.7 4.7\n-10.7 8-12 10-1.3 2-2 5.7-2 11zm100-26v40h399900v-40zM0 435v40h400000v-40z\nm0 0v40h400000v-40z",leftharpoondown:"M7 241c-4 4-6.333 8.667-7 14 0 5.333.667 9 2 11s5.333\n 5.333 12 10c90.667 54 156 130 196 228 3.333 10.667 6.333 16.333 9 17 2 .667 5\n 1 9 1h5c10.667 0 16.667-2 18-6 2-2.667 1-9.667-3-21-32-87.333-82.667-157.667\n-152-211l-3-3h399907v-40zM93 281 H400000 v-40L7 241z",leftharpoondownplus:"M7 435c-4 4-6.3 8.7-7 14 0 5.3.7 9 2 11s5.3 5.3 12\n 10c90.7 54 156 130 196 228 3.3 10.7 6.3 16.3 9 17 2 .7 5 1 9 1h5c10.7 0 16.7\n-2 18-6 2-2.7 1-9.7-3-21-32-87.3-82.7-157.7-152-211l-3-3h399907v-40H7zm93 0\nv40h399900v-40zM0 241v40h399900v-40zm0 0v40h399900v-40z",lefthook:"M400000 281 H103s-33-11.2-61-33.5S0 197.3 0 164s14.2-61.2 42.5\n-83.5C70.8 58.2 104 47 142 47 c16.7 0 25 6.7 25 20 0 12-8.7 18.7-26 20-40 3.3\n-68.7 15.7-86 37-10 12-15 25.3-15 40 0 22.7 9.8 40.7 29.5 54 19.7 13.3 43.5 21\n 71.5 23h399859zM103 281v-40h399897v40z",leftlinesegment:"M40 281 V428 H0 V94 H40 V241 H400000 v40z\nM40 281 V428 H0 V94 H40 V241 H400000 v40z",leftmapsto:"M40 281 V448H0V74H40V241H400000v40z\nM40 281 V448H0V74H40V241H400000v40z",leftToFrom:"M0 147h400000v40H0zm0 214c68 40 115.7 95.7 143 167h22c15.3 0 23\n-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69-70-101l-7-8h399905v-40H95l7-8\nc28.7-32 52-65.7 70-101 10.7-23.3 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 265.3\n 68 321 0 361zm0-174v-40h399900v40zm100 154v40h399900v-40z",longequal:"M0 50 h400000 v40H0z m0 194h40000v40H0z\nM0 50 h400000 v40H0z m0 194h40000v40H0z",midbrace:"M200428 334\nc-100.7-8.3-195.3-44-280-108-55.3-42-101.7-93-139-153l-9-14c-2.7 4-5.7 8.7-9 14\n-53.3 86.7-123.7 153-211 199-66.7 36-137.3 56.3-212 62H0V214h199568c178.3-11.7\n 311.7-78.3 403-201 6-8 9.7-12 11-12 .7-.7 6.7-1 18-1s17.3.3 18 1c1.3 0 5 4 11\n 12 44.7 59.3 101.3 106.3 170 141s145.3 54.3 229 60h199572v120z",midbraceunder:"M199572 214\nc100.7 8.3 195.3 44 280 108 55.3 42 101.7 93 139 153l9 14c2.7-4 5.7-8.7 9-14\n 53.3-86.7 123.7-153 211-199 66.7-36 137.3-56.3 212-62h199568v120H200432c-178.3\n 11.7-311.7 78.3-403 201-6 8-9.7 12-11 12-.7.7-6.7 1-18 1s-17.3-.3-18-1c-1.3 0\n-5-4-11-12-44.7-59.3-101.3-106.3-170-141s-145.3-54.3-229-60H0V214z",oiintSize1:"M512.6 71.6c272.6 0 320.3 106.8 320.3 178.2 0 70.8-47.7 177.6\n-320.3 177.6S193.1 320.6 193.1 249.8c0-71.4 46.9-178.2 319.5-178.2z\nm368.1 178.2c0-86.4-60.9-215.4-368.1-215.4-306.4 0-367.3 129-367.3 215.4 0 85.8\n60.9 214.8 367.3 214.8 307.2 0 368.1-129 368.1-214.8z",oiintSize2:"M757.8 100.1c384.7 0 451.1 137.6 451.1 230 0 91.3-66.4 228.8\n-451.1 228.8-386.3 0-452.7-137.5-452.7-228.8 0-92.4 66.4-230 452.7-230z\nm502.4 230c0-111.2-82.4-277.2-502.4-277.2s-504 166-504 277.2\nc0 110 84 276 504 276s502.4-166 502.4-276z",oiiintSize1:"M681.4 71.6c408.9 0 480.5 106.8 480.5 178.2 0 70.8-71.6 177.6\n-480.5 177.6S202.1 320.6 202.1 249.8c0-71.4 70.5-178.2 479.3-178.2z\nm525.8 178.2c0-86.4-86.8-215.4-525.7-215.4-437.9 0-524.7 129-524.7 215.4 0\n85.8 86.8 214.8 524.7 214.8 438.9 0 525.7-129 525.7-214.8z",oiiintSize2:"M1021.2 53c603.6 0 707.8 165.8 707.8 277.2 0 110-104.2 275.8\n-707.8 275.8-606 0-710.2-165.8-710.2-275.8C311 218.8 415.2 53 1021.2 53z\nm770.4 277.1c0-131.2-126.4-327.6-770.5-327.6S248.4 198.9 248.4 330.1\nc0 130 128.8 326.4 772.7 326.4s770.5-196.4 770.5-326.4z",rightarrow:"M0 241v40h399891c-47.3 35.3-84 78-110 128\n-16.7 32-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20\n 11 8 0 13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7\n 39-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85\n-40.5-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n 151.7 139 205zm0 0v40h399900v-40z",rightbrace:"M400000 542l\n-6 6h-17c-12.7 0-19.3-.3-20-1-4-4-7.3-8.3-10-13-35.3-51.3-80.8-93.8-136.5-127.5\ns-117.2-55.8-184.5-66.5c-.7 0-2-.3-4-1-18.7-2.7-76-4.3-172-5H0V214h399571l6 1\nc124.7 8 235 61.7 331 161 31.3 33.3 59.7 72.7 85 118l7 13v35z",rightbraceunder:"M399994 0l6 6v35l-6 11c-56 104-135.3 181.3-238 232-57.3\n 28.7-117 45-179 50H-300V214h399897c43.3-7 81-15 113-26 100.7-33 179.7-91 237\n-174 2.7-5 6-9 10-13 .7-1 7.3-1 20-1h17z",rightgroup:"M0 80h399565c371 0 266.7 149.4 414 180 5.9 1.2 18 0 18 0 2 0\n 3-1 3-3v-38c-76-158-257-219-435-219H0z",rightgroupunder:"M0 262h399565c371 0 266.7-149.4 414-180 5.9-1.2 18 0 18\n 0 2 0 3 1 3 3v38c-76 158-257 219-435 219H0z",rightharpoon:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3\n-3.7-15.3-11-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2\n-10.7 0-16.7 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58\n 69.2 92 94.5zm0 0v40h399900v-40z",rightharpoonplus:"M0 241v40h399993c4.7-4.7 7-9.3 7-14 0-9.3-3.7-15.3-11\n-18-92.7-56.7-159-133.7-199-231-3.3-9.3-6-14.7-8-16-2-1.3-7-2-15-2-10.7 0-16.7\n 2-18 6-2 2.7-1 9.7 3 21 15.3 42 36.7 81.8 64 119.5 27.3 37.7 58 69.2 92 94.5z\nm0 0v40h399900v-40z m100 194v40h399900v-40zm0 0v40h399900v-40z",rightharpoondown:"M399747 511c0 7.3 6.7 11 20 11 8 0 13-.8 15-2.5s4.7-6.8\n 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3 8.5-5.8 9.5\n-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3-64.7 57-92 95\n-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 241v40h399900v-40z",rightharpoondownplus:"M399747 705c0 7.3 6.7 11 20 11 8 0 13-.8\n 15-2.5s4.7-6.8 8-15.5c40-94 99.3-166.3 178-217 13.3-8 20.3-12.3 21-13 5.3-3.3\n 8.5-5.8 9.5-7.5 1-1.7 1.5-5.2 1.5-10.5s-2.3-10.3-7-15H0v40h399908c-34 25.3\n-64.7 57-92 95-27.3 38-48.7 77.7-64 119-3.3 8.7-5 14-5 16zM0 435v40h399900v-40z\nm0-194v40h400000v-40zm0 0v40h400000v-40z",righthook:"M399859 241c-764 0 0 0 0 0 40-3.3 68.7-15.7 86-37 10-12 15-25.3\n 15-40 0-22.7-9.8-40.7-29.5-54-19.7-13.3-43.5-21-71.5-23-17.3-1.3-26-8-26-20 0\n-13.3 8.7-20 26-20 38 0 71 11.2 99 33.5 0 0 7 5.6 21 16.7 14 11.2 21 33.5 21\n 66.8s-14 61.2-42 83.5c-28 22.3-61 33.5-99 33.5L0 241z M0 281v-40h399859v40z",rightlinesegment:"M399960 241 V94 h40 V428 h-40 V281 H0 v-40z\nM399960 241 V94 h40 V428 h-40 V281 H0 v-40z",rightToFrom:"M400000 167c-70.7-42-118-97.7-142-167h-23c-15.3 0-23 .3-23\n 1 0 1.3 5.3 13.7 16 37 18 35.3 41.3 69 70 101l7 8H0v40h399905l-7 8c-28.7 32\n-52 65.7-70 101-10.7 23.3-16 35.7-16 37 0 .7 7.7 1 23 1h23c24-69.3 71.3-125 142\n-167z M100 147v40h399900v-40zM0 341v40h399900v-40z",twoheadleftarrow:"M0 167c68 40\n 115.7 95.7 143 167h22c15.3 0 23-.3 23-1 0-1.3-5.3-13.7-16-37-18-35.3-41.3-69\n-70-101l-7-8h125l9 7c50.7 39.3 85 86 103 140h46c0-4.7-6.3-18.7-19-42-18-35.3\n-40-67.3-66-96l-9-9h399716v-40H284l9-9c26-28.7 48-60.7 66-96 12.7-23.333 19\n-37.333 19-42h-46c-18 54-52.3 100.7-103 140l-9 7H95l7-8c28.7-32 52-65.7 70-101\n 10.7-23.333 16-35.7 16-37 0-.7-7.7-1-23-1h-22C115.7 71.3 68 127 0 167z",twoheadrightarrow:"M400000 167\nc-68-40-115.7-95.7-143-167h-22c-15.3 0-23 .3-23 1 0 1.3 5.3 13.7 16 37 18 35.3\n 41.3 69 70 101l7 8h-125l-9-7c-50.7-39.3-85-86-103-140h-46c0 4.7 6.3 18.7 19 42\n 18 35.3 40 67.3 66 96l9 9H0v40h399716l-9 9c-26 28.7-48 60.7-66 96-12.7 23.333\n-19 37.333-19 42h46c18-54 52.3-100.7 103-140l9-7h125l-7 8c-28.7 32-52 65.7-70\n 101-10.7 23.333-16 35.7-16 37 0 .7 7.7 1 23 1h22c27.3-71.3 75-127 143-167z",tilde1:"M200 55.538c-77 0-168 73.953-177 73.953-3 0-7\n-2.175-9-5.437L2 97c-1-2-2-4-2-6 0-4 2-7 5-9l20-12C116 12 171 0 207 0c86 0\n 114 68 191 68 78 0 168-68 177-68 4 0 7 2 9 5l12 19c1 2.175 2 4.35 2 6.525 0\n 4.35-2 7.613-5 9.788l-19 13.05c-92 63.077-116.937 75.308-183 76.128\n-68.267.847-113-73.952-191-73.952z",tilde2:"M344 55.266c-142 0-300.638 81.316-311.5 86.418\n-8.01 3.762-22.5 10.91-23.5 5.562L1 120c-1-2-1-3-1-4 0-5 3-9 8-10l18.4-9C160.9\n 31.9 283 0 358 0c148 0 188 122 331 122s314-97 326-97c4 0 8 2 10 7l7 21.114\nc1 2.14 1 3.21 1 4.28 0 5.347-3 9.626-7 10.696l-22.3 12.622C852.6 158.372 751\n 181.476 676 181.476c-149 0-189-126.21-332-126.21z",tilde3:"M786 59C457 59 32 175.242 13 175.242c-6 0-10-3.457\n-11-10.37L.15 138c-1-7 3-12 10-13l19.2-6.4C378.4 40.7 634.3 0 804.3 0c337 0\n 411.8 157 746.8 157 328 0 754-112 773-112 5 0 10 3 11 9l1 14.075c1 8.066-.697\n 16.595-6.697 17.492l-21.052 7.31c-367.9 98.146-609.15 122.696-778.15 122.696\n -338 0-409-156.573-744-156.573z",tilde4:"M786 58C457 58 32 177.487 13 177.487c-6 0-10-3.345\n-11-10.035L.15 143c-1-7 3-12 10-13l22-6.7C381.2 35 637.15 0 807.15 0c337 0 409\n 177 744 177 328 0 754-127 773-127 5 0 10 3 11 9l1 14.794c1 7.805-3 13.38-9\n 14.495l-20.7 5.574c-366.85 99.79-607.3 139.372-776.3 139.372-338 0-409\n -175.236-744-175.236z",vec:"M377 20c0-5.333 1.833-10 5.5-14S391 0 397 0c4.667 0 8.667 1.667 12 5\n3.333 2.667 6.667 9 10 19 6.667 24.667 20.333 43.667 41 57 7.333 4.667 11\n10.667 11 18 0 6-1 10-3 12s-6.667 5-14 9c-28.667 14.667-53.667 35.667-75 63\n-1.333 1.333-3.167 3.5-5.5 6.5s-4 4.833-5 5.5c-1 .667-2.5 1.333-4.5 2s-4.333 1\n-7 1c-4.667 0-9.167-1.833-13.5-5.5S337 184 337 178c0-12.667 15.667-32.333 47-59\nH213l-171-1c-8.667-6-13-12.333-13-19 0-4.667 4.333-11.333 13-20h359\nc-16-25.333-24-45-24-59z",widehat1:"M529 0h5l519 115c5 1 9 5 9 10 0 1-1 2-1 3l-4 22\nc-1 5-5 9-11 9h-2L532 67 19 159h-2c-5 0-9-4-11-9l-5-22c-1-6 2-12 8-13z",widehat2:"M1181 0h2l1171 176c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 220h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat3:"M1181 0h2l1171 236c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 280h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widehat4:"M1181 0h2l1171 296c6 0 10 5 10 11l-2 23c-1 6-5 10\n-11 10h-1L1182 67 15 340h-1c-6 0-10-4-11-10l-2-23c-1-6 4-11 10-11z",widecheck1:"M529,159h5l519,-115c5,-1,9,-5,9,-10c0,-1,-1,-2,-1,-3l-4,-22c-1,\n-5,-5,-9,-11,-9h-2l-512,92l-513,-92h-2c-5,0,-9,4,-11,9l-5,22c-1,6,2,12,8,13z",widecheck2:"M1181,220h2l1171,-176c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,153l-1167,-153h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck3:"M1181,280h2l1171,-236c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,213l-1167,-213h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",widecheck4:"M1181,340h2l1171,-296c6,0,10,-5,10,-11l-2,-23c-1,-6,-5,-10,\n-11,-10h-1l-1168,273l-1167,-273h-1c-6,0,-10,4,-11,10l-2,23c-1,6,4,11,10,11z",baraboveleftarrow:"M400000 620h-399890l3 -3c68.7 -52.7 113.7 -120 135 -202\nc4 -14.7 6 -23 6 -25c0 -7.3 -7 -11 -21 -11c-8 0 -13.2 0.8 -15.5 2.5\nc-2.3 1.7 -4.2 5.8 -5.5 12.5c-1.3 4.7 -2.7 10.3 -4 17c-12 48.7 -34.8 92 -68.5 130\ns-74.2 66.3 -121.5 85c-10 4 -16 7.7 -18 11c0 8.7 6 14.3 18 17c47.3 18.7 87.8 47\n121.5 85s56.5 81.3 68.5 130c0.7 2 1.3 5 2 9s1.2 6.7 1.5 8c0.3 1.3 1 3.3 2 6\ns2.2 4.5 3.5 5.5c1.3 1 3.3 1.8 6 2.5s6 1 10 1c14 0 21 -3.7 21 -11\nc0 -2 -2 -10.3 -6 -25c-20 -79.3 -65 -146.7 -135 -202l-3 -3h399890z\nM100 620v40h399900v-40z M0 241v40h399900v-40zM0 241v40h399900v-40z",rightarrowabovebar:"M0 241v40h399891c-47.3 35.3-84 78-110 128-16.7 32\n-27.7 63.7-33 95 0 1.3-.2 2.7-.5 4-.3 1.3-.5 2.3-.5 3 0 7.3 6.7 11 20 11 8 0\n13.2-.8 15.5-2.5 2.3-1.7 4.2-5.5 5.5-11.5 2-13.3 5.7-27 11-41 14.7-44.7 39\n-84.5 73-119.5s73.7-60.2 119-75.5c6-2 9-5.7 9-11s-3-9-9-11c-45.3-15.3-85-40.5\n-119-75.5s-58.3-74.8-73-119.5c-4.7-14-8.3-27.3-11-40-1.3-6.7-3.2-10.8-5.5\n-12.5-2.3-1.7-7.5-2.5-15.5-2.5-14 0-21 3.7-21 11 0 2 2 10.3 6 25 20.7 83.3 67\n151.7 139 205zm96 379h399894v40H0zm0 0h399904v40H0z",baraboveshortleftharpoon:"M507,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17\nc2,0.7,5,1,9,1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21\nc-32,-87.3,-82.7,-157.7,-152,-211c0,0,-3,-3,-3,-3l399351,0l0,-40\nc-398570,0,-399437,0,-399437,0z M593 435 v40 H399500 v-40z\nM0 281 v-40 H399908 v40z M0 281 v-40 H399908 v40z",rightharpoonaboveshortbar:"M0,241 l0,40c399126,0,399993,0,399993,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM0 241 v40 H399908 v-40z M0 475 v-40 H399500 v40z M0 475 v-40 H399500 v40z",shortbaraboveleftharpoon:"M7,435c-4,4,-6.3,8.7,-7,14c0,5.3,0.7,9,2,11\nc1.3,2,5.3,5.3,12,10c90.7,54,156,130,196,228c3.3,10.7,6.3,16.3,9,17c2,0.7,5,1,9,\n1c0,0,5,0,5,0c10.7,0,16.7,-2,18,-6c2,-2.7,1,-9.7,-3,-21c-32,-87.3,-82.7,-157.7,\n-152,-211c0,0,-3,-3,-3,-3l399907,0l0,-40c-399126,0,-399993,0,-399993,0z\nM93 435 v40 H400000 v-40z M500 241 v40 H400000 v-40z M500 241 v40 H400000 v-40z",shortrightharpoonabovebar:"M53,241l0,40c398570,0,399437,0,399437,0\nc4.7,-4.7,7,-9.3,7,-14c0,-9.3,-3.7,-15.3,-11,-18c-92.7,-56.7,-159,-133.7,-199,\n-231c-3.3,-9.3,-6,-14.7,-8,-16c-2,-1.3,-7,-2,-15,-2c-10.7,0,-16.7,2,-18,6\nc-2,2.7,-1,9.7,3,21c15.3,42,36.7,81.8,64,119.5c27.3,37.7,58,69.2,92,94.5z\nM500 241 v40 H399408 v-40z M500 435 v40 H400000 v-40z"},A=function(){function t(t){this.children=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.children=t,this.classes=[],this.height=0,this.depth=0,this.maxFontSize=0,this.style={}}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){for(var t=document.createDocumentFragment(),e=0;e<this.children.length;e++)t.appendChild(this.children[e].toNode());return t},e.toMarkup=function(){for(var t="",e=0;e<this.children.length;e++)t+=this.children[e].toMarkup();return t},e.toText=function(){var t=function(t){return t.toText()};return this.children.map(t).join("")},t}(),T=function(t){return t.filter(function(t){return t}).join(" ")},B=function(t,e,r){if(this.classes=t||[],this.attributes={},this.height=0,this.depth=0,this.maxFontSize=0,this.style=r||{},e){e.style.isTight()&&this.classes.push("mtight");var a=e.getColor();a&&(this.style.color=a)}},C=function(t){var e=document.createElement(t);for(var r in e.className=T(this.classes),this.style)this.style.hasOwnProperty(r)&&(e.style[r]=this.style[r]);for(var a in this.attributes)this.attributes.hasOwnProperty(a)&&e.setAttribute(a,this.attributes[a]);for(var n=0;n<this.children.length;n++)e.appendChild(this.children[n].toNode());return e},q=function(t){var e="<"+t;this.classes.length&&(e+=' class="'+c.escape(T(this.classes))+'"');var r="";for(var a in this.style)this.style.hasOwnProperty(a)&&(r+=c.hyphenate(a)+":"+this.style[a]+";");for(var n in r&&(e+=' style="'+c.escape(r)+'"'),this.attributes)this.attributes.hasOwnProperty(n)&&(e+=" "+n+'="'+c.escape(this.attributes[n])+'"');e+=">";for(var i=0;i<this.children.length;i++)e+=this.children[i].toMarkup();return e+="</"+t+">"},N=function(){function t(t,e,r,a){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.width=void 0,this.maxFontSize=void 0,this.style=void 0,B.call(this,t,r,a),this.children=e||[]}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){return C.call(this,"span")},e.toMarkup=function(){return q.call(this,"span")},t}(),I=function(){function t(t,e,r,a){this.children=void 0,this.attributes=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,B.call(this,e,a),this.children=r||[],this.setAttribute("href",t)}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){return C.call(this,"a")},e.toMarkup=function(){return q.call(this,"a")},t}(),R=function(){function t(t,e,r){this.src=void 0,this.alt=void 0,this.classes=void 0,this.height=void 0,this.depth=void 0,this.maxFontSize=void 0,this.style=void 0,this.alt=e,this.src=t,this.classes=["mord"],this.style=r}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){var t=document.createElement("img");for(var e in t.src=this.src,t.alt=this.alt,t.className="mord",this.style)this.style.hasOwnProperty(e)&&(t.style[e]=this.style[e]);return t},e.toMarkup=function(){var t="<img src='"+this.src+" 'alt='"+this.alt+"' ",e="";for(var r in this.style)this.style.hasOwnProperty(r)&&(e+=c.hyphenate(r)+":"+this.style[r]+";");return e&&(t+=' style="'+c.escape(e)+'"'),t+="'/>"},t}(),O={"\xee":"\u0131\u0302","\xef":"\u0131\u0308","\xed":"\u0131\u0301","\xec":"\u0131\u0300"},E=function(){function t(t,e,r,a,n,i,o,s){this.text=void 0,this.height=void 0,this.depth=void 0,this.italic=void 0,this.skew=void 0,this.width=void 0,this.maxFontSize=void 0,this.classes=void 0,this.style=void 0,this.text=t,this.height=e||0,this.depth=r||0,this.italic=a||0,this.skew=n||0,this.width=i||0,this.classes=o||[],this.style=s||{},this.maxFontSize=0;var h=function(t){for(var e=0;e<k.length;e++)for(var r=k[e],a=0;a<r.blocks.length;a++){var n=r.blocks[a];if(t>=n[0]&&t<=n[1])return r.name}return null}(this.text.charCodeAt(0));h&&this.classes.push(h+"_fallback"),/[\xee\xef\xed\xec]/.test(this.text)&&(this.text=O[this.text])}var e=t.prototype;return e.hasClass=function(t){return c.contains(this.classes,t)},e.toNode=function(){var t=document.createTextNode(this.text),e=null;for(var r in this.italic>0&&((e=document.createElement("span")).style.marginRight=this.italic+"em"),this.classes.length>0&&((e=e||document.createElement("span")).className=T(this.classes)),this.style)this.style.hasOwnProperty(r)&&((e=e||document.createElement("span")).style[r]=this.style[r]);return e?(e.appendChild(t),e):t},e.toMarkup=function(){var t=!1,e="<span";this.classes.length&&(t=!0,e+=' class="',e+=c.escape(T(this.classes)),e+='"');var r="";for(var a in this.italic>0&&(r+="margin-right:"+this.italic+"em;"),this.style)this.style.hasOwnProperty(a)&&(r+=c.hyphenate(a)+":"+this.style[a]+";");r&&(t=!0,e+=' style="'+c.escape(r)+'"');var n=c.escape(this.text);return t?(e+=">",e+=n,e+="</span>"):n},t}(),L=function(){function t(t,e){this.children=void 0,this.attributes=void 0,this.children=t||[],this.attributes=e||{}}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","svg");for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);for(var r=0;r<this.children.length;r++)t.appendChild(this.children[r].toNode());return t},e.toMarkup=function(){var t="<svg";for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&(t+=" "+e+"='"+this.attributes[e]+"'");t+=">";for(var r=0;r<this.children.length;r++)t+=this.children[r].toMarkup();return t+="</svg>"},t}(),H=function(){function t(t,e){this.pathName=void 0,this.alternate=void 0,this.pathName=t,this.alternate=e}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","path");return this.alternate?t.setAttribute("d",this.alternate):t.setAttribute("d",z[this.pathName]),t},e.toMarkup=function(){return this.alternate?"<path d='"+this.alternate+"'/>":"<path d='"+z[this.pathName]+"'/>"},t}(),P=function(){function t(t){this.attributes=void 0,this.attributes=t||{}}var e=t.prototype;return e.toNode=function(){var t=document.createElementNS("http://www.w3.org/2000/svg","line");for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);return t},e.toMarkup=function(){var t="<line";for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&(t+=" "+e+"='"+this.attributes[e]+"'");return t+="/>"},t}();function D(t){if(t instanceof E)return t;throw new Error("Expected symbolNode but got "+String(t)+".")}var F={"AMS-Regular":{65:[0,.68889,0,0,.72222],66:[0,.68889,0,0,.66667],67:[0,.68889,0,0,.72222],68:[0,.68889,0,0,.72222],69:[0,.68889,0,0,.66667],70:[0,.68889,0,0,.61111],71:[0,.68889,0,0,.77778],72:[0,.68889,0,0,.77778],73:[0,.68889,0,0,.38889],74:[.16667,.68889,0,0,.5],75:[0,.68889,0,0,.77778],76:[0,.68889,0,0,.66667],77:[0,.68889,0,0,.94445],78:[0,.68889,0,0,.72222],79:[.16667,.68889,0,0,.77778],80:[0,.68889,0,0,.61111],81:[.16667,.68889,0,0,.77778],82:[0,.68889,0,0,.72222],83:[0,.68889,0,0,.55556],84:[0,.68889,0,0,.66667],85:[0,.68889,0,0,.72222],86:[0,.68889,0,0,.72222],87:[0,.68889,0,0,1],88:[0,.68889,0,0,.72222],89:[0,.68889,0,0,.72222],90:[0,.68889,0,0,.66667],107:[0,.68889,0,0,.55556],165:[0,.675,.025,0,.75],174:[.15559,.69224,0,0,.94666],240:[0,.68889,0,0,.55556],295:[0,.68889,0,0,.54028],710:[0,.825,0,0,2.33334],732:[0,.9,0,0,2.33334],770:[0,.825,0,0,2.33334],771:[0,.9,0,0,2.33334],989:[.08167,.58167,0,0,.77778],1008:[0,.43056,.04028,0,.66667],8245:[0,.54986,0,0,.275],8463:[0,.68889,0,0,.54028],8487:[0,.68889,0,0,.72222],8498:[0,.68889,0,0,.55556],8502:[0,.68889,0,0,.66667],8503:[0,.68889,0,0,.44445],8504:[0,.68889,0,0,.66667],8513:[0,.68889,0,0,.63889],8592:[-.03598,.46402,0,0,.5],8594:[-.03598,.46402,0,0,.5],8602:[-.13313,.36687,0,0,1],8603:[-.13313,.36687,0,0,1],8606:[.01354,.52239,0,0,1],8608:[.01354,.52239,0,0,1],8610:[.01354,.52239,0,0,1.11111],8611:[.01354,.52239,0,0,1.11111],8619:[0,.54986,0,0,1],8620:[0,.54986,0,0,1],8621:[-.13313,.37788,0,0,1.38889],8622:[-.13313,.36687,0,0,1],8624:[0,.69224,0,0,.5],8625:[0,.69224,0,0,.5],8630:[0,.43056,0,0,1],8631:[0,.43056,0,0,1],8634:[.08198,.58198,0,0,.77778],8635:[.08198,.58198,0,0,.77778],8638:[.19444,.69224,0,0,.41667],8639:[.19444,.69224,0,0,.41667],8642:[.19444,.69224,0,0,.41667],8643:[.19444,.69224,0,0,.41667],8644:[.1808,.675,0,0,1],8646:[.1808,.675,0,0,1],8647:[.1808,.675,0,0,1],8648:[.19444,.69224,0,0,.83334],8649:[.1808,.675,0,0,1],8650:[.19444,.69224,0,0,.83334],8651:[.01354,.52239,0,0,1],8652:[.01354,.52239,0,0,1],8653:[-.13313,.36687,0,0,1],8654:[-.13313,.36687,0,0,1],8655:[-.13313,.36687,0,0,1],8666:[.13667,.63667,0,0,1],8667:[.13667,.63667,0,0,1],8669:[-.13313,.37788,0,0,1],8672:[-.064,.437,0,0,1.334],8674:[-.064,.437,0,0,1.334],8705:[0,.825,0,0,.5],8708:[0,.68889,0,0,.55556],8709:[.08167,.58167,0,0,.77778],8717:[0,.43056,0,0,.42917],8722:[-.03598,.46402,0,0,.5],8724:[.08198,.69224,0,0,.77778],8726:[.08167,.58167,0,0,.77778],8733:[0,.69224,0,0,.77778],8736:[0,.69224,0,0,.72222],8737:[0,.69224,0,0,.72222],8738:[.03517,.52239,0,0,.72222],8739:[.08167,.58167,0,0,.22222],8740:[.25142,.74111,0,0,.27778],8741:[.08167,.58167,0,0,.38889],8742:[.25142,.74111,0,0,.5],8756:[0,.69224,0,0,.66667],8757:[0,.69224,0,0,.66667],8764:[-.13313,.36687,0,0,.77778],8765:[-.13313,.37788,0,0,.77778],8769:[-.13313,.36687,0,0,.77778],8770:[-.03625,.46375,0,0,.77778],8774:[.30274,.79383,0,0,.77778],8776:[-.01688,.48312,0,0,.77778],8778:[.08167,.58167,0,0,.77778],8782:[.06062,.54986,0,0,.77778],8783:[.06062,.54986,0,0,.77778],8785:[.08198,.58198,0,0,.77778],8786:[.08198,.58198,0,0,.77778],8787:[.08198,.58198,0,0,.77778],8790:[0,.69224,0,0,.77778],8791:[.22958,.72958,0,0,.77778],8796:[.08198,.91667,0,0,.77778],8806:[.25583,.75583,0,0,.77778],8807:[.25583,.75583,0,0,.77778],8808:[.25142,.75726,0,0,.77778],8809:[.25142,.75726,0,0,.77778],8812:[.25583,.75583,0,0,.5],8814:[.20576,.70576,0,0,.77778],8815:[.20576,.70576,0,0,.77778],8816:[.30274,.79383,0,0,.77778],8817:[.30274,.79383,0,0,.77778],8818:[.22958,.72958,0,0,.77778],8819:[.22958,.72958,0,0,.77778],8822:[.1808,.675,0,0,.77778],8823:[.1808,.675,0,0,.77778],8828:[.13667,.63667,0,0,.77778],8829:[.13667,.63667,0,0,.77778],8830:[.22958,.72958,0,0,.77778],8831:[.22958,.72958,0,0,.77778],8832:[.20576,.70576,0,0,.77778],8833:[.20576,.70576,0,0,.77778],8840:[.30274,.79383,0,0,.77778],8841:[.30274,.79383,0,0,.77778],8842:[.13597,.63597,0,0,.77778],8843:[.13597,.63597,0,0,.77778],8847:[.03517,.54986,0,0,.77778],8848:[.03517,.54986,0,0,.77778],8858:[.08198,.58198,0,0,.77778],8859:[.08198,.58198,0,0,.77778],8861:[.08198,.58198,0,0,.77778],8862:[0,.675,0,0,.77778],8863:[0,.675,0,0,.77778],8864:[0,.675,0,0,.77778],8865:[0,.675,0,0,.77778],8872:[0,.69224,0,0,.61111],8873:[0,.69224,0,0,.72222],8874:[0,.69224,0,0,.88889],8876:[0,.68889,0,0,.61111],8877:[0,.68889,0,0,.61111],8878:[0,.68889,0,0,.72222],8879:[0,.68889,0,0,.72222],8882:[.03517,.54986,0,0,.77778],8883:[.03517,.54986,0,0,.77778],8884:[.13667,.63667,0,0,.77778],8885:[.13667,.63667,0,0,.77778],8888:[0,.54986,0,0,1.11111],8890:[.19444,.43056,0,0,.55556],8891:[.19444,.69224,0,0,.61111],8892:[.19444,.69224,0,0,.61111],8901:[0,.54986,0,0,.27778],8903:[.08167,.58167,0,0,.77778],8905:[.08167,.58167,0,0,.77778],8906:[.08167,.58167,0,0,.77778],8907:[0,.69224,0,0,.77778],8908:[0,.69224,0,0,.77778],8909:[-.03598,.46402,0,0,.77778],8910:[0,.54986,0,0,.76042],8911:[0,.54986,0,0,.76042],8912:[.03517,.54986,0,0,.77778],8913:[.03517,.54986,0,0,.77778],8914:[0,.54986,0,0,.66667],8915:[0,.54986,0,0,.66667],8916:[0,.69224,0,0,.66667],8918:[.0391,.5391,0,0,.77778],8919:[.0391,.5391,0,0,.77778],8920:[.03517,.54986,0,0,1.33334],8921:[.03517,.54986,0,0,1.33334],8922:[.38569,.88569,0,0,.77778],8923:[.38569,.88569,0,0,.77778],8926:[.13667,.63667,0,0,.77778],8927:[.13667,.63667,0,0,.77778],8928:[.30274,.79383,0,0,.77778],8929:[.30274,.79383,0,0,.77778],8934:[.23222,.74111,0,0,.77778],8935:[.23222,.74111,0,0,.77778],8936:[.23222,.74111,0,0,.77778],8937:[.23222,.74111,0,0,.77778],8938:[.20576,.70576,0,0,.77778],8939:[.20576,.70576,0,0,.77778],8940:[.30274,.79383,0,0,.77778],8941:[.30274,.79383,0,0,.77778],8994:[.19444,.69224,0,0,.77778],8995:[.19444,.69224,0,0,.77778],9416:[.15559,.69224,0,0,.90222],9484:[0,.69224,0,0,.5],9488:[0,.69224,0,0,.5],9492:[0,.37788,0,0,.5],9496:[0,.37788,0,0,.5],9585:[.19444,.68889,0,0,.88889],9586:[.19444,.74111,0,0,.88889],9632:[0,.675,0,0,.77778],9633:[0,.675,0,0,.77778],9650:[0,.54986,0,0,.72222],9651:[0,.54986,0,0,.72222],9654:[.03517,.54986,0,0,.77778],9660:[0,.54986,0,0,.72222],9661:[0,.54986,0,0,.72222],9664:[.03517,.54986,0,0,.77778],9674:[.11111,.69224,0,0,.66667],9733:[.19444,.69224,0,0,.94445],10003:[0,.69224,0,0,.83334],10016:[0,.69224,0,0,.83334],10731:[.11111,.69224,0,0,.66667],10846:[.19444,.75583,0,0,.61111],10877:[.13667,.63667,0,0,.77778],10878:[.13667,.63667,0,0,.77778],10885:[.25583,.75583,0,0,.77778],10886:[.25583,.75583,0,0,.77778],10887:[.13597,.63597,0,0,.77778],10888:[.13597,.63597,0,0,.77778],10889:[.26167,.75726,0,0,.77778],10890:[.26167,.75726,0,0,.77778],10891:[.48256,.98256,0,0,.77778],10892:[.48256,.98256,0,0,.77778],10901:[.13667,.63667,0,0,.77778],10902:[.13667,.63667,0,0,.77778],10933:[.25142,.75726,0,0,.77778],10934:[.25142,.75726,0,0,.77778],10935:[.26167,.75726,0,0,.77778],10936:[.26167,.75726,0,0,.77778],10937:[.26167,.75726,0,0,.77778],10938:[.26167,.75726,0,0,.77778],10949:[.25583,.75583,0,0,.77778],10950:[.25583,.75583,0,0,.77778],10955:[.28481,.79383,0,0,.77778],10956:[.28481,.79383,0,0,.77778],57350:[.08167,.58167,0,0,.22222],57351:[.08167,.58167,0,0,.38889],57352:[.08167,.58167,0,0,.77778],57353:[0,.43056,.04028,0,.66667],57356:[.25142,.75726,0,0,.77778],57357:[.25142,.75726,0,0,.77778],57358:[.41951,.91951,0,0,.77778],57359:[.30274,.79383,0,0,.77778],57360:[.30274,.79383,0,0,.77778],57361:[.41951,.91951,0,0,.77778],57366:[.25142,.75726,0,0,.77778],57367:[.25142,.75726,0,0,.77778],57368:[.25142,.75726,0,0,.77778],57369:[.25142,.75726,0,0,.77778],57370:[.13597,.63597,0,0,.77778],57371:[.13597,.63597,0,0,.77778]},"Caligraphic-Regular":{48:[0,.43056,0,0,.5],49:[0,.43056,0,0,.5],50:[0,.43056,0,0,.5],51:[.19444,.43056,0,0,.5],52:[.19444,.43056,0,0,.5],53:[.19444,.43056,0,0,.5],54:[0,.64444,0,0,.5],55:[.19444,.43056,0,0,.5],56:[0,.64444,0,0,.5],57:[.19444,.43056,0,0,.5],65:[0,.68333,0,.19445,.79847],66:[0,.68333,.03041,.13889,.65681],67:[0,.68333,.05834,.13889,.52653],68:[0,.68333,.02778,.08334,.77139],69:[0,.68333,.08944,.11111,.52778],70:[0,.68333,.09931,.11111,.71875],71:[.09722,.68333,.0593,.11111,.59487],72:[0,.68333,.00965,.11111,.84452],73:[0,.68333,.07382,0,.54452],74:[.09722,.68333,.18472,.16667,.67778],75:[0,.68333,.01445,.05556,.76195],76:[0,.68333,0,.13889,.68972],77:[0,.68333,0,.13889,1.2009],78:[0,.68333,.14736,.08334,.82049],79:[0,.68333,.02778,.11111,.79611],80:[0,.68333,.08222,.08334,.69556],81:[.09722,.68333,0,.11111,.81667],82:[0,.68333,0,.08334,.8475],83:[0,.68333,.075,.13889,.60556],84:[0,.68333,.25417,0,.54464],85:[0,.68333,.09931,.08334,.62583],86:[0,.68333,.08222,0,.61278],87:[0,.68333,.08222,.08334,.98778],88:[0,.68333,.14643,.13889,.7133],89:[.09722,.68333,.08222,.08334,.66834],90:[0,.68333,.07944,.13889,.72473]},"Fraktur-Regular":{33:[0,.69141,0,0,.29574],34:[0,.69141,0,0,.21471],38:[0,.69141,0,0,.73786],39:[0,.69141,0,0,.21201],40:[.24982,.74947,0,0,.38865],41:[.24982,.74947,0,0,.38865],42:[0,.62119,0,0,.27764],43:[.08319,.58283,0,0,.75623],44:[0,.10803,0,0,.27764],45:[.08319,.58283,0,0,.75623],46:[0,.10803,0,0,.27764],47:[.24982,.74947,0,0,.50181],48:[0,.47534,0,0,.50181],49:[0,.47534,0,0,.50181],50:[0,.47534,0,0,.50181],51:[.18906,.47534,0,0,.50181],52:[.18906,.47534,0,0,.50181],53:[.18906,.47534,0,0,.50181],54:[0,.69141,0,0,.50181],55:[.18906,.47534,0,0,.50181],56:[0,.69141,0,0,.50181],57:[.18906,.47534,0,0,.50181],58:[0,.47534,0,0,.21606],59:[.12604,.47534,0,0,.21606],61:[-.13099,.36866,0,0,.75623],63:[0,.69141,0,0,.36245],65:[0,.69141,0,0,.7176],66:[0,.69141,0,0,.88397],67:[0,.69141,0,0,.61254],68:[0,.69141,0,0,.83158],69:[0,.69141,0,0,.66278],70:[.12604,.69141,0,0,.61119],71:[0,.69141,0,0,.78539],72:[.06302,.69141,0,0,.7203],73:[0,.69141,0,0,.55448],74:[.12604,.69141,0,0,.55231],75:[0,.69141,0,0,.66845],76:[0,.69141,0,0,.66602],77:[0,.69141,0,0,1.04953],78:[0,.69141,0,0,.83212],79:[0,.69141,0,0,.82699],80:[.18906,.69141,0,0,.82753],81:[.03781,.69141,0,0,.82699],82:[0,.69141,0,0,.82807],83:[0,.69141,0,0,.82861],84:[0,.69141,0,0,.66899],85:[0,.69141,0,0,.64576],86:[0,.69141,0,0,.83131],87:[0,.69141,0,0,1.04602],88:[0,.69141,0,0,.71922],89:[.18906,.69141,0,0,.83293],90:[.12604,.69141,0,0,.60201],91:[.24982,.74947,0,0,.27764],93:[.24982,.74947,0,0,.27764],94:[0,.69141,0,0,.49965],97:[0,.47534,0,0,.50046],98:[0,.69141,0,0,.51315],99:[0,.47534,0,0,.38946],100:[0,.62119,0,0,.49857],101:[0,.47534,0,0,.40053],102:[.18906,.69141,0,0,.32626],103:[.18906,.47534,0,0,.5037],104:[.18906,.69141,0,0,.52126],105:[0,.69141,0,0,.27899],106:[0,.69141,0,0,.28088],107:[0,.69141,0,0,.38946],108:[0,.69141,0,0,.27953],109:[0,.47534,0,0,.76676],110:[0,.47534,0,0,.52666],111:[0,.47534,0,0,.48885],112:[.18906,.52396,0,0,.50046],113:[.18906,.47534,0,0,.48912],114:[0,.47534,0,0,.38919],115:[0,.47534,0,0,.44266],116:[0,.62119,0,0,.33301],117:[0,.47534,0,0,.5172],118:[0,.52396,0,0,.5118],119:[0,.52396,0,0,.77351],120:[.18906,.47534,0,0,.38865],121:[.18906,.47534,0,0,.49884],122:[.18906,.47534,0,0,.39054],8216:[0,.69141,0,0,.21471],8217:[0,.69141,0,0,.21471],58112:[0,.62119,0,0,.49749],58113:[0,.62119,0,0,.4983],58114:[.18906,.69141,0,0,.33328],58115:[.18906,.69141,0,0,.32923],58116:[.18906,.47534,0,0,.50343],58117:[0,.69141,0,0,.33301],58118:[0,.62119,0,0,.33409],58119:[0,.47534,0,0,.50073]},"Main-Bold":{33:[0,.69444,0,0,.35],34:[0,.69444,0,0,.60278],35:[.19444,.69444,0,0,.95833],36:[.05556,.75,0,0,.575],37:[.05556,.75,0,0,.95833],38:[0,.69444,0,0,.89444],39:[0,.69444,0,0,.31944],40:[.25,.75,0,0,.44722],41:[.25,.75,0,0,.44722],42:[0,.75,0,0,.575],43:[.13333,.63333,0,0,.89444],44:[.19444,.15556,0,0,.31944],45:[0,.44444,0,0,.38333],46:[0,.15556,0,0,.31944],47:[.25,.75,0,0,.575],48:[0,.64444,0,0,.575],49:[0,.64444,0,0,.575],50:[0,.64444,0,0,.575],51:[0,.64444,0,0,.575],52:[0,.64444,0,0,.575],53:[0,.64444,0,0,.575],54:[0,.64444,0,0,.575],55:[0,.64444,0,0,.575],56:[0,.64444,0,0,.575],57:[0,.64444,0,0,.575],58:[0,.44444,0,0,.31944],59:[.19444,.44444,0,0,.31944],60:[.08556,.58556,0,0,.89444],61:[-.10889,.39111,0,0,.89444],62:[.08556,.58556,0,0,.89444],63:[0,.69444,0,0,.54305],64:[0,.69444,0,0,.89444],65:[0,.68611,0,0,.86944],66:[0,.68611,0,0,.81805],67:[0,.68611,0,0,.83055],68:[0,.68611,0,0,.88194],69:[0,.68611,0,0,.75555],70:[0,.68611,0,0,.72361],71:[0,.68611,0,0,.90416],72:[0,.68611,0,0,.9],73:[0,.68611,0,0,.43611],74:[0,.68611,0,0,.59444],75:[0,.68611,0,0,.90138],76:[0,.68611,0,0,.69166],77:[0,.68611,0,0,1.09166],78:[0,.68611,0,0,.9],79:[0,.68611,0,0,.86388],80:[0,.68611,0,0,.78611],81:[.19444,.68611,0,0,.86388],82:[0,.68611,0,0,.8625],83:[0,.68611,0,0,.63889],84:[0,.68611,0,0,.8],85:[0,.68611,0,0,.88472],86:[0,.68611,.01597,0,.86944],87:[0,.68611,.01597,0,1.18888],88:[0,.68611,0,0,.86944],89:[0,.68611,.02875,0,.86944],90:[0,.68611,0,0,.70277],91:[.25,.75,0,0,.31944],92:[.25,.75,0,0,.575],93:[.25,.75,0,0,.31944],94:[0,.69444,0,0,.575],95:[.31,.13444,.03194,0,.575],97:[0,.44444,0,0,.55902],98:[0,.69444,0,0,.63889],99:[0,.44444,0,0,.51111],100:[0,.69444,0,0,.63889],101:[0,.44444,0,0,.52708],102:[0,.69444,.10903,0,.35139],103:[.19444,.44444,.01597,0,.575],104:[0,.69444,0,0,.63889],105:[0,.69444,0,0,.31944],106:[.19444,.69444,0,0,.35139],107:[0,.69444,0,0,.60694],108:[0,.69444,0,0,.31944],109:[0,.44444,0,0,.95833],110:[0,.44444,0,0,.63889],111:[0,.44444,0,0,.575],112:[.19444,.44444,0,0,.63889],113:[.19444,.44444,0,0,.60694],114:[0,.44444,0,0,.47361],115:[0,.44444,0,0,.45361],116:[0,.63492,0,0,.44722],117:[0,.44444,0,0,.63889],118:[0,.44444,.01597,0,.60694],119:[0,.44444,.01597,0,.83055],120:[0,.44444,0,0,.60694],121:[.19444,.44444,.01597,0,.60694],122:[0,.44444,0,0,.51111],123:[.25,.75,0,0,.575],124:[.25,.75,0,0,.31944],125:[.25,.75,0,0,.575],126:[.35,.34444,0,0,.575],168:[0,.69444,0,0,.575],172:[0,.44444,0,0,.76666],176:[0,.69444,0,0,.86944],177:[.13333,.63333,0,0,.89444],184:[.17014,0,0,0,.51111],198:[0,.68611,0,0,1.04166],215:[.13333,.63333,0,0,.89444],216:[.04861,.73472,0,0,.89444],223:[0,.69444,0,0,.59722],230:[0,.44444,0,0,.83055],247:[.13333,.63333,0,0,.89444],248:[.09722,.54167,0,0,.575],305:[0,.44444,0,0,.31944],338:[0,.68611,0,0,1.16944],339:[0,.44444,0,0,.89444],567:[.19444,.44444,0,0,.35139],710:[0,.69444,0,0,.575],711:[0,.63194,0,0,.575],713:[0,.59611,0,0,.575],714:[0,.69444,0,0,.575],715:[0,.69444,0,0,.575],728:[0,.69444,0,0,.575],729:[0,.69444,0,0,.31944],730:[0,.69444,0,0,.86944],732:[0,.69444,0,0,.575],733:[0,.69444,0,0,.575],915:[0,.68611,0,0,.69166],916:[0,.68611,0,0,.95833],920:[0,.68611,0,0,.89444],923:[0,.68611,0,0,.80555],926:[0,.68611,0,0,.76666],928:[0,.68611,0,0,.9],931:[0,.68611,0,0,.83055],933:[0,.68611,0,0,.89444],934:[0,.68611,0,0,.83055],936:[0,.68611,0,0,.89444],937:[0,.68611,0,0,.83055],8211:[0,.44444,.03194,0,.575],8212:[0,.44444,.03194,0,1.14999],8216:[0,.69444,0,0,.31944],8217:[0,.69444,0,0,.31944],8220:[0,.69444,0,0,.60278],8221:[0,.69444,0,0,.60278],8224:[.19444,.69444,0,0,.51111],8225:[.19444,.69444,0,0,.51111],8242:[0,.55556,0,0,.34444],8407:[0,.72444,.15486,0,.575],8463:[0,.69444,0,0,.66759],8465:[0,.69444,0,0,.83055],8467:[0,.69444,0,0,.47361],8472:[.19444,.44444,0,0,.74027],8476:[0,.69444,0,0,.83055],8501:[0,.69444,0,0,.70277],8592:[-.10889,.39111,0,0,1.14999],8593:[.19444,.69444,0,0,.575],8594:[-.10889,.39111,0,0,1.14999],8595:[.19444,.69444,0,0,.575],8596:[-.10889,.39111,0,0,1.14999],8597:[.25,.75,0,0,.575],8598:[.19444,.69444,0,0,1.14999],8599:[.19444,.69444,0,0,1.14999],8600:[.19444,.69444,0,0,1.14999],8601:[.19444,.69444,0,0,1.14999],8636:[-.10889,.39111,0,0,1.14999],8637:[-.10889,.39111,0,0,1.14999],8640:[-.10889,.39111,0,0,1.14999],8641:[-.10889,.39111,0,0,1.14999],8656:[-.10889,.39111,0,0,1.14999],8657:[.19444,.69444,0,0,.70277],8658:[-.10889,.39111,0,0,1.14999],8659:[.19444,.69444,0,0,.70277],8660:[-.10889,.39111,0,0,1.14999],8661:[.25,.75,0,0,.70277],8704:[0,.69444,0,0,.63889],8706:[0,.69444,.06389,0,.62847],8707:[0,.69444,0,0,.63889],8709:[.05556,.75,0,0,.575],8711:[0,.68611,0,0,.95833],8712:[.08556,.58556,0,0,.76666],8715:[.08556,.58556,0,0,.76666],8722:[.13333,.63333,0,0,.89444],8723:[.13333,.63333,0,0,.89444],8725:[.25,.75,0,0,.575],8726:[.25,.75,0,0,.575],8727:[-.02778,.47222,0,0,.575],8728:[-.02639,.47361,0,0,.575],8729:[-.02639,.47361,0,0,.575],8730:[.18,.82,0,0,.95833],8733:[0,.44444,0,0,.89444],8734:[0,.44444,0,0,1.14999],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.31944],8741:[.25,.75,0,0,.575],8743:[0,.55556,0,0,.76666],8744:[0,.55556,0,0,.76666],8745:[0,.55556,0,0,.76666],8746:[0,.55556,0,0,.76666],8747:[.19444,.69444,.12778,0,.56875],8764:[-.10889,.39111,0,0,.89444],8768:[.19444,.69444,0,0,.31944],8771:[.00222,.50222,0,0,.89444],8776:[.02444,.52444,0,0,.89444],8781:[.00222,.50222,0,0,.89444],8801:[.00222,.50222,0,0,.89444],8804:[.19667,.69667,0,0,.89444],8805:[.19667,.69667,0,0,.89444],8810:[.08556,.58556,0,0,1.14999],8811:[.08556,.58556,0,0,1.14999],8826:[.08556,.58556,0,0,.89444],8827:[.08556,.58556,0,0,.89444],8834:[.08556,.58556,0,0,.89444],8835:[.08556,.58556,0,0,.89444],8838:[.19667,.69667,0,0,.89444],8839:[.19667,.69667,0,0,.89444],8846:[0,.55556,0,0,.76666],8849:[.19667,.69667,0,0,.89444],8850:[.19667,.69667,0,0,.89444],8851:[0,.55556,0,0,.76666],8852:[0,.55556,0,0,.76666],8853:[.13333,.63333,0,0,.89444],8854:[.13333,.63333,0,0,.89444],8855:[.13333,.63333,0,0,.89444],8856:[.13333,.63333,0,0,.89444],8857:[.13333,.63333,0,0,.89444],8866:[0,.69444,0,0,.70277],8867:[0,.69444,0,0,.70277],8868:[0,.69444,0,0,.89444],8869:[0,.69444,0,0,.89444],8900:[-.02639,.47361,0,0,.575],8901:[-.02639,.47361,0,0,.31944],8902:[-.02778,.47222,0,0,.575],8968:[.25,.75,0,0,.51111],8969:[.25,.75,0,0,.51111],8970:[.25,.75,0,0,.51111],8971:[.25,.75,0,0,.51111],8994:[-.13889,.36111,0,0,1.14999],8995:[-.13889,.36111,0,0,1.14999],9651:[.19444,.69444,0,0,1.02222],9657:[-.02778,.47222,0,0,.575],9661:[.19444,.69444,0,0,1.02222],9667:[-.02778,.47222,0,0,.575],9711:[.19444,.69444,0,0,1.14999],9824:[.12963,.69444,0,0,.89444],9825:[.12963,.69444,0,0,.89444],9826:[.12963,.69444,0,0,.89444],9827:[.12963,.69444,0,0,.89444],9837:[0,.75,0,0,.44722],9838:[.19444,.69444,0,0,.44722],9839:[.19444,.69444,0,0,.44722],10216:[.25,.75,0,0,.44722],10217:[.25,.75,0,0,.44722],10815:[0,.68611,0,0,.9],10927:[.19667,.69667,0,0,.89444],10928:[.19667,.69667,0,0,.89444],57376:[.19444,.69444,0,0,0]},"Main-BoldItalic":{33:[0,.69444,.11417,0,.38611],34:[0,.69444,.07939,0,.62055],35:[.19444,.69444,.06833,0,.94444],37:[.05556,.75,.12861,0,.94444],38:[0,.69444,.08528,0,.88555],39:[0,.69444,.12945,0,.35555],40:[.25,.75,.15806,0,.47333],41:[.25,.75,.03306,0,.47333],42:[0,.75,.14333,0,.59111],43:[.10333,.60333,.03306,0,.88555],44:[.19444,.14722,0,0,.35555],45:[0,.44444,.02611,0,.41444],46:[0,.14722,0,0,.35555],47:[.25,.75,.15806,0,.59111],48:[0,.64444,.13167,0,.59111],49:[0,.64444,.13167,0,.59111],50:[0,.64444,.13167,0,.59111],51:[0,.64444,.13167,0,.59111],52:[.19444,.64444,.13167,0,.59111],53:[0,.64444,.13167,0,.59111],54:[0,.64444,.13167,0,.59111],55:[.19444,.64444,.13167,0,.59111],56:[0,.64444,.13167,0,.59111],57:[0,.64444,.13167,0,.59111],58:[0,.44444,.06695,0,.35555],59:[.19444,.44444,.06695,0,.35555],61:[-.10889,.39111,.06833,0,.88555],63:[0,.69444,.11472,0,.59111],64:[0,.69444,.09208,0,.88555],65:[0,.68611,0,0,.86555],66:[0,.68611,.0992,0,.81666],67:[0,.68611,.14208,0,.82666],68:[0,.68611,.09062,0,.87555],69:[0,.68611,.11431,0,.75666],70:[0,.68611,.12903,0,.72722],71:[0,.68611,.07347,0,.89527],72:[0,.68611,.17208,0,.8961],73:[0,.68611,.15681,0,.47166],74:[0,.68611,.145,0,.61055],75:[0,.68611,.14208,0,.89499],76:[0,.68611,0,0,.69777],77:[0,.68611,.17208,0,1.07277],78:[0,.68611,.17208,0,.8961],79:[0,.68611,.09062,0,.85499],80:[0,.68611,.0992,0,.78721],81:[.19444,.68611,.09062,0,.85499],82:[0,.68611,.02559,0,.85944],83:[0,.68611,.11264,0,.64999],84:[0,.68611,.12903,0,.7961],85:[0,.68611,.17208,0,.88083],86:[0,.68611,.18625,0,.86555],87:[0,.68611,.18625,0,1.15999],88:[0,.68611,.15681,0,.86555],89:[0,.68611,.19803,0,.86555],90:[0,.68611,.14208,0,.70888],91:[.25,.75,.1875,0,.35611],93:[.25,.75,.09972,0,.35611],94:[0,.69444,.06709,0,.59111],95:[.31,.13444,.09811,0,.59111],97:[0,.44444,.09426,0,.59111],98:[0,.69444,.07861,0,.53222],99:[0,.44444,.05222,0,.53222],100:[0,.69444,.10861,0,.59111],101:[0,.44444,.085,0,.53222],102:[.19444,.69444,.21778,0,.4],103:[.19444,.44444,.105,0,.53222],104:[0,.69444,.09426,0,.59111],105:[0,.69326,.11387,0,.35555],106:[.19444,.69326,.1672,0,.35555],107:[0,.69444,.11111,0,.53222],108:[0,.69444,.10861,0,.29666],109:[0,.44444,.09426,0,.94444],110:[0,.44444,.09426,0,.64999],111:[0,.44444,.07861,0,.59111],112:[.19444,.44444,.07861,0,.59111],113:[.19444,.44444,.105,0,.53222],114:[0,.44444,.11111,0,.50167],115:[0,.44444,.08167,0,.48694],116:[0,.63492,.09639,0,.385],117:[0,.44444,.09426,0,.62055],118:[0,.44444,.11111,0,.53222],119:[0,.44444,.11111,0,.76777],120:[0,.44444,.12583,0,.56055],121:[.19444,.44444,.105,0,.56166],122:[0,.44444,.13889,0,.49055],126:[.35,.34444,.11472,0,.59111],163:[0,.69444,0,0,.86853],168:[0,.69444,.11473,0,.59111],176:[0,.69444,0,0,.94888],184:[.17014,0,0,0,.53222],198:[0,.68611,.11431,0,1.02277],216:[.04861,.73472,.09062,0,.88555],223:[.19444,.69444,.09736,0,.665],230:[0,.44444,.085,0,.82666],248:[.09722,.54167,.09458,0,.59111],305:[0,.44444,.09426,0,.35555],338:[0,.68611,.11431,0,1.14054],339:[0,.44444,.085,0,.82666],567:[.19444,.44444,.04611,0,.385],710:[0,.69444,.06709,0,.59111],711:[0,.63194,.08271,0,.59111],713:[0,.59444,.10444,0,.59111],714:[0,.69444,.08528,0,.59111],715:[0,.69444,0,0,.59111],728:[0,.69444,.10333,0,.59111],729:[0,.69444,.12945,0,.35555],730:[0,.69444,0,0,.94888],732:[0,.69444,.11472,0,.59111],733:[0,.69444,.11472,0,.59111],915:[0,.68611,.12903,0,.69777],916:[0,.68611,0,0,.94444],920:[0,.68611,.09062,0,.88555],923:[0,.68611,0,0,.80666],926:[0,.68611,.15092,0,.76777],928:[0,.68611,.17208,0,.8961],931:[0,.68611,.11431,0,.82666],933:[0,.68611,.10778,0,.88555],934:[0,.68611,.05632,0,.82666],936:[0,.68611,.10778,0,.88555],937:[0,.68611,.0992,0,.82666],8211:[0,.44444,.09811,0,.59111],8212:[0,.44444,.09811,0,1.18221],8216:[0,.69444,.12945,0,.35555],8217:[0,.69444,.12945,0,.35555],8220:[0,.69444,.16772,0,.62055],8221:[0,.69444,.07939,0,.62055]},"Main-Italic":{33:[0,.69444,.12417,0,.30667],34:[0,.69444,.06961,0,.51444],35:[.19444,.69444,.06616,0,.81777],37:[.05556,.75,.13639,0,.81777],38:[0,.69444,.09694,0,.76666],39:[0,.69444,.12417,0,.30667],40:[.25,.75,.16194,0,.40889],41:[.25,.75,.03694,0,.40889],42:[0,.75,.14917,0,.51111],43:[.05667,.56167,.03694,0,.76666],44:[.19444,.10556,0,0,.30667],45:[0,.43056,.02826,0,.35778],46:[0,.10556,0,0,.30667],47:[.25,.75,.16194,0,.51111],48:[0,.64444,.13556,0,.51111],49:[0,.64444,.13556,0,.51111],50:[0,.64444,.13556,0,.51111],51:[0,.64444,.13556,0,.51111],52:[.19444,.64444,.13556,0,.51111],53:[0,.64444,.13556,0,.51111],54:[0,.64444,.13556,0,.51111],55:[.19444,.64444,.13556,0,.51111],56:[0,.64444,.13556,0,.51111],57:[0,.64444,.13556,0,.51111],58:[0,.43056,.0582,0,.30667],59:[.19444,.43056,.0582,0,.30667],61:[-.13313,.36687,.06616,0,.76666],63:[0,.69444,.1225,0,.51111],64:[0,.69444,.09597,0,.76666],65:[0,.68333,0,0,.74333],66:[0,.68333,.10257,0,.70389],67:[0,.68333,.14528,0,.71555],68:[0,.68333,.09403,0,.755],69:[0,.68333,.12028,0,.67833],70:[0,.68333,.13305,0,.65277],71:[0,.68333,.08722,0,.77361],72:[0,.68333,.16389,0,.74333],73:[0,.68333,.15806,0,.38555],74:[0,.68333,.14028,0,.525],75:[0,.68333,.14528,0,.76888],76:[0,.68333,0,0,.62722],77:[0,.68333,.16389,0,.89666],78:[0,.68333,.16389,0,.74333],79:[0,.68333,.09403,0,.76666],80:[0,.68333,.10257,0,.67833],81:[.19444,.68333,.09403,0,.76666],82:[0,.68333,.03868,0,.72944],83:[0,.68333,.11972,0,.56222],84:[0,.68333,.13305,0,.71555],85:[0,.68333,.16389,0,.74333],86:[0,.68333,.18361,0,.74333],87:[0,.68333,.18361,0,.99888],88:[0,.68333,.15806,0,.74333],89:[0,.68333,.19383,0,.74333],90:[0,.68333,.14528,0,.61333],91:[.25,.75,.1875,0,.30667],93:[.25,.75,.10528,0,.30667],94:[0,.69444,.06646,0,.51111],95:[.31,.12056,.09208,0,.51111],97:[0,.43056,.07671,0,.51111],98:[0,.69444,.06312,0,.46],99:[0,.43056,.05653,0,.46],100:[0,.69444,.10333,0,.51111],101:[0,.43056,.07514,0,.46],102:[.19444,.69444,.21194,0,.30667],103:[.19444,.43056,.08847,0,.46],104:[0,.69444,.07671,0,.51111],105:[0,.65536,.1019,0,.30667],106:[.19444,.65536,.14467,0,.30667],107:[0,.69444,.10764,0,.46],108:[0,.69444,.10333,0,.25555],109:[0,.43056,.07671,0,.81777],110:[0,.43056,.07671,0,.56222],111:[0,.43056,.06312,0,.51111],112:[.19444,.43056,.06312,0,.51111],113:[.19444,.43056,.08847,0,.46],114:[0,.43056,.10764,0,.42166],115:[0,.43056,.08208,0,.40889],116:[0,.61508,.09486,0,.33222],117:[0,.43056,.07671,0,.53666],118:[0,.43056,.10764,0,.46],119:[0,.43056,.10764,0,.66444],120:[0,.43056,.12042,0,.46389],121:[.19444,.43056,.08847,0,.48555],122:[0,.43056,.12292,0,.40889],126:[.35,.31786,.11585,0,.51111],163:[0,.69444,0,0,.76909],168:[0,.66786,.10474,0,.51111],176:[0,.69444,0,0,.83129],184:[.17014,0,0,0,.46],198:[0,.68333,.12028,0,.88277],216:[.04861,.73194,.09403,0,.76666],223:[.19444,.69444,.10514,0,.53666],230:[0,.43056,.07514,0,.71555],248:[.09722,.52778,.09194,0,.51111],305:[0,.43056,0,.02778,.32246],338:[0,.68333,.12028,0,.98499],339:[0,.43056,.07514,0,.71555],567:[.19444,.43056,0,.08334,.38403],710:[0,.69444,.06646,0,.51111],711:[0,.62847,.08295,0,.51111],713:[0,.56167,.10333,0,.51111],714:[0,.69444,.09694,0,.51111],715:[0,.69444,0,0,.51111],728:[0,.69444,.10806,0,.51111],729:[0,.66786,.11752,0,.30667],730:[0,.69444,0,0,.83129],732:[0,.66786,.11585,0,.51111],733:[0,.69444,.1225,0,.51111],915:[0,.68333,.13305,0,.62722],916:[0,.68333,0,0,.81777],920:[0,.68333,.09403,0,.76666],923:[0,.68333,0,0,.69222],926:[0,.68333,.15294,0,.66444],928:[0,.68333,.16389,0,.74333],931:[0,.68333,.12028,0,.71555],933:[0,.68333,.11111,0,.76666],934:[0,.68333,.05986,0,.71555],936:[0,.68333,.11111,0,.76666],937:[0,.68333,.10257,0,.71555],8211:[0,.43056,.09208,0,.51111],8212:[0,.43056,.09208,0,1.02222],8216:[0,.69444,.12417,0,.30667],8217:[0,.69444,.12417,0,.30667],8220:[0,.69444,.1685,0,.51444],8221:[0,.69444,.06961,0,.51444],8463:[0,.68889,0,0,.54028]},"Main-Regular":{32:[0,0,0,0,.25],33:[0,.69444,0,0,.27778],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.77778],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.19444,.10556,0,0,.27778],45:[0,.43056,0,0,.33333],46:[0,.10556,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.64444,0,0,.5],49:[0,.64444,0,0,.5],50:[0,.64444,0,0,.5],51:[0,.64444,0,0,.5],52:[0,.64444,0,0,.5],53:[0,.64444,0,0,.5],54:[0,.64444,0,0,.5],55:[0,.64444,0,0,.5],56:[0,.64444,0,0,.5],57:[0,.64444,0,0,.5],58:[0,.43056,0,0,.27778],59:[.19444,.43056,0,0,.27778],60:[.0391,.5391,0,0,.77778],61:[-.13313,.36687,0,0,.77778],62:[.0391,.5391,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.77778],65:[0,.68333,0,0,.75],66:[0,.68333,0,0,.70834],67:[0,.68333,0,0,.72222],68:[0,.68333,0,0,.76389],69:[0,.68333,0,0,.68056],70:[0,.68333,0,0,.65278],71:[0,.68333,0,0,.78472],72:[0,.68333,0,0,.75],73:[0,.68333,0,0,.36111],74:[0,.68333,0,0,.51389],75:[0,.68333,0,0,.77778],76:[0,.68333,0,0,.625],77:[0,.68333,0,0,.91667],78:[0,.68333,0,0,.75],79:[0,.68333,0,0,.77778],80:[0,.68333,0,0,.68056],81:[.19444,.68333,0,0,.77778],82:[0,.68333,0,0,.73611],83:[0,.68333,0,0,.55556],84:[0,.68333,0,0,.72222],85:[0,.68333,0,0,.75],86:[0,.68333,.01389,0,.75],87:[0,.68333,.01389,0,1.02778],88:[0,.68333,0,0,.75],89:[0,.68333,.025,0,.75],90:[0,.68333,0,0,.61111],91:[.25,.75,0,0,.27778],92:[.25,.75,0,0,.5],93:[.25,.75,0,0,.27778],94:[0,.69444,0,0,.5],95:[.31,.12056,.02778,0,.5],97:[0,.43056,0,0,.5],98:[0,.69444,0,0,.55556],99:[0,.43056,0,0,.44445],100:[0,.69444,0,0,.55556],101:[0,.43056,0,0,.44445],102:[0,.69444,.07778,0,.30556],103:[.19444,.43056,.01389,0,.5],104:[0,.69444,0,0,.55556],105:[0,.66786,0,0,.27778],106:[.19444,.66786,0,0,.30556],107:[0,.69444,0,0,.52778],108:[0,.69444,0,0,.27778],109:[0,.43056,0,0,.83334],110:[0,.43056,0,0,.55556],111:[0,.43056,0,0,.5],112:[.19444,.43056,0,0,.55556],113:[.19444,.43056,0,0,.52778],114:[0,.43056,0,0,.39167],115:[0,.43056,0,0,.39445],116:[0,.61508,0,0,.38889],117:[0,.43056,0,0,.55556],118:[0,.43056,.01389,0,.52778],119:[0,.43056,.01389,0,.72222],120:[0,.43056,0,0,.52778],121:[.19444,.43056,.01389,0,.52778],122:[0,.43056,0,0,.44445],123:[.25,.75,0,0,.5],124:[.25,.75,0,0,.27778],125:[.25,.75,0,0,.5],126:[.35,.31786,0,0,.5],160:[0,0,0,0,.25],167:[.19444,.69444,0,0,.44445],168:[0,.66786,0,0,.5],172:[0,.43056,0,0,.66667],176:[0,.69444,0,0,.75],177:[.08333,.58333,0,0,.77778],182:[.19444,.69444,0,0,.61111],184:[.17014,0,0,0,.44445],198:[0,.68333,0,0,.90278],215:[.08333,.58333,0,0,.77778],216:[.04861,.73194,0,0,.77778],223:[0,.69444,0,0,.5],230:[0,.43056,0,0,.72222],247:[.08333,.58333,0,0,.77778],248:[.09722,.52778,0,0,.5],305:[0,.43056,0,0,.27778],338:[0,.68333,0,0,1.01389],339:[0,.43056,0,0,.77778],567:[.19444,.43056,0,0,.30556],710:[0,.69444,0,0,.5],711:[0,.62847,0,0,.5],713:[0,.56778,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.66786,0,0,.27778],730:[0,.69444,0,0,.75],732:[0,.66786,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.68333,0,0,.625],916:[0,.68333,0,0,.83334],920:[0,.68333,0,0,.77778],923:[0,.68333,0,0,.69445],926:[0,.68333,0,0,.66667],928:[0,.68333,0,0,.75],931:[0,.68333,0,0,.72222],933:[0,.68333,0,0,.77778],934:[0,.68333,0,0,.72222],936:[0,.68333,0,0,.77778],937:[0,.68333,0,0,.72222],8211:[0,.43056,.02778,0,.5],8212:[0,.43056,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5],8224:[.19444,.69444,0,0,.44445],8225:[.19444,.69444,0,0,.44445],8230:[0,.12,0,0,1.172],8242:[0,.55556,0,0,.275],8407:[0,.71444,.15382,0,.5],8463:[0,.68889,0,0,.54028],8465:[0,.69444,0,0,.72222],8467:[0,.69444,0,.11111,.41667],8472:[.19444,.43056,0,.11111,.63646],8476:[0,.69444,0,0,.72222],8501:[0,.69444,0,0,.61111],8592:[-.13313,.36687,0,0,1],8593:[.19444,.69444,0,0,.5],8594:[-.13313,.36687,0,0,1],8595:[.19444,.69444,0,0,.5],8596:[-.13313,.36687,0,0,1],8597:[.25,.75,0,0,.5],8598:[.19444,.69444,0,0,1],8599:[.19444,.69444,0,0,1],8600:[.19444,.69444,0,0,1],8601:[.19444,.69444,0,0,1],8614:[.011,.511,0,0,1],8617:[.011,.511,0,0,1.126],8618:[.011,.511,0,0,1.126],8636:[-.13313,.36687,0,0,1],8637:[-.13313,.36687,0,0,1],8640:[-.13313,.36687,0,0,1],8641:[-.13313,.36687,0,0,1],8652:[.011,.671,0,0,1],8656:[-.13313,.36687,0,0,1],8657:[.19444,.69444,0,0,.61111],8658:[-.13313,.36687,0,0,1],8659:[.19444,.69444,0,0,.61111],8660:[-.13313,.36687,0,0,1],8661:[.25,.75,0,0,.61111],8704:[0,.69444,0,0,.55556],8706:[0,.69444,.05556,.08334,.5309],8707:[0,.69444,0,0,.55556],8709:[.05556,.75,0,0,.5],8711:[0,.68333,0,0,.83334],8712:[.0391,.5391,0,0,.66667],8715:[.0391,.5391,0,0,.66667],8722:[.08333,.58333,0,0,.77778],8723:[.08333,.58333,0,0,.77778],8725:[.25,.75,0,0,.5],8726:[.25,.75,0,0,.5],8727:[-.03472,.46528,0,0,.5],8728:[-.05555,.44445,0,0,.5],8729:[-.05555,.44445,0,0,.5],8730:[.2,.8,0,0,.83334],8733:[0,.43056,0,0,.77778],8734:[0,.43056,0,0,1],8736:[0,.69224,0,0,.72222],8739:[.25,.75,0,0,.27778],8741:[.25,.75,0,0,.5],8743:[0,.55556,0,0,.66667],8744:[0,.55556,0,0,.66667],8745:[0,.55556,0,0,.66667],8746:[0,.55556,0,0,.66667],8747:[.19444,.69444,.11111,0,.41667],8764:[-.13313,.36687,0,0,.77778],8768:[.19444,.69444,0,0,.27778],8771:[-.03625,.46375,0,0,.77778],8773:[-.022,.589,0,0,1],8776:[-.01688,.48312,0,0,.77778],8781:[-.03625,.46375,0,0,.77778],8784:[-.133,.67,0,0,.778],8801:[-.03625,.46375,0,0,.77778],8804:[.13597,.63597,0,0,.77778],8805:[.13597,.63597,0,0,.77778],8810:[.0391,.5391,0,0,1],8811:[.0391,.5391,0,0,1],8826:[.0391,.5391,0,0,.77778],8827:[.0391,.5391,0,0,.77778],8834:[.0391,.5391,0,0,.77778],8835:[.0391,.5391,0,0,.77778],8838:[.13597,.63597,0,0,.77778],8839:[.13597,.63597,0,0,.77778],8846:[0,.55556,0,0,.66667],8849:[.13597,.63597,0,0,.77778],8850:[.13597,.63597,0,0,.77778],8851:[0,.55556,0,0,.66667],8852:[0,.55556,0,0,.66667],8853:[.08333,.58333,0,0,.77778],8854:[.08333,.58333,0,0,.77778],8855:[.08333,.58333,0,0,.77778],8856:[.08333,.58333,0,0,.77778],8857:[.08333,.58333,0,0,.77778],8866:[0,.69444,0,0,.61111],8867:[0,.69444,0,0,.61111],8868:[0,.69444,0,0,.77778],8869:[0,.69444,0,0,.77778],8872:[.249,.75,0,0,.867],8900:[-.05555,.44445,0,0,.5],8901:[-.05555,.44445,0,0,.27778],8902:[-.03472,.46528,0,0,.5],8904:[.005,.505,0,0,.9],8942:[.03,.9,0,0,.278],8943:[-.19,.31,0,0,1.172],8945:[-.1,.82,0,0,1.282],8968:[.25,.75,0,0,.44445],8969:[.25,.75,0,0,.44445],8970:[.25,.75,0,0,.44445],8971:[.25,.75,0,0,.44445],8994:[-.14236,.35764,0,0,1],8995:[-.14236,.35764,0,0,1],9136:[.244,.744,0,0,.412],9137:[.244,.744,0,0,.412],9651:[.19444,.69444,0,0,.88889],9657:[-.03472,.46528,0,0,.5],9661:[.19444,.69444,0,0,.88889],9667:[-.03472,.46528,0,0,.5],9711:[.19444,.69444,0,0,1],9824:[.12963,.69444,0,0,.77778],9825:[.12963,.69444,0,0,.77778],9826:[.12963,.69444,0,0,.77778],9827:[.12963,.69444,0,0,.77778],9837:[0,.75,0,0,.38889],9838:[.19444,.69444,0,0,.38889],9839:[.19444,.69444,0,0,.38889],10216:[.25,.75,0,0,.38889],10217:[.25,.75,0,0,.38889],10222:[.244,.744,0,0,.412],10223:[.244,.744,0,0,.412],10229:[.011,.511,0,0,1.609],10230:[.011,.511,0,0,1.638],10231:[.011,.511,0,0,1.859],10232:[.024,.525,0,0,1.609],10233:[.024,.525,0,0,1.638],10234:[.024,.525,0,0,1.858],10236:[.011,.511,0,0,1.638],10815:[0,.68333,0,0,.75],10927:[.13597,.63597,0,0,.77778],10928:[.13597,.63597,0,0,.77778],57376:[.19444,.69444,0,0,0]},"Math-BoldItalic":{65:[0,.68611,0,0,.86944],66:[0,.68611,.04835,0,.8664],67:[0,.68611,.06979,0,.81694],68:[0,.68611,.03194,0,.93812],69:[0,.68611,.05451,0,.81007],70:[0,.68611,.15972,0,.68889],71:[0,.68611,0,0,.88673],72:[0,.68611,.08229,0,.98229],73:[0,.68611,.07778,0,.51111],74:[0,.68611,.10069,0,.63125],75:[0,.68611,.06979,0,.97118],76:[0,.68611,0,0,.75555],77:[0,.68611,.11424,0,1.14201],78:[0,.68611,.11424,0,.95034],79:[0,.68611,.03194,0,.83666],80:[0,.68611,.15972,0,.72309],81:[.19444,.68611,0,0,.86861],82:[0,.68611,.00421,0,.87235],83:[0,.68611,.05382,0,.69271],84:[0,.68611,.15972,0,.63663],85:[0,.68611,.11424,0,.80027],86:[0,.68611,.25555,0,.67778],87:[0,.68611,.15972,0,1.09305],88:[0,.68611,.07778,0,.94722],89:[0,.68611,.25555,0,.67458],90:[0,.68611,.06979,0,.77257],97:[0,.44444,0,0,.63287],98:[0,.69444,0,0,.52083],99:[0,.44444,0,0,.51342],100:[0,.69444,0,0,.60972],101:[0,.44444,0,0,.55361],102:[.19444,.69444,.11042,0,.56806],103:[.19444,.44444,.03704,0,.5449],104:[0,.69444,0,0,.66759],105:[0,.69326,0,0,.4048],106:[.19444,.69326,.0622,0,.47083],107:[0,.69444,.01852,0,.6037],108:[0,.69444,.0088,0,.34815],109:[0,.44444,0,0,1.0324],110:[0,.44444,0,0,.71296],111:[0,.44444,0,0,.58472],112:[.19444,.44444,0,0,.60092],113:[.19444,.44444,.03704,0,.54213],114:[0,.44444,.03194,0,.5287],115:[0,.44444,0,0,.53125],116:[0,.63492,0,0,.41528],117:[0,.44444,0,0,.68102],118:[0,.44444,.03704,0,.56666],119:[0,.44444,.02778,0,.83148],120:[0,.44444,0,0,.65903],121:[.19444,.44444,.03704,0,.59028],122:[0,.44444,.04213,0,.55509],915:[0,.68611,.15972,0,.65694],916:[0,.68611,0,0,.95833],920:[0,.68611,.03194,0,.86722],923:[0,.68611,0,0,.80555],926:[0,.68611,.07458,0,.84125],928:[0,.68611,.08229,0,.98229],931:[0,.68611,.05451,0,.88507],933:[0,.68611,.15972,0,.67083],934:[0,.68611,0,0,.76666],936:[0,.68611,.11653,0,.71402],937:[0,.68611,.04835,0,.8789],945:[0,.44444,0,0,.76064],946:[.19444,.69444,.03403,0,.65972],947:[.19444,.44444,.06389,0,.59003],948:[0,.69444,.03819,0,.52222],949:[0,.44444,0,0,.52882],950:[.19444,.69444,.06215,0,.50833],951:[.19444,.44444,.03704,0,.6],952:[0,.69444,.03194,0,.5618],953:[0,.44444,0,0,.41204],954:[0,.44444,0,0,.66759],955:[0,.69444,0,0,.67083],956:[.19444,.44444,0,0,.70787],957:[0,.44444,.06898,0,.57685],958:[.19444,.69444,.03021,0,.50833],959:[0,.44444,0,0,.58472],960:[0,.44444,.03704,0,.68241],961:[.19444,.44444,0,0,.6118],962:[.09722,.44444,.07917,0,.42361],963:[0,.44444,.03704,0,.68588],964:[0,.44444,.13472,0,.52083],965:[0,.44444,.03704,0,.63055],966:[.19444,.44444,0,0,.74722],967:[.19444,.44444,0,0,.71805],968:[.19444,.69444,.03704,0,.75833],969:[0,.44444,.03704,0,.71782],977:[0,.69444,0,0,.69155],981:[.19444,.69444,0,0,.7125],982:[0,.44444,.03194,0,.975],1009:[.19444,.44444,0,0,.6118],1013:[0,.44444,0,0,.48333]},"Math-Italic":{65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059]},"Math-Regular":{65:[0,.68333,0,.13889,.75],66:[0,.68333,.05017,.08334,.75851],67:[0,.68333,.07153,.08334,.71472],68:[0,.68333,.02778,.05556,.82792],69:[0,.68333,.05764,.08334,.7382],70:[0,.68333,.13889,.08334,.64306],71:[0,.68333,0,.08334,.78625],72:[0,.68333,.08125,.05556,.83125],73:[0,.68333,.07847,.11111,.43958],74:[0,.68333,.09618,.16667,.55451],75:[0,.68333,.07153,.05556,.84931],76:[0,.68333,0,.02778,.68056],77:[0,.68333,.10903,.08334,.97014],78:[0,.68333,.10903,.08334,.80347],79:[0,.68333,.02778,.08334,.76278],80:[0,.68333,.13889,.08334,.64201],81:[.19444,.68333,0,.08334,.79056],82:[0,.68333,.00773,.08334,.75929],83:[0,.68333,.05764,.08334,.6132],84:[0,.68333,.13889,.08334,.58438],85:[0,.68333,.10903,.02778,.68278],86:[0,.68333,.22222,0,.58333],87:[0,.68333,.13889,0,.94445],88:[0,.68333,.07847,.08334,.82847],89:[0,.68333,.22222,0,.58056],90:[0,.68333,.07153,.08334,.68264],97:[0,.43056,0,0,.52859],98:[0,.69444,0,0,.42917],99:[0,.43056,0,.05556,.43276],100:[0,.69444,0,.16667,.52049],101:[0,.43056,0,.05556,.46563],102:[.19444,.69444,.10764,.16667,.48959],103:[.19444,.43056,.03588,.02778,.47697],104:[0,.69444,0,0,.57616],105:[0,.65952,0,0,.34451],106:[.19444,.65952,.05724,0,.41181],107:[0,.69444,.03148,0,.5206],108:[0,.69444,.01968,.08334,.29838],109:[0,.43056,0,0,.87801],110:[0,.43056,0,0,.60023],111:[0,.43056,0,.05556,.48472],112:[.19444,.43056,0,.08334,.50313],113:[.19444,.43056,.03588,.08334,.44641],114:[0,.43056,.02778,.05556,.45116],115:[0,.43056,0,.05556,.46875],116:[0,.61508,0,.08334,.36111],117:[0,.43056,0,.02778,.57246],118:[0,.43056,.03588,.02778,.48472],119:[0,.43056,.02691,.08334,.71592],120:[0,.43056,0,.02778,.57153],121:[.19444,.43056,.03588,.05556,.49028],122:[0,.43056,.04398,.05556,.46505],915:[0,.68333,.13889,.08334,.61528],916:[0,.68333,0,.16667,.83334],920:[0,.68333,.02778,.08334,.76278],923:[0,.68333,0,.16667,.69445],926:[0,.68333,.07569,.08334,.74236],928:[0,.68333,.08125,.05556,.83125],931:[0,.68333,.05764,.08334,.77986],933:[0,.68333,.13889,.05556,.58333],934:[0,.68333,0,.08334,.66667],936:[0,.68333,.11,.05556,.61222],937:[0,.68333,.05017,.08334,.7724],945:[0,.43056,.0037,.02778,.6397],946:[.19444,.69444,.05278,.08334,.56563],947:[.19444,.43056,.05556,0,.51773],948:[0,.69444,.03785,.05556,.44444],949:[0,.43056,0,.08334,.46632],950:[.19444,.69444,.07378,.08334,.4375],951:[.19444,.43056,.03588,.05556,.49653],952:[0,.69444,.02778,.08334,.46944],953:[0,.43056,0,.05556,.35394],954:[0,.43056,0,0,.57616],955:[0,.69444,0,0,.58334],956:[.19444,.43056,0,.02778,.60255],957:[0,.43056,.06366,.02778,.49398],958:[.19444,.69444,.04601,.11111,.4375],959:[0,.43056,0,.05556,.48472],960:[0,.43056,.03588,0,.57003],961:[.19444,.43056,0,.08334,.51702],962:[.09722,.43056,.07986,.08334,.36285],963:[0,.43056,.03588,0,.57141],964:[0,.43056,.1132,.02778,.43715],965:[0,.43056,.03588,.02778,.54028],966:[.19444,.43056,0,.08334,.65417],967:[.19444,.43056,0,.05556,.62569],968:[.19444,.69444,.03588,.11111,.65139],969:[0,.43056,.03588,0,.62245],977:[0,.69444,0,.08334,.59144],981:[.19444,.69444,0,.08334,.59583],982:[0,.43056,.02778,0,.82813],1009:[.19444,.43056,0,.08334,.51702],1013:[0,.43056,0,.05556,.4059]},"SansSerif-Bold":{33:[0,.69444,0,0,.36667],34:[0,.69444,0,0,.55834],35:[.19444,.69444,0,0,.91667],36:[.05556,.75,0,0,.55],37:[.05556,.75,0,0,1.02912],38:[0,.69444,0,0,.83056],39:[0,.69444,0,0,.30556],40:[.25,.75,0,0,.42778],41:[.25,.75,0,0,.42778],42:[0,.75,0,0,.55],43:[.11667,.61667,0,0,.85556],44:[.10556,.13056,0,0,.30556],45:[0,.45833,0,0,.36667],46:[0,.13056,0,0,.30556],47:[.25,.75,0,0,.55],48:[0,.69444,0,0,.55],49:[0,.69444,0,0,.55],50:[0,.69444,0,0,.55],51:[0,.69444,0,0,.55],52:[0,.69444,0,0,.55],53:[0,.69444,0,0,.55],54:[0,.69444,0,0,.55],55:[0,.69444,0,0,.55],56:[0,.69444,0,0,.55],57:[0,.69444,0,0,.55],58:[0,.45833,0,0,.30556],59:[.10556,.45833,0,0,.30556],61:[-.09375,.40625,0,0,.85556],63:[0,.69444,0,0,.51945],64:[0,.69444,0,0,.73334],65:[0,.69444,0,0,.73334],66:[0,.69444,0,0,.73334],67:[0,.69444,0,0,.70278],68:[0,.69444,0,0,.79445],69:[0,.69444,0,0,.64167],70:[0,.69444,0,0,.61111],71:[0,.69444,0,0,.73334],72:[0,.69444,0,0,.79445],73:[0,.69444,0,0,.33056],74:[0,.69444,0,0,.51945],75:[0,.69444,0,0,.76389],76:[0,.69444,0,0,.58056],77:[0,.69444,0,0,.97778],78:[0,.69444,0,0,.79445],79:[0,.69444,0,0,.79445],80:[0,.69444,0,0,.70278],81:[.10556,.69444,0,0,.79445],82:[0,.69444,0,0,.70278],83:[0,.69444,0,0,.61111],84:[0,.69444,0,0,.73334],85:[0,.69444,0,0,.76389],86:[0,.69444,.01528,0,.73334],87:[0,.69444,.01528,0,1.03889],88:[0,.69444,0,0,.73334],89:[0,.69444,.0275,0,.73334],90:[0,.69444,0,0,.67223],91:[.25,.75,0,0,.34306],93:[.25,.75,0,0,.34306],94:[0,.69444,0,0,.55],95:[.35,.10833,.03056,0,.55],97:[0,.45833,0,0,.525],98:[0,.69444,0,0,.56111],99:[0,.45833,0,0,.48889],100:[0,.69444,0,0,.56111],101:[0,.45833,0,0,.51111],102:[0,.69444,.07639,0,.33611],103:[.19444,.45833,.01528,0,.55],104:[0,.69444,0,0,.56111],105:[0,.69444,0,0,.25556],106:[.19444,.69444,0,0,.28611],107:[0,.69444,0,0,.53056],108:[0,.69444,0,0,.25556],109:[0,.45833,0,0,.86667],110:[0,.45833,0,0,.56111],111:[0,.45833,0,0,.55],112:[.19444,.45833,0,0,.56111],113:[.19444,.45833,0,0,.56111],114:[0,.45833,.01528,0,.37222],115:[0,.45833,0,0,.42167],116:[0,.58929,0,0,.40417],117:[0,.45833,0,0,.56111],118:[0,.45833,.01528,0,.5],119:[0,.45833,.01528,0,.74445],120:[0,.45833,0,0,.5],121:[.19444,.45833,.01528,0,.5],122:[0,.45833,0,0,.47639],126:[.35,.34444,0,0,.55],168:[0,.69444,0,0,.55],176:[0,.69444,0,0,.73334],180:[0,.69444,0,0,.55],184:[.17014,0,0,0,.48889],305:[0,.45833,0,0,.25556],567:[.19444,.45833,0,0,.28611],710:[0,.69444,0,0,.55],711:[0,.63542,0,0,.55],713:[0,.63778,0,0,.55],728:[0,.69444,0,0,.55],729:[0,.69444,0,0,.30556],730:[0,.69444,0,0,.73334],732:[0,.69444,0,0,.55],733:[0,.69444,0,0,.55],915:[0,.69444,0,0,.58056],916:[0,.69444,0,0,.91667],920:[0,.69444,0,0,.85556],923:[0,.69444,0,0,.67223],926:[0,.69444,0,0,.73334],928:[0,.69444,0,0,.79445],931:[0,.69444,0,0,.79445],933:[0,.69444,0,0,.85556],934:[0,.69444,0,0,.79445],936:[0,.69444,0,0,.85556],937:[0,.69444,0,0,.79445],8211:[0,.45833,.03056,0,.55],8212:[0,.45833,.03056,0,1.10001],8216:[0,.69444,0,0,.30556],8217:[0,.69444,0,0,.30556],8220:[0,.69444,0,0,.55834],8221:[0,.69444,0,0,.55834]},"SansSerif-Italic":{33:[0,.69444,.05733,0,.31945],34:[0,.69444,.00316,0,.5],35:[.19444,.69444,.05087,0,.83334],36:[.05556,.75,.11156,0,.5],37:[.05556,.75,.03126,0,.83334],38:[0,.69444,.03058,0,.75834],39:[0,.69444,.07816,0,.27778],40:[.25,.75,.13164,0,.38889],41:[.25,.75,.02536,0,.38889],42:[0,.75,.11775,0,.5],43:[.08333,.58333,.02536,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,.01946,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,.13164,0,.5],48:[0,.65556,.11156,0,.5],49:[0,.65556,.11156,0,.5],50:[0,.65556,.11156,0,.5],51:[0,.65556,.11156,0,.5],52:[0,.65556,.11156,0,.5],53:[0,.65556,.11156,0,.5],54:[0,.65556,.11156,0,.5],55:[0,.65556,.11156,0,.5],56:[0,.65556,.11156,0,.5],57:[0,.65556,.11156,0,.5],58:[0,.44444,.02502,0,.27778],59:[.125,.44444,.02502,0,.27778],61:[-.13,.37,.05087,0,.77778],63:[0,.69444,.11809,0,.47222],64:[0,.69444,.07555,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,.08293,0,.66667],67:[0,.69444,.11983,0,.63889],68:[0,.69444,.07555,0,.72223],69:[0,.69444,.11983,0,.59722],70:[0,.69444,.13372,0,.56945],71:[0,.69444,.11983,0,.66667],72:[0,.69444,.08094,0,.70834],73:[0,.69444,.13372,0,.27778],74:[0,.69444,.08094,0,.47222],75:[0,.69444,.11983,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,.08094,0,.875],78:[0,.69444,.08094,0,.70834],79:[0,.69444,.07555,0,.73611],80:[0,.69444,.08293,0,.63889],81:[.125,.69444,.07555,0,.73611],82:[0,.69444,.08293,0,.64584],83:[0,.69444,.09205,0,.55556],84:[0,.69444,.13372,0,.68056],85:[0,.69444,.08094,0,.6875],86:[0,.69444,.1615,0,.66667],87:[0,.69444,.1615,0,.94445],88:[0,.69444,.13372,0,.66667],89:[0,.69444,.17261,0,.66667],90:[0,.69444,.11983,0,.61111],91:[.25,.75,.15942,0,.28889],93:[.25,.75,.08719,0,.28889],94:[0,.69444,.0799,0,.5],95:[.35,.09444,.08616,0,.5],97:[0,.44444,.00981,0,.48056],98:[0,.69444,.03057,0,.51667],99:[0,.44444,.08336,0,.44445],100:[0,.69444,.09483,0,.51667],101:[0,.44444,.06778,0,.44445],102:[0,.69444,.21705,0,.30556],103:[.19444,.44444,.10836,0,.5],104:[0,.69444,.01778,0,.51667],105:[0,.67937,.09718,0,.23889],106:[.19444,.67937,.09162,0,.26667],107:[0,.69444,.08336,0,.48889],108:[0,.69444,.09483,0,.23889],109:[0,.44444,.01778,0,.79445],110:[0,.44444,.01778,0,.51667],111:[0,.44444,.06613,0,.5],112:[.19444,.44444,.0389,0,.51667],113:[.19444,.44444,.04169,0,.51667],114:[0,.44444,.10836,0,.34167],115:[0,.44444,.0778,0,.38333],116:[0,.57143,.07225,0,.36111],117:[0,.44444,.04169,0,.51667],118:[0,.44444,.10836,0,.46111],119:[0,.44444,.10836,0,.68334],120:[0,.44444,.09169,0,.46111],121:[.19444,.44444,.10836,0,.46111],122:[0,.44444,.08752,0,.43472],126:[.35,.32659,.08826,0,.5],168:[0,.67937,.06385,0,.5],176:[0,.69444,0,0,.73752],184:[.17014,0,0,0,.44445],305:[0,.44444,.04169,0,.23889],567:[.19444,.44444,.04169,0,.26667],710:[0,.69444,.0799,0,.5],711:[0,.63194,.08432,0,.5],713:[0,.60889,.08776,0,.5],714:[0,.69444,.09205,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,.09483,0,.5],729:[0,.67937,.07774,0,.27778],730:[0,.69444,0,0,.73752],732:[0,.67659,.08826,0,.5],733:[0,.69444,.09205,0,.5],915:[0,.69444,.13372,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,.07555,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,.12816,0,.66667],928:[0,.69444,.08094,0,.70834],931:[0,.69444,.11983,0,.72222],933:[0,.69444,.09031,0,.77778],934:[0,.69444,.04603,0,.72222],936:[0,.69444,.09031,0,.77778],937:[0,.69444,.08293,0,.72222],8211:[0,.44444,.08616,0,.5],8212:[0,.44444,.08616,0,1],8216:[0,.69444,.07816,0,.27778],8217:[0,.69444,.07816,0,.27778],8220:[0,.69444,.14205,0,.5],8221:[0,.69444,.00316,0,.5]},"SansSerif-Regular":{33:[0,.69444,0,0,.31945],34:[0,.69444,0,0,.5],35:[.19444,.69444,0,0,.83334],36:[.05556,.75,0,0,.5],37:[.05556,.75,0,0,.83334],38:[0,.69444,0,0,.75834],39:[0,.69444,0,0,.27778],40:[.25,.75,0,0,.38889],41:[.25,.75,0,0,.38889],42:[0,.75,0,0,.5],43:[.08333,.58333,0,0,.77778],44:[.125,.08333,0,0,.27778],45:[0,.44444,0,0,.33333],46:[0,.08333,0,0,.27778],47:[.25,.75,0,0,.5],48:[0,.65556,0,0,.5],49:[0,.65556,0,0,.5],50:[0,.65556,0,0,.5],51:[0,.65556,0,0,.5],52:[0,.65556,0,0,.5],53:[0,.65556,0,0,.5],54:[0,.65556,0,0,.5],55:[0,.65556,0,0,.5],56:[0,.65556,0,0,.5],57:[0,.65556,0,0,.5],58:[0,.44444,0,0,.27778],59:[.125,.44444,0,0,.27778],61:[-.13,.37,0,0,.77778],63:[0,.69444,0,0,.47222],64:[0,.69444,0,0,.66667],65:[0,.69444,0,0,.66667],66:[0,.69444,0,0,.66667],67:[0,.69444,0,0,.63889],68:[0,.69444,0,0,.72223],69:[0,.69444,0,0,.59722],70:[0,.69444,0,0,.56945],71:[0,.69444,0,0,.66667],72:[0,.69444,0,0,.70834],73:[0,.69444,0,0,.27778],74:[0,.69444,0,0,.47222],75:[0,.69444,0,0,.69445],76:[0,.69444,0,0,.54167],77:[0,.69444,0,0,.875],78:[0,.69444,0,0,.70834],79:[0,.69444,0,0,.73611],80:[0,.69444,0,0,.63889],81:[.125,.69444,0,0,.73611],82:[0,.69444,0,0,.64584],83:[0,.69444,0,0,.55556],84:[0,.69444,0,0,.68056],85:[0,.69444,0,0,.6875],86:[0,.69444,.01389,0,.66667],87:[0,.69444,.01389,0,.94445],88:[0,.69444,0,0,.66667],89:[0,.69444,.025,0,.66667],90:[0,.69444,0,0,.61111],91:[.25,.75,0,0,.28889],93:[.25,.75,0,0,.28889],94:[0,.69444,0,0,.5],95:[.35,.09444,.02778,0,.5],97:[0,.44444,0,0,.48056],98:[0,.69444,0,0,.51667],99:[0,.44444,0,0,.44445],100:[0,.69444,0,0,.51667],101:[0,.44444,0,0,.44445],102:[0,.69444,.06944,0,.30556],103:[.19444,.44444,.01389,0,.5],104:[0,.69444,0,0,.51667],105:[0,.67937,0,0,.23889],106:[.19444,.67937,0,0,.26667],107:[0,.69444,0,0,.48889],108:[0,.69444,0,0,.23889],109:[0,.44444,0,0,.79445],110:[0,.44444,0,0,.51667],111:[0,.44444,0,0,.5],112:[.19444,.44444,0,0,.51667],113:[.19444,.44444,0,0,.51667],114:[0,.44444,.01389,0,.34167],115:[0,.44444,0,0,.38333],116:[0,.57143,0,0,.36111],117:[0,.44444,0,0,.51667],118:[0,.44444,.01389,0,.46111],119:[0,.44444,.01389,0,.68334],120:[0,.44444,0,0,.46111],121:[.19444,.44444,.01389,0,.46111],122:[0,.44444,0,0,.43472],126:[.35,.32659,0,0,.5],168:[0,.67937,0,0,.5],176:[0,.69444,0,0,.66667],184:[.17014,0,0,0,.44445],305:[0,.44444,0,0,.23889],567:[.19444,.44444,0,0,.26667],710:[0,.69444,0,0,.5],711:[0,.63194,0,0,.5],713:[0,.60889,0,0,.5],714:[0,.69444,0,0,.5],715:[0,.69444,0,0,.5],728:[0,.69444,0,0,.5],729:[0,.67937,0,0,.27778],730:[0,.69444,0,0,.66667],732:[0,.67659,0,0,.5],733:[0,.69444,0,0,.5],915:[0,.69444,0,0,.54167],916:[0,.69444,0,0,.83334],920:[0,.69444,0,0,.77778],923:[0,.69444,0,0,.61111],926:[0,.69444,0,0,.66667],928:[0,.69444,0,0,.70834],931:[0,.69444,0,0,.72222],933:[0,.69444,0,0,.77778],934:[0,.69444,0,0,.72222],936:[0,.69444,0,0,.77778],937:[0,.69444,0,0,.72222],8211:[0,.44444,.02778,0,.5],8212:[0,.44444,.02778,0,1],8216:[0,.69444,0,0,.27778],8217:[0,.69444,0,0,.27778],8220:[0,.69444,0,0,.5],8221:[0,.69444,0,0,.5]},"Script-Regular":{65:[0,.7,.22925,0,.80253],66:[0,.7,.04087,0,.90757],67:[0,.7,.1689,0,.66619],68:[0,.7,.09371,0,.77443],69:[0,.7,.18583,0,.56162],70:[0,.7,.13634,0,.89544],71:[0,.7,.17322,0,.60961],72:[0,.7,.29694,0,.96919],73:[0,.7,.19189,0,.80907],74:[.27778,.7,.19189,0,1.05159],75:[0,.7,.31259,0,.91364],76:[0,.7,.19189,0,.87373],77:[0,.7,.15981,0,1.08031],78:[0,.7,.3525,0,.9015],79:[0,.7,.08078,0,.73787],80:[0,.7,.08078,0,1.01262],81:[0,.7,.03305,0,.88282],82:[0,.7,.06259,0,.85],83:[0,.7,.19189,0,.86767],84:[0,.7,.29087,0,.74697],85:[0,.7,.25815,0,.79996],86:[0,.7,.27523,0,.62204],87:[0,.7,.27523,0,.80532],88:[0,.7,.26006,0,.94445],89:[0,.7,.2939,0,.70961],90:[0,.7,.24037,0,.8212]},"Size1-Regular":{40:[.35001,.85,0,0,.45834],41:[.35001,.85,0,0,.45834],47:[.35001,.85,0,0,.57778],91:[.35001,.85,0,0,.41667],92:[.35001,.85,0,0,.57778],93:[.35001,.85,0,0,.41667],123:[.35001,.85,0,0,.58334],125:[.35001,.85,0,0,.58334],710:[0,.72222,0,0,.55556],732:[0,.72222,0,0,.55556],770:[0,.72222,0,0,.55556],771:[0,.72222,0,0,.55556],8214:[-99e-5,.601,0,0,.77778],8593:[1e-5,.6,0,0,.66667],8595:[1e-5,.6,0,0,.66667],8657:[1e-5,.6,0,0,.77778],8659:[1e-5,.6,0,0,.77778],8719:[.25001,.75,0,0,.94445],8720:[.25001,.75,0,0,.94445],8721:[.25001,.75,0,0,1.05556],8730:[.35001,.85,0,0,1],8739:[-.00599,.606,0,0,.33333],8741:[-.00599,.606,0,0,.55556],8747:[.30612,.805,.19445,0,.47222],8748:[.306,.805,.19445,0,.47222],8749:[.306,.805,.19445,0,.47222],8750:[.30612,.805,.19445,0,.47222],8896:[.25001,.75,0,0,.83334],8897:[.25001,.75,0,0,.83334],8898:[.25001,.75,0,0,.83334],8899:[.25001,.75,0,0,.83334],8968:[.35001,.85,0,0,.47222],8969:[.35001,.85,0,0,.47222],8970:[.35001,.85,0,0,.47222],8971:[.35001,.85,0,0,.47222],9168:[-99e-5,.601,0,0,.66667],10216:[.35001,.85,0,0,.47222],10217:[.35001,.85,0,0,.47222],10752:[.25001,.75,0,0,1.11111],10753:[.25001,.75,0,0,1.11111],10754:[.25001,.75,0,0,1.11111],10756:[.25001,.75,0,0,.83334],10758:[.25001,.75,0,0,.83334]},"Size2-Regular":{40:[.65002,1.15,0,0,.59722],41:[.65002,1.15,0,0,.59722],47:[.65002,1.15,0,0,.81111],91:[.65002,1.15,0,0,.47222],92:[.65002,1.15,0,0,.81111],93:[.65002,1.15,0,0,.47222],123:[.65002,1.15,0,0,.66667],125:[.65002,1.15,0,0,.66667],710:[0,.75,0,0,1],732:[0,.75,0,0,1],770:[0,.75,0,0,1],771:[0,.75,0,0,1],8719:[.55001,1.05,0,0,1.27778],8720:[.55001,1.05,0,0,1.27778],8721:[.55001,1.05,0,0,1.44445],8730:[.65002,1.15,0,0,1],8747:[.86225,1.36,.44445,0,.55556],8748:[.862,1.36,.44445,0,.55556],8749:[.862,1.36,.44445,0,.55556],8750:[.86225,1.36,.44445,0,.55556],8896:[.55001,1.05,0,0,1.11111],8897:[.55001,1.05,0,0,1.11111],8898:[.55001,1.05,0,0,1.11111],8899:[.55001,1.05,0,0,1.11111],8968:[.65002,1.15,0,0,.52778],8969:[.65002,1.15,0,0,.52778],8970:[.65002,1.15,0,0,.52778],8971:[.65002,1.15,0,0,.52778],10216:[.65002,1.15,0,0,.61111],10217:[.65002,1.15,0,0,.61111],10752:[.55001,1.05,0,0,1.51112],10753:[.55001,1.05,0,0,1.51112],10754:[.55001,1.05,0,0,1.51112],10756:[.55001,1.05,0,0,1.11111],10758:[.55001,1.05,0,0,1.11111]},"Size3-Regular":{40:[.95003,1.45,0,0,.73611],41:[.95003,1.45,0,0,.73611],47:[.95003,1.45,0,0,1.04445],91:[.95003,1.45,0,0,.52778],92:[.95003,1.45,0,0,1.04445],93:[.95003,1.45,0,0,.52778],123:[.95003,1.45,0,0,.75],125:[.95003,1.45,0,0,.75],710:[0,.75,0,0,1.44445],732:[0,.75,0,0,1.44445],770:[0,.75,0,0,1.44445],771:[0,.75,0,0,1.44445],8730:[.95003,1.45,0,0,1],8968:[.95003,1.45,0,0,.58334],8969:[.95003,1.45,0,0,.58334],8970:[.95003,1.45,0,0,.58334],8971:[.95003,1.45,0,0,.58334],10216:[.95003,1.45,0,0,.75],10217:[.95003,1.45,0,0,.75]},"Size4-Regular":{40:[1.25003,1.75,0,0,.79167],41:[1.25003,1.75,0,0,.79167],47:[1.25003,1.75,0,0,1.27778],91:[1.25003,1.75,0,0,.58334],92:[1.25003,1.75,0,0,1.27778],93:[1.25003,1.75,0,0,.58334],123:[1.25003,1.75,0,0,.80556],125:[1.25003,1.75,0,0,.80556],710:[0,.825,0,0,1.8889],732:[0,.825,0,0,1.8889],770:[0,.825,0,0,1.8889],771:[0,.825,0,0,1.8889],8730:[1.25003,1.75,0,0,1],8968:[1.25003,1.75,0,0,.63889],8969:[1.25003,1.75,0,0,.63889],8970:[1.25003,1.75,0,0,.63889],8971:[1.25003,1.75,0,0,.63889],9115:[.64502,1.155,0,0,.875],9116:[1e-5,.6,0,0,.875],9117:[.64502,1.155,0,0,.875],9118:[.64502,1.155,0,0,.875],9119:[1e-5,.6,0,0,.875],9120:[.64502,1.155,0,0,.875],9121:[.64502,1.155,0,0,.66667],9122:[-99e-5,.601,0,0,.66667],9123:[.64502,1.155,0,0,.66667],9124:[.64502,1.155,0,0,.66667],9125:[-99e-5,.601,0,0,.66667],9126:[.64502,1.155,0,0,.66667],9127:[1e-5,.9,0,0,.88889],9128:[.65002,1.15,0,0,.88889],9129:[.90001,0,0,0,.88889],9130:[0,.3,0,0,.88889],9131:[1e-5,.9,0,0,.88889],9132:[.65002,1.15,0,0,.88889],9133:[.90001,0,0,0,.88889],9143:[.88502,.915,0,0,1.05556],10216:[1.25003,1.75,0,0,.80556],10217:[1.25003,1.75,0,0,.80556],57344:[-.00499,.605,0,0,1.05556],57345:[-.00499,.605,0,0,1.05556],57680:[0,.12,0,0,.45],57681:[0,.12,0,0,.45],57682:[0,.12,0,0,.45],57683:[0,.12,0,0,.45]},"Typewriter-Regular":{32:[0,0,0,0,.525],33:[0,.61111,0,0,.525],34:[0,.61111,0,0,.525],35:[0,.61111,0,0,.525],36:[.08333,.69444,0,0,.525],37:[.08333,.69444,0,0,.525],38:[0,.61111,0,0,.525],39:[0,.61111,0,0,.525],40:[.08333,.69444,0,0,.525],41:[.08333,.69444,0,0,.525],42:[0,.52083,0,0,.525],43:[-.08056,.53055,0,0,.525],44:[.13889,.125,0,0,.525],45:[-.08056,.53055,0,0,.525],46:[0,.125,0,0,.525],47:[.08333,.69444,0,0,.525],48:[0,.61111,0,0,.525],49:[0,.61111,0,0,.525],50:[0,.61111,0,0,.525],51:[0,.61111,0,0,.525],52:[0,.61111,0,0,.525],53:[0,.61111,0,0,.525],54:[0,.61111,0,0,.525],55:[0,.61111,0,0,.525],56:[0,.61111,0,0,.525],57:[0,.61111,0,0,.525],58:[0,.43056,0,0,.525],59:[.13889,.43056,0,0,.525],60:[-.05556,.55556,0,0,.525],61:[-.19549,.41562,0,0,.525],62:[-.05556,.55556,0,0,.525],63:[0,.61111,0,0,.525],64:[0,.61111,0,0,.525],65:[0,.61111,0,0,.525],66:[0,.61111,0,0,.525],67:[0,.61111,0,0,.525],68:[0,.61111,0,0,.525],69:[0,.61111,0,0,.525],70:[0,.61111,0,0,.525],71:[0,.61111,0,0,.525],72:[0,.61111,0,0,.525],73:[0,.61111,0,0,.525],74:[0,.61111,0,0,.525],75:[0,.61111,0,0,.525],76:[0,.61111,0,0,.525],77:[0,.61111,0,0,.525],78:[0,.61111,0,0,.525],79:[0,.61111,0,0,.525],80:[0,.61111,0,0,.525],81:[.13889,.61111,0,0,.525],82:[0,.61111,0,0,.525],83:[0,.61111,0,0,.525],84:[0,.61111,0,0,.525],85:[0,.61111,0,0,.525],86:[0,.61111,0,0,.525],87:[0,.61111,0,0,.525],88:[0,.61111,0,0,.525],89:[0,.61111,0,0,.525],90:[0,.61111,0,0,.525],91:[.08333,.69444,0,0,.525],92:[.08333,.69444,0,0,.525],93:[.08333,.69444,0,0,.525],94:[0,.61111,0,0,.525],95:[.09514,0,0,0,.525],96:[0,.61111,0,0,.525],97:[0,.43056,0,0,.525],98:[0,.61111,0,0,.525],99:[0,.43056,0,0,.525],100:[0,.61111,0,0,.525],101:[0,.43056,0,0,.525],102:[0,.61111,0,0,.525],103:[.22222,.43056,0,0,.525],104:[0,.61111,0,0,.525],105:[0,.61111,0,0,.525],106:[.22222,.61111,0,0,.525],107:[0,.61111,0,0,.525],108:[0,.61111,0,0,.525],109:[0,.43056,0,0,.525],110:[0,.43056,0,0,.525],111:[0,.43056,0,0,.525],112:[.22222,.43056,0,0,.525],113:[.22222,.43056,0,0,.525],114:[0,.43056,0,0,.525],115:[0,.43056,0,0,.525],116:[0,.55358,0,0,.525],117:[0,.43056,0,0,.525],118:[0,.43056,0,0,.525],119:[0,.43056,0,0,.525],120:[0,.43056,0,0,.525],121:[.22222,.43056,0,0,.525],122:[0,.43056,0,0,.525],123:[.08333,.69444,0,0,.525],124:[.08333,.69444,0,0,.525],125:[.08333,.69444,0,0,.525],126:[0,.61111,0,0,.525],127:[0,.61111,0,0,.525],160:[0,0,0,0,.525],176:[0,.61111,0,0,.525],184:[.19445,0,0,0,.525],305:[0,.43056,0,0,.525],567:[.22222,.43056,0,0,.525],711:[0,.56597,0,0,.525],713:[0,.56555,0,0,.525],714:[0,.61111,0,0,.525],715:[0,.61111,0,0,.525],728:[0,.61111,0,0,.525],730:[0,.61111,0,0,.525],770:[0,.61111,0,0,.525],771:[0,.61111,0,0,.525],776:[0,.61111,0,0,.525],915:[0,.61111,0,0,.525],916:[0,.61111,0,0,.525],920:[0,.61111,0,0,.525],923:[0,.61111,0,0,.525],926:[0,.61111,0,0,.525],928:[0,.61111,0,0,.525],931:[0,.61111,0,0,.525],933:[0,.61111,0,0,.525],934:[0,.61111,0,0,.525],936:[0,.61111,0,0,.525],937:[0,.61111,0,0,.525],8216:[0,.61111,0,0,.525],8217:[0,.61111,0,0,.525],8242:[0,.61111,0,0,.525],9251:[.11111,.21944,0,0,.525]}},V={slant:[.25,.25,.25],space:[0,0,0],stretch:[0,0,0],shrink:[0,0,0],xHeight:[.431,.431,.431],quad:[1,1.171,1.472],extraSpace:[0,0,0],num1:[.677,.732,.925],num2:[.394,.384,.387],num3:[.444,.471,.504],denom1:[.686,.752,1.025],denom2:[.345,.344,.532],sup1:[.413,.503,.504],sup2:[.363,.431,.404],sup3:[.289,.286,.294],sub1:[.15,.143,.2],sub2:[.247,.286,.4],supDrop:[.386,.353,.494],subDrop:[.05,.071,.1],delim1:[2.39,1.7,1.98],delim2:[1.01,1.157,1.42],axisHeight:[.25,.25,.25],defaultRuleThickness:[.04,.049,.049],bigOpSpacing1:[.111,.111,.111],bigOpSpacing2:[.166,.166,.166],bigOpSpacing3:[.2,.2,.2],bigOpSpacing4:[.6,.611,.611],bigOpSpacing5:[.1,.143,.143],sqrtRuleThickness:[.04,.04,.04],ptPerEm:[10,10,10],doubleRuleSep:[.2,.2,.2],arrayRuleWidth:[.04,.04,.04],fboxsep:[.3,.3,.3],fboxrule:[.04,.04,.04]},U={"\xc5":"A","\xc7":"C","\xd0":"D","\xde":"o","\xe5":"a","\xe7":"c","\xf0":"d","\xfe":"o","\u0410":"A","\u0411":"B","\u0412":"B","\u0413":"F","\u0414":"A","\u0415":"E","\u0416":"K","\u0417":"3","\u0418":"N","\u0419":"N","\u041a":"K","\u041b":"N","\u041c":"M","\u041d":"H","\u041e":"O","\u041f":"N","\u0420":"P","\u0421":"C","\u0422":"T","\u0423":"y","\u0424":"O","\u0425":"X","\u0426":"U","\u0427":"h","\u0428":"W","\u0429":"W","\u042a":"B","\u042b":"X","\u042c":"B","\u042d":"3","\u042e":"X","\u042f":"R","\u0430":"a","\u0431":"b","\u0432":"a","\u0433":"r","\u0434":"y","\u0435":"e","\u0436":"m","\u0437":"e","\u0438":"n","\u0439":"n","\u043a":"n","\u043b":"n","\u043c":"m","\u043d":"n","\u043e":"o","\u043f":"n","\u0440":"p","\u0441":"c","\u0442":"o","\u0443":"y","\u0444":"b","\u0445":"x","\u0446":"n","\u0447":"n","\u0448":"w","\u0449":"w","\u044a":"a","\u044b":"m","\u044c":"a","\u044d":"e","\u044e":"m","\u044f":"r"};function G(t,e,r){if(!F[e])throw new Error("Font metrics not found for font: "+e+".");var a=t.charCodeAt(0),n=F[e][a];if(!n&&t[0]in U&&(a=U[t[0]].charCodeAt(0),n=F[e][a]),n||"text"!==r||M(a)&&(n=F[e][77]),n)return{depth:n[0],height:n[1],italic:n[2],skew:n[3],width:n[4]}}var Y={};var W={bin:1,close:1,inner:1,open:1,punct:1,rel:1},X={"accent-token":1,mathord:1,"op-token":1,spacing:1,textord:1},_={math:{},text:{}},j=_;function $(t,e,r,a,n,i){_[t][n]={font:e,group:r,replace:a},i&&a&&(_[t][a]=_[t][n])}var Z="main",K="ams",J="bin",Q="mathord",tt="op-token",et="rel";$("math",Z,et,"\u2261","\\equiv",!0),$("math",Z,et,"\u227a","\\prec",!0),$("math",Z,et,"\u227b","\\succ",!0),$("math",Z,et,"\u223c","\\sim",!0),$("math",Z,et,"\u22a5","\\perp"),$("math",Z,et,"\u2aaf","\\preceq",!0),$("math",Z,et,"\u2ab0","\\succeq",!0),$("math",Z,et,"\u2243","\\simeq",!0),$("math",Z,et,"\u2223","\\mid",!0),$("math",Z,et,"\u226a","\\ll",!0),$("math",Z,et,"\u226b","\\gg",!0),$("math",Z,et,"\u224d","\\asymp",!0),$("math",Z,et,"\u2225","\\parallel"),$("math",Z,et,"\u22c8","\\bowtie",!0),$("math",Z,et,"\u2323","\\smile",!0),$("math",Z,et,"\u2291","\\sqsubseteq",!0),$("math",Z,et,"\u2292","\\sqsupseteq",!0),$("math",Z,et,"\u2250","\\doteq",!0),$("math",Z,et,"\u2322","\\frown",!0),$("math",Z,et,"\u220b","\\ni",!0),$("math",Z,et,"\u221d","\\propto",!0),$("math",Z,et,"\u22a2","\\vdash",!0),$("math",Z,et,"\u22a3","\\dashv",!0),$("math",Z,et,"\u220b","\\owns"),$("math",Z,"punct",".","\\ldotp"),$("math",Z,"punct","\u22c5","\\cdotp"),$("math",Z,"textord","#","\\#"),$("text",Z,"textord","#","\\#"),$("math",Z,"textord","&","\\&"),$("text",Z,"textord","&","\\&"),$("math",Z,"textord","\u2135","\\aleph",!0),$("math",Z,"textord","\u2200","\\forall",!0),$("math",Z,"textord","\u210f","\\hbar",!0),$("math",Z,"textord","\u2203","\\exists",!0),$("math",Z,"textord","\u2207","\\nabla",!0),$("math",Z,"textord","\u266d","\\flat",!0),$("math",Z,"textord","\u2113","\\ell",!0),$("math",Z,"textord","\u266e","\\natural",!0),$("math",Z,"textord","\u2663","\\clubsuit",!0),$("math",Z,"textord","\u2118","\\wp",!0),$("math",Z,"textord","\u266f","\\sharp",!0),$("math",Z,"textord","\u2662","\\diamondsuit",!0),$("math",Z,"textord","\u211c","\\Re",!0),$("math",Z,"textord","\u2661","\\heartsuit",!0),$("math",Z,"textord","\u2111","\\Im",!0),$("math",Z,"textord","\u2660","\\spadesuit",!0),$("text",Z,"textord","\xa7","\\S",!0),$("text",Z,"textord","\xb6","\\P",!0),$("math",Z,"textord","\u2020","\\dag"),$("text",Z,"textord","\u2020","\\dag"),$("text",Z,"textord","\u2020","\\textdagger"),$("math",Z,"textord","\u2021","\\ddag"),$("text",Z,"textord","\u2021","\\ddag"),$("text",Z,"textord","\u2021","\\textdaggerdbl"),$("math",Z,"close","\u23b1","\\rmoustache",!0),$("math",Z,"open","\u23b0","\\lmoustache",!0),$("math",Z,"close","\u27ef","\\rgroup",!0),$("math",Z,"open","\u27ee","\\lgroup",!0),$("math",Z,J,"\u2213","\\mp",!0),$("math",Z,J,"\u2296","\\ominus",!0),$("math",Z,J,"\u228e","\\uplus",!0),$("math",Z,J,"\u2293","\\sqcap",!0),$("math",Z,J,"\u2217","\\ast"),$("math",Z,J,"\u2294","\\sqcup",!0),$("math",Z,J,"\u25ef","\\bigcirc"),$("math",Z,J,"\u2219","\\bullet"),$("math",Z,J,"\u2021","\\ddagger"),$("math",Z,J,"\u2240","\\wr",!0),$("math",Z,J,"\u2a3f","\\amalg"),$("math",Z,J,"&","\\And"),$("math",Z,et,"\u27f5","\\longleftarrow",!0),$("math",Z,et,"\u21d0","\\Leftarrow",!0),$("math",Z,et,"\u27f8","\\Longleftarrow",!0),$("math",Z,et,"\u27f6","\\longrightarrow",!0),$("math",Z,et,"\u21d2","\\Rightarrow",!0),$("math",Z,et,"\u27f9","\\Longrightarrow",!0),$("math",Z,et,"\u2194","\\leftrightarrow",!0),$("math",Z,et,"\u27f7","\\longleftrightarrow",!0),$("math",Z,et,"\u21d4","\\Leftrightarrow",!0),$("math",Z,et,"\u27fa","\\Longleftrightarrow",!0),$("math",Z,et,"\u21a6","\\mapsto",!0),$("math",Z,et,"\u27fc","\\longmapsto",!0),$("math",Z,et,"\u2197","\\nearrow",!0),$("math",Z,et,"\u21a9","\\hookleftarrow",!0),$("math",Z,et,"\u21aa","\\hookrightarrow",!0),$("math",Z,et,"\u2198","\\searrow",!0),$("math",Z,et,"\u21bc","\\leftharpoonup",!0),$("math",Z,et,"\u21c0","\\rightharpoonup",!0),$("math",Z,et,"\u2199","\\swarrow",!0),$("math",Z,et,"\u21bd","\\leftharpoondown",!0),$("math",Z,et,"\u21c1","\\rightharpoondown",!0),$("math",Z,et,"\u2196","\\nwarrow",!0),$("math",Z,et,"\u21cc","\\rightleftharpoons",!0),$("math",K,et,"\u226e","\\nless",!0),$("math",K,et,"\ue010","\\@nleqslant"),$("math",K,et,"\ue011","\\@nleqq"),$("math",K,et,"\u2a87","\\lneq",!0),$("math",K,et,"\u2268","\\lneqq",!0),$("math",K,et,"\ue00c","\\@lvertneqq"),$("math",K,et,"\u22e6","\\lnsim",!0),$("math",K,et,"\u2a89","\\lnapprox",!0),$("math",K,et,"\u2280","\\nprec",!0),$("math",K,et,"\u22e0","\\npreceq",!0),$("math",K,et,"\u22e8","\\precnsim",!0),$("math",K,et,"\u2ab9","\\precnapprox",!0),$("math",K,et,"\u2241","\\nsim",!0),$("math",K,et,"\ue006","\\@nshortmid"),$("math",K,et,"\u2224","\\nmid",!0),$("math",K,et,"\u22ac","\\nvdash",!0),$("math",K,et,"\u22ad","\\nvDash",!0),$("math",K,et,"\u22ea","\\ntriangleleft"),$("math",K,et,"\u22ec","\\ntrianglelefteq",!0),$("math",K,et,"\u228a","\\subsetneq",!0),$("math",K,et,"\ue01a","\\@varsubsetneq"),$("math",K,et,"\u2acb","\\subsetneqq",!0),$("math",K,et,"\ue017","\\@varsubsetneqq"),$("math",K,et,"\u226f","\\ngtr",!0),$("math",K,et,"\ue00f","\\@ngeqslant"),$("math",K,et,"\ue00e","\\@ngeqq"),$("math",K,et,"\u2a88","\\gneq",!0),$("math",K,et,"\u2269","\\gneqq",!0),$("math",K,et,"\ue00d","\\@gvertneqq"),$("math",K,et,"\u22e7","\\gnsim",!0),$("math",K,et,"\u2a8a","\\gnapprox",!0),$("math",K,et,"\u2281","\\nsucc",!0),$("math",K,et,"\u22e1","\\nsucceq",!0),$("math",K,et,"\u22e9","\\succnsim",!0),$("math",K,et,"\u2aba","\\succnapprox",!0),$("math",K,et,"\u2246","\\ncong",!0),$("math",K,et,"\ue007","\\@nshortparallel"),$("math",K,et,"\u2226","\\nparallel",!0),$("math",K,et,"\u22af","\\nVDash",!0),$("math",K,et,"\u22eb","\\ntriangleright"),$("math",K,et,"\u22ed","\\ntrianglerighteq",!0),$("math",K,et,"\ue018","\\@nsupseteqq"),$("math",K,et,"\u228b","\\supsetneq",!0),$("math",K,et,"\ue01b","\\@varsupsetneq"),$("math",K,et,"\u2acc","\\supsetneqq",!0),$("math",K,et,"\ue019","\\@varsupsetneqq"),$("math",K,et,"\u22ae","\\nVdash",!0),$("math",K,et,"\u2ab5","\\precneqq",!0),$("math",K,et,"\u2ab6","\\succneqq",!0),$("math",K,et,"\ue016","\\@nsubseteqq"),$("math",K,J,"\u22b4","\\unlhd"),$("math",K,J,"\u22b5","\\unrhd"),$("math",K,et,"\u219a","\\nleftarrow",!0),$("math",K,et,"\u219b","\\nrightarrow",!0),$("math",K,et,"\u21cd","\\nLeftarrow",!0),$("math",K,et,"\u21cf","\\nRightarrow",!0),$("math",K,et,"\u21ae","\\nleftrightarrow",!0),$("math",K,et,"\u21ce","\\nLeftrightarrow",!0),$("math",K,et,"\u25b3","\\vartriangle"),$("math",K,"textord","\u210f","\\hslash"),$("math",K,"textord","\u25bd","\\triangledown"),$("math",K,"textord","\u25ca","\\lozenge"),$("math",K,"textord","\u24c8","\\circledS"),$("math",K,"textord","\xae","\\circledR"),$("text",K,"textord","\xae","\\circledR"),$("math",K,"textord","\u2221","\\measuredangle",!0),$("math",K,"textord","\u2204","\\nexists"),$("math",K,"textord","\u2127","\\mho"),$("math",K,"textord","\u2132","\\Finv",!0),$("math",K,"textord","\u2141","\\Game",!0),$("math",K,"textord","\u2035","\\backprime"),$("math",K,"textord","\u25b2","\\blacktriangle"),$("math",K,"textord","\u25bc","\\blacktriangledown"),$("math",K,"textord","\u25a0","\\blacksquare"),$("math",K,"textord","\u29eb","\\blacklozenge"),$("math",K,"textord","\u2605","\\bigstar"),$("math",K,"textord","\u2222","\\sphericalangle",!0),$("math",K,"textord","\u2201","\\complement",!0),$("math",K,"textord","\xf0","\\eth",!0),$("math",K,"textord","\u2571","\\diagup"),$("math",K,"textord","\u2572","\\diagdown"),$("math",K,"textord","\u25a1","\\square"),$("math",K,"textord","\u25a1","\\Box"),$("math",K,"textord","\u25ca","\\Diamond"),$("math",K,"textord","\xa5","\\yen",!0),$("text",K,"textord","\xa5","\\yen",!0),$("math",K,"textord","\u2713","\\checkmark",!0),$("text",K,"textord","\u2713","\\checkmark"),$("math",K,"textord","\u2136","\\beth",!0),$("math",K,"textord","\u2138","\\daleth",!0),$("math",K,"textord","\u2137","\\gimel",!0),$("math",K,"textord","\u03dd","\\digamma",!0),$("math",K,"textord","\u03f0","\\varkappa"),$("math",K,"open","\u250c","\\ulcorner",!0),$("math",K,"close","\u2510","\\urcorner",!0),$("math",K,"open","\u2514","\\llcorner",!0),$("math",K,"close","\u2518","\\lrcorner",!0),$("math",K,et,"\u2266","\\leqq",!0),$("math",K,et,"\u2a7d","\\leqslant",!0),$("math",K,et,"\u2a95","\\eqslantless",!0),$("math",K,et,"\u2272","\\lesssim",!0),$("math",K,et,"\u2a85","\\lessapprox",!0),$("math",K,et,"\u224a","\\approxeq",!0),$("math",K,J,"\u22d6","\\lessdot"),$("math",K,et,"\u22d8","\\lll",!0),$("math",K,et,"\u2276","\\lessgtr",!0),$("math",K,et,"\u22da","\\lesseqgtr",!0),$("math",K,et,"\u2a8b","\\lesseqqgtr",!0),$("math",K,et,"\u2251","\\doteqdot"),$("math",K,et,"\u2253","\\risingdotseq",!0),$("math",K,et,"\u2252","\\fallingdotseq",!0),$("math",K,et,"\u223d","\\backsim",!0),$("math",K,et,"\u22cd","\\backsimeq",!0),$("math",K,et,"\u2ac5","\\subseteqq",!0),$("math",K,et,"\u22d0","\\Subset",!0),$("math",K,et,"\u228f","\\sqsubset",!0),$("math",K,et,"\u227c","\\preccurlyeq",!0),$("math",K,et,"\u22de","\\curlyeqprec",!0),$("math",K,et,"\u227e","\\precsim",!0),$("math",K,et,"\u2ab7","\\precapprox",!0),$("math",K,et,"\u22b2","\\vartriangleleft"),$("math",K,et,"\u22b4","\\trianglelefteq"),$("math",K,et,"\u22a8","\\vDash",!0),$("math",K,et,"\u22aa","\\Vvdash",!0),$("math",K,et,"\u2323","\\smallsmile"),$("math",K,et,"\u2322","\\smallfrown"),$("math",K,et,"\u224f","\\bumpeq",!0),$("math",K,et,"\u224e","\\Bumpeq",!0),$("math",K,et,"\u2267","\\geqq",!0),$("math",K,et,"\u2a7e","\\geqslant",!0),$("math",K,et,"\u2a96","\\eqslantgtr",!0),$("math",K,et,"\u2273","\\gtrsim",!0),$("math",K,et,"\u2a86","\\gtrapprox",!0),$("math",K,J,"\u22d7","\\gtrdot"),$("math",K,et,"\u22d9","\\ggg",!0),$("math",K,et,"\u2277","\\gtrless",!0),$("math",K,et,"\u22db","\\gtreqless",!0),$("math",K,et,"\u2a8c","\\gtreqqless",!0),$("math",K,et,"\u2256","\\eqcirc",!0),$("math",K,et,"\u2257","\\circeq",!0),$("math",K,et,"\u225c","\\triangleq",!0),$("math",K,et,"\u223c","\\thicksim"),$("math",K,et,"\u2248","\\thickapprox"),$("math",K,et,"\u2ac6","\\supseteqq",!0),$("math",K,et,"\u22d1","\\Supset",!0),$("math",K,et,"\u2290","\\sqsupset",!0),$("math",K,et,"\u227d","\\succcurlyeq",!0),$("math",K,et,"\u22df","\\curlyeqsucc",!0),$("math",K,et,"\u227f","\\succsim",!0),$("math",K,et,"\u2ab8","\\succapprox",!0),$("math",K,et,"\u22b3","\\vartriangleright"),$("math",K,et,"\u22b5","\\trianglerighteq"),$("math",K,et,"\u22a9","\\Vdash",!0),$("math",K,et,"\u2223","\\shortmid"),$("math",K,et,"\u2225","\\shortparallel"),$("math",K,et,"\u226c","\\between",!0),$("math",K,et,"\u22d4","\\pitchfork",!0),$("math",K,et,"\u221d","\\varpropto"),$("math",K,et,"\u25c0","\\blacktriangleleft"),$("math",K,et,"\u2234","\\therefore",!0),$("math",K,et,"\u220d","\\backepsilon"),$("math",K,et,"\u25b6","\\blacktriangleright"),$("math",K,et,"\u2235","\\because",!0),$("math",K,et,"\u22d8","\\llless"),$("math",K,et,"\u22d9","\\gggtr"),$("math",K,J,"\u22b2","\\lhd"),$("math",K,J,"\u22b3","\\rhd"),$("math",K,et,"\u2242","\\eqsim",!0),$("math",Z,et,"\u22c8","\\Join"),$("math",K,et,"\u2251","\\Doteq",!0),$("math",K,J,"\u2214","\\dotplus",!0),$("math",K,J,"\u2216","\\smallsetminus"),$("math",K,J,"\u22d2","\\Cap",!0),$("math",K,J,"\u22d3","\\Cup",!0),$("math",K,J,"\u2a5e","\\doublebarwedge",!0),$("math",K,J,"\u229f","\\boxminus",!0),$("math",K,J,"\u229e","\\boxplus",!0),$("math",K,J,"\u22c7","\\divideontimes",!0),$("math",K,J,"\u22c9","\\ltimes",!0),$("math",K,J,"\u22ca","\\rtimes",!0),$("math",K,J,"\u22cb","\\leftthreetimes",!0),$("math",K,J,"\u22cc","\\rightthreetimes",!0),$("math",K,J,"\u22cf","\\curlywedge",!0),$("math",K,J,"\u22ce","\\curlyvee",!0),$("math",K,J,"\u229d","\\circleddash",!0),$("math",K,J,"\u229b","\\circledast",!0),$("math",K,J,"\u22c5","\\centerdot"),$("math",K,J,"\u22ba","\\intercal",!0),$("math",K,J,"\u22d2","\\doublecap"),$("math",K,J,"\u22d3","\\doublecup"),$("math",K,J,"\u22a0","\\boxtimes",!0),$("math",K,et,"\u21e2","\\dashrightarrow",!0),$("math",K,et,"\u21e0","\\dashleftarrow",!0),$("math",K,et,"\u21c7","\\leftleftarrows",!0),$("math",K,et,"\u21c6","\\leftrightarrows",!0),$("math",K,et,"\u21da","\\Lleftarrow",!0),$("math",K,et,"\u219e","\\twoheadleftarrow",!0),$("math",K,et,"\u21a2","\\leftarrowtail",!0),$("math",K,et,"\u21ab","\\looparrowleft",!0),$("math",K,et,"\u21cb","\\leftrightharpoons",!0),$("math",K,et,"\u21b6","\\curvearrowleft",!0),$("math",K,et,"\u21ba","\\circlearrowleft",!0),$("math",K,et,"\u21b0","\\Lsh",!0),$("math",K,et,"\u21c8","\\upuparrows",!0),$("math",K,et,"\u21bf","\\upharpoonleft",!0),$("math",K,et,"\u21c3","\\downharpoonleft",!0),$("math",K,et,"\u22b8","\\multimap",!0),$("math",K,et,"\u21ad","\\leftrightsquigarrow",!0),$("math",K,et,"\u21c9","\\rightrightarrows",!0),$("math",K,et,"\u21c4","\\rightleftarrows",!0),$("math",K,et,"\u21a0","\\twoheadrightarrow",!0),$("math",K,et,"\u21a3","\\rightarrowtail",!0),$("math",K,et,"\u21ac","\\looparrowright",!0),$("math",K,et,"\u21b7","\\curvearrowright",!0),$("math",K,et,"\u21bb","\\circlearrowright",!0),$("math",K,et,"\u21b1","\\Rsh",!0),$("math",K,et,"\u21ca","\\downdownarrows",!0),$("math",K,et,"\u21be","\\upharpoonright",!0),$("math",K,et,"\u21c2","\\downharpoonright",!0),$("math",K,et,"\u21dd","\\rightsquigarrow",!0),$("math",K,et,"\u21dd","\\leadsto"),$("math",K,et,"\u21db","\\Rrightarrow",!0),$("math",K,et,"\u21be","\\restriction"),$("math",Z,"textord","\u2018","`"),$("math",Z,"textord","$","\\$"),$("text",Z,"textord","$","\\$"),$("text",Z,"textord","$","\\textdollar"),$("math",Z,"textord","%","\\%"),$("text",Z,"textord","%","\\%"),$("math",Z,"textord","_","\\_"),$("text",Z,"textord","_","\\_"),$("text",Z,"textord","_","\\textunderscore"),$("math",Z,"textord","\u2220","\\angle",!0),$("math",Z,"textord","\u221e","\\infty",!0),$("math",Z,"textord","\u2032","\\prime"),$("math",Z,"textord","\u25b3","\\triangle"),$("math",Z,"textord","\u0393","\\Gamma",!0),$("math",Z,"textord","\u0394","\\Delta",!0),$("math",Z,"textord","\u0398","\\Theta",!0),$("math",Z,"textord","\u039b","\\Lambda",!0),$("math",Z,"textord","\u039e","\\Xi",!0),$("math",Z,"textord","\u03a0","\\Pi",!0),$("math",Z,"textord","\u03a3","\\Sigma",!0),$("math",Z,"textord","\u03a5","\\Upsilon",!0),$("math",Z,"textord","\u03a6","\\Phi",!0),$("math",Z,"textord","\u03a8","\\Psi",!0),$("math",Z,"textord","\u03a9","\\Omega",!0),$("math",Z,"textord","A","\u0391"),$("math",Z,"textord","B","\u0392"),$("math",Z,"textord","E","\u0395"),$("math",Z,"textord","Z","\u0396"),$("math",Z,"textord","H","\u0397"),$("math",Z,"textord","I","\u0399"),$("math",Z,"textord","K","\u039a"),$("math",Z,"textord","M","\u039c"),$("math",Z,"textord","N","\u039d"),$("math",Z,"textord","O","\u039f"),$("math",Z,"textord","P","\u03a1"),$("math",Z,"textord","T","\u03a4"),$("math",Z,"textord","X","\u03a7"),$("math",Z,"textord","\xac","\\neg",!0),$("math",Z,"textord","\xac","\\lnot"),$("math",Z,"textord","\u22a4","\\top"),$("math",Z,"textord","\u22a5","\\bot"),$("math",Z,"textord","\u2205","\\emptyset"),$("math",K,"textord","\u2205","\\varnothing"),$("math",Z,Q,"\u03b1","\\alpha",!0),$("math",Z,Q,"\u03b2","\\beta",!0),$("math",Z,Q,"\u03b3","\\gamma",!0),$("math",Z,Q,"\u03b4","\\delta",!0),$("math",Z,Q,"\u03f5","\\epsilon",!0),$("math",Z,Q,"\u03b6","\\zeta",!0),$("math",Z,Q,"\u03b7","\\eta",!0),$("math",Z,Q,"\u03b8","\\theta",!0),$("math",Z,Q,"\u03b9","\\iota",!0),$("math",Z,Q,"\u03ba","\\kappa",!0),$("math",Z,Q,"\u03bb","\\lambda",!0),$("math",Z,Q,"\u03bc","\\mu",!0),$("math",Z,Q,"\u03bd","\\nu",!0),$("math",Z,Q,"\u03be","\\xi",!0),$("math",Z,Q,"\u03bf","\\omicron",!0),$("math",Z,Q,"\u03c0","\\pi",!0),$("math",Z,Q,"\u03c1","\\rho",!0),$("math",Z,Q,"\u03c3","\\sigma",!0),$("math",Z,Q,"\u03c4","\\tau",!0),$("math",Z,Q,"\u03c5","\\upsilon",!0),$("math",Z,Q,"\u03d5","\\phi",!0),$("math",Z,Q,"\u03c7","\\chi",!0),$("math",Z,Q,"\u03c8","\\psi",!0),$("math",Z,Q,"\u03c9","\\omega",!0),$("math",Z,Q,"\u03b5","\\varepsilon",!0),$("math",Z,Q,"\u03d1","\\vartheta",!0),$("math",Z,Q,"\u03d6","\\varpi",!0),$("math",Z,Q,"\u03f1","\\varrho",!0),$("math",Z,Q,"\u03c2","\\varsigma",!0),$("math",Z,Q,"\u03c6","\\varphi",!0),$("math",Z,J,"\u2217","*"),$("math",Z,J,"+","+"),$("math",Z,J,"\u2212","-"),$("math",Z,J,"\u22c5","\\cdot",!0),$("math",Z,J,"\u2218","\\circ"),$("math",Z,J,"\xf7","\\div",!0),$("math",Z,J,"\xb1","\\pm",!0),$("math",Z,J,"\xd7","\\times",!0),$("math",Z,J,"\u2229","\\cap",!0),$("math",Z,J,"\u222a","\\cup",!0),$("math",Z,J,"\u2216","\\setminus"),$("math",Z,J,"\u2227","\\land"),$("math",Z,J,"\u2228","\\lor"),$("math",Z,J,"\u2227","\\wedge",!0),$("math",Z,J,"\u2228","\\vee",!0),$("math",Z,"textord","\u221a","\\surd"),$("math",Z,"open","(","("),$("math",Z,"open","[","["),$("math",Z,"open","\u27e8","\\langle",!0),$("math",Z,"open","\u2223","\\lvert"),$("math",Z,"open","\u2225","\\lVert"),$("math",Z,"close",")",")"),$("math",Z,"close","]","]"),$("math",Z,"close","?","?"),$("math",Z,"close","!","!"),$("math",Z,"close","\u27e9","\\rangle",!0),$("math",Z,"close","\u2223","\\rvert"),$("math",Z,"close","\u2225","\\rVert"),$("math",Z,et,"=","="),$("math",Z,et,"<","<"),$("math",Z,et,">",">"),$("math",Z,et,":",":"),$("math",Z,et,"\u2248","\\approx",!0),$("math",Z,et,"\u2245","\\cong",!0),$("math",Z,et,"\u2265","\\ge"),$("math",Z,et,"\u2265","\\geq",!0),$("math",Z,et,"\u2190","\\gets"),$("math",Z,et,">","\\gt"),$("math",Z,et,"\u2208","\\in",!0),$("math",Z,et,"\ue020","\\@not"),$("math",Z,et,"\u2282","\\subset",!0),$("math",Z,et,"\u2283","\\supset",!0),$("math",Z,et,"\u2286","\\subseteq",!0),$("math",Z,et,"\u2287","\\supseteq",!0),$("math",K,et,"\u2288","\\nsubseteq",!0),$("math",K,et,"\u2289","\\nsupseteq",!0),$("math",Z,et,"\u22a8","\\models"),$("math",Z,et,"\u2190","\\leftarrow",!0),$("math",Z,et,"\u2264","\\le"),$("math",Z,et,"\u2264","\\leq",!0),$("math",Z,et,"<","\\lt"),$("math",Z,et,"\u2192","\\rightarrow",!0),$("math",Z,et,"\u2192","\\to"),$("math",K,et,"\u2271","\\ngeq",!0),$("math",K,et,"\u2270","\\nleq",!0),$("math",Z,"spacing","\xa0","\\ "),$("math",Z,"spacing","\xa0","~"),$("math",Z,"spacing","\xa0","\\space"),$("math",Z,"spacing","\xa0","\\nobreakspace"),$("text",Z,"spacing","\xa0","\\ "),$("text",Z,"spacing","\xa0","~"),$("text",Z,"spacing","\xa0","\\space"),$("text",Z,"spacing","\xa0","\\nobreakspace"),$("math",Z,"spacing",null,"\\nobreak"),$("math",Z,"spacing",null,"\\allowbreak"),$("math",Z,"punct",",",","),$("math",Z,"punct",";",";"),$("math",K,J,"\u22bc","\\barwedge",!0),$("math",K,J,"\u22bb","\\veebar",!0),$("math",Z,J,"\u2299","\\odot",!0),$("math",Z,J,"\u2295","\\oplus",!0),$("math",Z,J,"\u2297","\\otimes",!0),$("math",Z,"textord","\u2202","\\partial",!0),$("math",Z,J,"\u2298","\\oslash",!0),$("math",K,J,"\u229a","\\circledcirc",!0),$("math",K,J,"\u22a1","\\boxdot",!0),$("math",Z,J,"\u25b3","\\bigtriangleup"),$("math",Z,J,"\u25bd","\\bigtriangledown"),$("math",Z,J,"\u2020","\\dagger"),$("math",Z,J,"\u22c4","\\diamond"),$("math",Z,J,"\u22c6","\\star"),$("math",Z,J,"\u25c3","\\triangleleft"),$("math",Z,J,"\u25b9","\\triangleright"),$("math",Z,"open","{","\\{"),$("text",Z,"textord","{","\\{"),$("text",Z,"textord","{","\\textbraceleft"),$("math",Z,"close","}","\\}"),$("text",Z,"textord","}","\\}"),$("text",Z,"textord","}","\\textbraceright"),$("math",Z,"open","{","\\lbrace"),$("math",Z,"close","}","\\rbrace"),$("math",Z,"open","[","\\lbrack"),$("text",Z,"textord","[","\\lbrack"),$("math",Z,"close","]","\\rbrack"),$("text",Z,"textord","]","\\rbrack"),$("math",Z,"open","(","\\lparen"),$("math",Z,"close",")","\\rparen"),$("text",Z,"textord","<","\\textless"),$("text",Z,"textord",">","\\textgreater"),$("math",Z,"open","\u230a","\\lfloor",!0),$("math",Z,"close","\u230b","\\rfloor",!0),$("math",Z,"open","\u2308","\\lceil",!0),$("math",Z,"close","\u2309","\\rceil",!0),$("math",Z,"textord","\\","\\backslash"),$("math",Z,"textord","\u2223","|"),$("math",Z,"textord","\u2223","\\vert"),$("text",Z,"textord","|","\\textbar"),$("math",Z,"textord","\u2225","\\|"),$("math",Z,"textord","\u2225","\\Vert"),$("text",Z,"textord","\u2225","\\textbardbl"),$("text",Z,"textord","~","\\textasciitilde"),$("text",Z,"textord","\\","\\textbackslash"),$("text",Z,"textord","^","\\textasciicircum"),$("math",Z,et,"\u2191","\\uparrow",!0),$("math",Z,et,"\u21d1","\\Uparrow",!0),$("math",Z,et,"\u2193","\\downarrow",!0),$("math",Z,et,"\u21d3","\\Downarrow",!0),$("math",Z,et,"\u2195","\\updownarrow",!0),$("math",Z,et,"\u21d5","\\Updownarrow",!0),$("math",Z,tt,"\u2210","\\coprod"),$("math",Z,tt,"\u22c1","\\bigvee"),$("math",Z,tt,"\u22c0","\\bigwedge"),$("math",Z,tt,"\u2a04","\\biguplus"),$("math",Z,tt,"\u22c2","\\bigcap"),$("math",Z,tt,"\u22c3","\\bigcup"),$("math",Z,tt,"\u222b","\\int"),$("math",Z,tt,"\u222b","\\intop"),$("math",Z,tt,"\u222c","\\iint"),$("math",Z,tt,"\u222d","\\iiint"),$("math",Z,tt,"\u220f","\\prod"),$("math",Z,tt,"\u2211","\\sum"),$("math",Z,tt,"\u2a02","\\bigotimes"),$("math",Z,tt,"\u2a01","\\bigoplus"),$("math",Z,tt,"\u2a00","\\bigodot"),$("math",Z,tt,"\u222e","\\oint"),$("math",Z,tt,"\u222f","\\oiint"),$("math",Z,tt,"\u2230","\\oiiint"),$("math",Z,tt,"\u2a06","\\bigsqcup"),$("math",Z,tt,"\u222b","\\smallint"),$("text",Z,"inner","\u2026","\\textellipsis"),$("math",Z,"inner","\u2026","\\mathellipsis"),$("text",Z,"inner","\u2026","\\ldots",!0),$("math",Z,"inner","\u2026","\\ldots",!0),$("math",Z,"inner","\u22ef","\\@cdots",!0),$("math",Z,"inner","\u22f1","\\ddots",!0),$("math",Z,"textord","\u22ee","\\varvdots"),$("math",Z,"accent-token","\u02ca","\\acute"),$("math",Z,"accent-token","\u02cb","\\grave"),$("math",Z,"accent-token","\xa8","\\ddot"),$("math",Z,"accent-token","~","\\tilde"),$("math",Z,"accent-token","\u02c9","\\bar"),$("math",Z,"accent-token","\u02d8","\\breve"),$("math",Z,"accent-token","\u02c7","\\check"),$("math",Z,"accent-token","^","\\hat"),$("math",Z,"accent-token","\u20d7","\\vec"),$("math",Z,"accent-token","\u02d9","\\dot"),$("math",Z,"accent-token","\u02da","\\mathring"),$("math",Z,Q,"\u0131","\\imath",!0),$("math",Z,Q,"\u0237","\\jmath",!0),$("text",Z,"textord","\u0131","\\i",!0),$("text",Z,"textord","\u0237","\\j",!0),$("text",Z,"textord","\xdf","\\ss",!0),$("text",Z,"textord","\xe6","\\ae",!0),$("text",Z,"textord","\xe6","\\ae",!0),$("text",Z,"textord","\u0153","\\oe",!0),$("text",Z,"textord","\xf8","\\o",!0),$("text",Z,"textord","\xc6","\\AE",!0),$("text",Z,"textord","\u0152","\\OE",!0),$("text",Z,"textord","\xd8","\\O",!0),$("text",Z,"accent-token","\u02ca","\\'"),$("text",Z,"accent-token","\u02cb","\\`"),$("text",Z,"accent-token","\u02c6","\\^"),$("text",Z,"accent-token","\u02dc","\\~"),$("text",Z,"accent-token","\u02c9","\\="),$("text",Z,"accent-token","\u02d8","\\u"),$("text",Z,"accent-token","\u02d9","\\."),$("text",Z,"accent-token","\u02da","\\r"),$("text",Z,"accent-token","\u02c7","\\v"),$("text",Z,"accent-token","\xa8",'\\"'),$("text",Z,"accent-token","\u02dd","\\H"),$("text",Z,"accent-token","\u25ef","\\textcircled");var rt={"--":!0,"---":!0,"``":!0,"''":!0};$("text",Z,"textord","\u2013","--"),$("text",Z,"textord","\u2013","\\textendash"),$("text",Z,"textord","\u2014","---"),$("text",Z,"textord","\u2014","\\textemdash"),$("text",Z,"textord","\u2018","`"),$("text",Z,"textord","\u2018","\\textquoteleft"),$("text",Z,"textord","\u2019","'"),$("text",Z,"textord","\u2019","\\textquoteright"),$("text",Z,"textord","\u201c","``"),$("text",Z,"textord","\u201c","\\textquotedblleft"),$("text",Z,"textord","\u201d","''"),$("text",Z,"textord","\u201d","\\textquotedblright"),$("math",Z,"textord","\xb0","\\degree",!0),$("text",Z,"textord","\xb0","\\degree"),$("text",Z,"textord","\xb0","\\textdegree",!0),$("math",Z,Q,"\xa3","\\pounds"),$("math",Z,Q,"\xa3","\\mathsterling",!0),$("text",Z,Q,"\xa3","\\pounds"),$("text",Z,Q,"\xa3","\\textsterling",!0),$("math",K,"textord","\u2720","\\maltese"),$("text",K,"textord","\u2720","\\maltese"),$("text",Z,"spacing","\xa0","\\ "),$("text",Z,"spacing","\xa0"," "),$("text",Z,"spacing","\xa0","~");for(var at=0;at<'0123456789/@."'.length;at++){var nt='0123456789/@."'.charAt(at);$("math",Z,"textord",nt,nt)}for(var it=0;it<'0123456789!@*()-=+[]<>|";:?/.,'.length;it++){var ot='0123456789!@*()-=+[]<>|";:?/.,'.charAt(it);$("text",Z,"textord",ot,ot)}for(var st="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",ht=0;ht<st.length;ht++){var lt=st.charAt(ht);$("math",Z,Q,lt,lt),$("text",Z,"textord",lt,lt)}$("math",K,"textord","C","\u2102"),$("text",K,"textord","C","\u2102"),$("math",K,"textord","H","\u210d"),$("text",K,"textord","H","\u210d"),$("math",K,"textord","N","\u2115"),$("text",K,"textord","N","\u2115"),$("math",K,"textord","P","\u2119"),$("text",K,"textord","P","\u2119"),$("math",K,"textord","Q","\u211a"),$("text",K,"textord","Q","\u211a"),$("math",K,"textord","R","\u211d"),$("text",K,"textord","R","\u211d"),$("math",K,"textord","Z","\u2124"),$("text",K,"textord","Z","\u2124"),$("math",Z,Q,"h","\u210e"),$("text",Z,Q,"h","\u210e");for(var mt="",ct=0;ct<st.length;ct++){var ut=st.charAt(ct);$("math",Z,Q,ut,mt=String.fromCharCode(55349,56320+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56372+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56424+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56580+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56736+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56788+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56840+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56944+ct)),$("text",Z,"textord",ut,mt),ct<26&&($("math",Z,Q,ut,mt=String.fromCharCode(55349,56632+ct)),$("text",Z,"textord",ut,mt),$("math",Z,Q,ut,mt=String.fromCharCode(55349,56476+ct)),$("text",Z,"textord",ut,mt))}$("math",Z,Q,"k",mt=String.fromCharCode(55349,56668)),$("text",Z,"textord","k",mt);for(var pt=0;pt<10;pt++){var dt=pt.toString();$("math",Z,Q,dt,mt=String.fromCharCode(55349,57294+pt)),$("text",Z,"textord",dt,mt),$("math",Z,Q,dt,mt=String.fromCharCode(55349,57314+pt)),$("text",Z,"textord",dt,mt),$("math",Z,Q,dt,mt=String.fromCharCode(55349,57324+pt)),$("text",Z,"textord",dt,mt),$("math",Z,Q,dt,mt=String.fromCharCode(55349,57334+pt)),$("text",Z,"textord",dt,mt)}for(var ft=0;ft<"\xc7\xd0\xde\xe7\xfe".length;ft++){var gt="\xc7\xd0\xde\xe7\xfe".charAt(ft);$("math",Z,Q,gt,gt),$("text",Z,"textord",gt,gt)}$("text",Z,"textord","\xf0","\xf0"),$("text",Z,"textord","\u2013","\u2013"),$("text",Z,"textord","\u2014","\u2014"),$("text",Z,"textord","\u2018","\u2018"),$("text",Z,"textord","\u2019","\u2019"),$("text",Z,"textord","\u201c","\u201c"),$("text",Z,"textord","\u201d","\u201d");var xt=[["mathbf","textbf","Main-Bold"],["mathbf","textbf","Main-Bold"],["mathdefault","textit","Math-Italic"],["mathdefault","textit","Math-Italic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["boldsymbol","boldsymbol","Main-BoldItalic"],["mathscr","textscr","Script-Regular"],["","",""],["","",""],["","",""],["mathfrak","textfrak","Fraktur-Regular"],["mathfrak","textfrak","Fraktur-Regular"],["mathbb","textbb","AMS-Regular"],["mathbb","textbb","AMS-Regular"],["","",""],["","",""],["mathsf","textsf","SansSerif-Regular"],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathitsf","textitsf","SansSerif-Italic"],["mathitsf","textitsf","SansSerif-Italic"],["","",""],["","",""],["mathtt","texttt","Typewriter-Regular"],["mathtt","texttt","Typewriter-Regular"]],vt=[["mathbf","textbf","Main-Bold"],["","",""],["mathsf","textsf","SansSerif-Regular"],["mathboldsf","textboldsf","SansSerif-Bold"],["mathtt","texttt","Typewriter-Regular"]],bt=[[1,1,1],[2,1,1],[3,1,1],[4,2,1],[5,2,1],[6,3,1],[7,4,2],[8,6,3],[9,7,6],[10,8,7],[11,10,9]],yt=[.5,.6,.7,.8,.9,1,1.2,1.44,1.728,2.074,2.488],wt=function(t,e){return e.size<2?t:bt[t-1][e.size-1]},kt=function(){function t(e){this.style=void 0,this.color=void 0,this.size=void 0,this.textSize=void 0,this.phantom=void 0,this.font=void 0,this.fontFamily=void 0,this.fontWeight=void 0,this.fontShape=void 0,this.sizeMultiplier=void 0,this.maxSize=void 0,this.minRuleThickness=void 0,this._fontMetrics=void 0,this.style=e.style,this.color=e.color,this.size=e.size||t.BASESIZE,this.textSize=e.textSize||this.size,this.phantom=!!e.phantom,this.font=e.font||"",this.fontFamily=e.fontFamily||"",this.fontWeight=e.fontWeight||"",this.fontShape=e.fontShape||"",this.sizeMultiplier=yt[this.size-1],this.maxSize=e.maxSize,this.minRuleThickness=e.minRuleThickness,this._fontMetrics=void 0}var e=t.prototype;return e.extend=function(e){var r={style:this.style,size:this.size,textSize:this.textSize,color:this.color,phantom:this.phantom,font:this.font,fontFamily:this.fontFamily,fontWeight:this.fontWeight,fontShape:this.fontShape,maxSize:this.maxSize,minRuleThickness:this.minRuleThickness};for(var a in e)e.hasOwnProperty(a)&&(r[a]=e[a]);return new t(r)},e.havingStyle=function(t){return this.style===t?this:this.extend({style:t,size:wt(this.textSize,t)})},e.havingCrampedStyle=function(){return this.havingStyle(this.style.cramp())},e.havingSize=function(t){return this.size===t&&this.textSize===t?this:this.extend({style:this.style.text(),size:t,textSize:t,sizeMultiplier:yt[t-1]})},e.havingBaseStyle=function(e){e=e||this.style.text();var r=wt(t.BASESIZE,e);return this.size===r&&this.textSize===t.BASESIZE&&this.style===e?this:this.extend({style:e,size:r})},e.havingBaseSizing=function(){var t;switch(this.style.id){case 4:case 5:t=3;break;case 6:case 7:t=1;break;default:t=6}return this.extend({style:this.style.text(),size:t})},e.withColor=function(t){return this.extend({color:t})},e.withPhantom=function(){return this.extend({phantom:!0})},e.withFont=function(t){return this.extend({font:t})},e.withTextFontFamily=function(t){return this.extend({fontFamily:t,font:""})},e.withTextFontWeight=function(t){return this.extend({fontWeight:t,font:""})},e.withTextFontShape=function(t){return this.extend({fontShape:t,font:""})},e.sizingClasses=function(t){return t.size!==this.size?["sizing","reset-size"+t.size,"size"+this.size]:[]},e.baseSizingClasses=function(){return this.size!==t.BASESIZE?["sizing","reset-size"+this.size,"size"+t.BASESIZE]:[]},e.fontMetrics=function(){return this._fontMetrics||(this._fontMetrics=function(t){var e;if(!Y[e=t>=5?0:t>=3?1:2]){var r=Y[e]={cssEmPerMu:V.quad[e]/18};for(var a in V)V.hasOwnProperty(a)&&(r[a]=V[a][e])}return Y[e]}(this.size)),this._fontMetrics},e.getColor=function(){return this.phantom?"transparent":this.color},t}();kt.BASESIZE=6;var St=kt,Mt={pt:1,mm:7227/2540,cm:7227/254,in:72.27,bp:1.00375,pc:12,dd:1238/1157,cc:14856/1157,nd:685/642,nc:1370/107,sp:1/65536,px:1.00375},zt={ex:!0,em:!0,mu:!0},At=function(t){return"string"!=typeof t&&(t=t.unit),t in Mt||t in zt||"ex"===t},Tt=function(t,e){var r;if(t.unit in Mt)r=Mt[t.unit]/e.fontMetrics().ptPerEm/e.sizeMultiplier;else if("mu"===t.unit)r=e.fontMetrics().cssEmPerMu;else{var a;if(a=e.style.isTight()?e.havingStyle(e.style.text()):e,"ex"===t.unit)r=a.fontMetrics().xHeight;else{if("em"!==t.unit)throw new o("Invalid unit: '"+t.unit+"'");r=a.fontMetrics().quad}a!==e&&(r*=a.sizeMultiplier/e.sizeMultiplier)}return Math.min(t.number*r,e.maxSize)},Bt=["\\imath","\u0131","\\jmath","\u0237","\\pounds","\\mathsterling","\\textsterling","\xa3"],Ct=function(t,e,r){return j[r][t]&&j[r][t].replace&&(t=j[r][t].replace),{value:t,metrics:G(t,e,r)}},qt=function(t,e,r,a,n){var i,o=Ct(t,e,r),s=o.metrics;if(t=o.value,s){var h=s.italic;("text"===r||a&&"mathit"===a.font)&&(h=0),i=new E(t,s.height,s.depth,h,s.skew,s.width,n)}else"undefined"!=typeof console&&console.warn("No character metrics for '"+t+"' in style '"+e+"' and mode '"+r+"'"),i=new E(t,0,0,0,0,0,n);if(a){i.maxFontSize=a.sizeMultiplier,a.style.isTight()&&i.classes.push("mtight");var l=a.getColor();l&&(i.style.color=l)}return i},Nt=function(t,e){if(T(t.classes)!==T(e.classes)||t.skew!==e.skew||t.maxFontSize!==e.maxFontSize)return!1;for(var r in t.style)if(t.style.hasOwnProperty(r)&&t.style[r]!==e.style[r])return!1;for(var a in e.style)if(e.style.hasOwnProperty(a)&&t.style[a]!==e.style[a])return!1;return!0},It=function(t){for(var e=0,r=0,a=0,n=0;n<t.children.length;n++){var i=t.children[n];i.height>e&&(e=i.height),i.depth>r&&(r=i.depth),i.maxFontSize>a&&(a=i.maxFontSize)}t.height=e,t.depth=r,t.maxFontSize=a},Rt=function(t,e,r,a){var n=new N(t,e,r,a);return It(n),n},Ot=function(t,e,r,a){return new N(t,e,r,a)},Et=function(t){var e=new A(t);return It(e),e},Lt=function(t,e,r){var a="";switch(t){case"amsrm":a="AMS";break;case"textrm":a="Main";break;case"textsf":a="SansSerif";break;case"texttt":a="Typewriter";break;default:a=t}return a+"-"+("textbf"===e&&"textit"===r?"BoldItalic":"textbf"===e?"Bold":"textit"===e?"Italic":"Regular")},Ht={mathbf:{variant:"bold",fontName:"Main-Bold"},mathrm:{variant:"normal",fontName:"Main-Regular"},textit:{variant:"italic",fontName:"Main-Italic"},mathit:{variant:"italic",fontName:"Main-Italic"},mathbb:{variant:"double-struck",fontName:"AMS-Regular"},mathcal:{variant:"script",fontName:"Caligraphic-Regular"},mathfrak:{variant:"fraktur",fontName:"Fraktur-Regular"},mathscr:{variant:"script",fontName:"Script-Regular"},mathsf:{variant:"sans-serif",fontName:"SansSerif-Regular"},mathtt:{variant:"monospace",fontName:"Typewriter-Regular"}},Pt={vec:["vec",.471,.714],oiintSize1:["oiintSize1",.957,.499],oiintSize2:["oiintSize2",1.472,.659],oiiintSize1:["oiiintSize1",1.304,.499],oiiintSize2:["oiiintSize2",1.98,.659]},Dt={fontMap:Ht,makeSymbol:qt,mathsym:function(t,e,r,a){return void 0===a&&(a=[]),"boldsymbol"===r.font&&Ct(t,"Main-Bold",e).metrics?qt(t,"Main-Bold",e,r,a.concat(["mathbf"])):"\\"===t||"main"===j[e][t].font?qt(t,"Main-Regular",e,r,a):qt(t,"AMS-Regular",e,r,a.concat(["amsrm"]))},makeSpan:Rt,makeSvgSpan:Ot,makeLineSpan:function(t,e,r){var a=Rt([t],[],e);return a.height=Math.max(r||e.fontMetrics().defaultRuleThickness,e.minRuleThickness),a.style.borderBottomWidth=a.height+"em",a.maxFontSize=1,a},makeAnchor:function(t,e,r,a){var n=new I(t,e,r,a);return It(n),n},makeFragment:Et,wrapFragment:function(t,e){return t instanceof A?Rt([],[t],e):t},makeVList:function(t,e){for(var r=function(t){if("individualShift"===t.positionType){for(var e=t.children,r=[e[0]],a=-e[0].shift-e[0].elem.depth,n=a,i=1;i<e.length;i++){var o=-e[i].shift-n-e[i].elem.depth,s=o-(e[i-1].elem.height+e[i-1].elem.depth);n+=o,r.push({type:"kern",size:s}),r.push(e[i])}return{children:r,depth:a}}var h;if("top"===t.positionType){for(var l=t.positionData,m=0;m<t.children.length;m++){var c=t.children[m];l-="kern"===c.type?c.size:c.elem.height+c.elem.depth}h=l}else if("bottom"===t.positionType)h=-t.positionData;else{var u=t.children[0];if("elem"!==u.type)throw new Error('First child must have type "elem".');if("shift"===t.positionType)h=-u.elem.depth-t.positionData;else{if("firstBaseline"!==t.positionType)throw new Error("Invalid positionType "+t.positionType+".");h=-u.elem.depth}}return{children:t.children,depth:h}}(t),a=r.children,n=r.depth,i=0,o=0;o<a.length;o++){var s=a[o];if("elem"===s.type){var h=s.elem;i=Math.max(i,h.maxFontSize,h.height)}}i+=2;var l=Rt(["pstrut"],[]);l.style.height=i+"em";for(var m=[],c=n,u=n,p=n,d=0;d<a.length;d++){var f=a[d];if("kern"===f.type)p+=f.size;else{var g=f.elem,x=f.wrapperClasses||[],v=f.wrapperStyle||{},b=Rt(x,[l,g],void 0,v);b.style.top=-i-p-g.depth+"em",f.marginLeft&&(b.style.marginLeft=f.marginLeft),f.marginRight&&(b.style.marginRight=f.marginRight),m.push(b),p+=g.height+g.depth}c=Math.min(c,p),u=Math.max(u,p)}var y,w=Rt(["vlist"],m);if(w.style.height=u+"em",c<0){var k=Rt([],[]),S=Rt(["vlist"],[k]);S.style.height=-c+"em";var M=Rt(["vlist-s"],[new E("\u200b")]);y=[Rt(["vlist-r"],[w,M]),Rt(["vlist-r"],[S])]}else y=[Rt(["vlist-r"],[w])];var z=Rt(["vlist-t"],y);return 2===y.length&&z.classes.push("vlist-t2"),z.height=u,z.depth=-c,z},makeOrd:function(t,e,r){var a,n=t.mode,i=t.text,s=["mord"],h="math"===n||"text"===n&&e.font,l=h?e.font:e.fontFamily;if(55349===i.charCodeAt(0)){var m=function(t,e){var r=1024*(t.charCodeAt(0)-55296)+(t.charCodeAt(1)-56320)+65536,a="math"===e?0:1;if(119808<=r&&r<120484){var n=Math.floor((r-119808)/26);return[xt[n][2],xt[n][a]]}if(120782<=r&&r<=120831){var i=Math.floor((r-120782)/10);return[vt[i][2],vt[i][a]]}if(120485===r||120486===r)return[xt[0][2],xt[0][a]];if(120486<r&&r<120782)return["",""];throw new o("Unsupported character: "+t)}(i,n),u=m[0],p=m[1];return qt(i,u,n,e,s.concat(p))}if(l){var d,f;if("boldsymbol"===l||"mathnormal"===l){var g="boldsymbol"===l?function(t,e,r,a){return Ct(t,"Math-BoldItalic",e).metrics?{fontName:"Math-BoldItalic",fontClass:"boldsymbol"}:{fontName:"Main-Bold",fontClass:"mathbf"}}(i,n):(a=i,c.contains(Bt,a)?{fontName:"Main-Italic",fontClass:"mathit"}:/[0-9]/.test(a.charAt(0))?{fontName:"Caligraphic-Regular",fontClass:"mathcal"}:{fontName:"Math-Italic",fontClass:"mathdefault"});d=g.fontName,f=[g.fontClass]}else c.contains(Bt,i)?(d="Main-Italic",f=["mathit"]):h?(d=Ht[l].fontName,f=[l]):(d=Lt(l,e.fontWeight,e.fontShape),f=[l,e.fontWeight,e.fontShape]);if(Ct(i,d,n).metrics)return qt(i,d,n,e,s.concat(f));if(rt.hasOwnProperty(i)&&"Typewriter"===d.substr(0,10)){for(var x=[],v=0;v<i.length;v++)x.push(qt(i[v],d,n,e,s.concat(f)));return Et(x)}}if("mathord"===r){var b=function(t,e,r,a){return/[0-9]/.test(t.charAt(0))||c.contains(Bt,t)?{fontName:"Main-Italic",fontClass:"mathit"}:{fontName:"Math-Italic",fontClass:"mathdefault"}}(i);return qt(i,b.fontName,n,e,s.concat([b.fontClass]))}if("textord"===r){var y=j[n][i]&&j[n][i].font;if("ams"===y){var w=Lt("amsrm",e.fontWeight,e.fontShape);return qt(i,w,n,e,s.concat("amsrm",e.fontWeight,e.fontShape))}if("main"!==y&&y){var k=Lt(y,e.fontWeight,e.fontShape);return qt(i,k,n,e,s.concat(k,e.fontWeight,e.fontShape))}var S=Lt("textrm",e.fontWeight,e.fontShape);return qt(i,S,n,e,s.concat(e.fontWeight,e.fontShape))}throw new Error("unexpected type: "+r+" in makeOrd")},makeGlue:function(t,e){var r=Rt(["mspace"],[],e),a=Tt(t,e);return r.style.marginRight=a+"em",r},staticSvg:function(t,e){var r=Pt[t],a=r[0],n=r[1],i=r[2],o=new H(a),s=new L([o],{width:n+"em",height:i+"em",style:"width:"+n+"em",viewBox:"0 0 "+1e3*n+" "+1e3*i,preserveAspectRatio:"xMinYMin"}),h=Ot(["overlay"],[s],e);return h.height=i,h.style.height=i+"em",h.style.width=n+"em",h},svgData:Pt,tryCombineChars:function(t){for(var e=0;e<t.length-1;e++){var r=t[e],a=t[e+1];r instanceof E&&a instanceof E&&Nt(r,a)&&(r.text+=a.text,r.height=Math.max(r.height,a.height),r.depth=Math.max(r.depth,a.depth),r.italic=a.italic,t.splice(e+1,1),e--)}return t}};function Ft(t,e){var r=Vt(t,e);if(!r)throw new Error("Expected node of type "+e+", but got "+(t?"node of type "+t.type:String(t)));return r}function Vt(t,e){return t&&t.type===e?t:null}function Ut(t,e){var r=function(t,e){return t&&"atom"===t.type&&t.family===e?t:null}(t,e);if(!r)throw new Error('Expected node of type "atom" and family "'+e+'", but got '+(t?"atom"===t.type?"atom of family "+t.family:"node of type "+t.type:String(t)));return r}function Gt(t){var e=Yt(t);if(!e)throw new Error("Expected node of symbol group type, but got "+(t?"node of type "+t.type:String(t)));return e}function Yt(t){return t&&("atom"===t.type||X.hasOwnProperty(t.type))?t:null}var Wt={number:3,unit:"mu"},Xt={number:4,unit:"mu"},_t={number:5,unit:"mu"},jt={mord:{mop:Wt,mbin:Xt,mrel:_t,minner:Wt},mop:{mord:Wt,mop:Wt,mrel:_t,minner:Wt},mbin:{mord:Xt,mop:Xt,mopen:Xt,minner:Xt},mrel:{mord:_t,mop:_t,mopen:_t,minner:_t},mopen:{},mclose:{mop:Wt,mbin:Xt,mrel:_t,minner:Wt},mpunct:{mord:Wt,mop:Wt,mrel:_t,mopen:Wt,mclose:Wt,mpunct:Wt,minner:Wt},minner:{mord:Wt,mop:Wt,mbin:Xt,mrel:_t,mopen:Wt,mpunct:Wt,minner:Wt}},$t={mord:{mop:Wt},mop:{mord:Wt,mop:Wt},mbin:{},mrel:{},mopen:{},mclose:{mop:Wt},mpunct:{},minner:{mop:Wt}},Zt={},Kt={},Jt={};function Qt(t){for(var e=t.type,r=t.names,a=t.props,n=t.handler,i=t.htmlBuilder,o=t.mathmlBuilder,s={type:e,numArgs:a.numArgs,argTypes:a.argTypes,greediness:void 0===a.greediness?1:a.greediness,allowedInText:!!a.allowedInText,allowedInMath:void 0===a.allowedInMath||a.allowedInMath,numOptionalArgs:a.numOptionalArgs||0,infix:!!a.infix,handler:n},h=0;h<r.length;++h)Zt[r[h]]=s;e&&(i&&(Kt[e]=i),o&&(Jt[e]=o))}function te(t){Qt({type:t.type,names:[],props:{numArgs:0},handler:function(){throw new Error("Should never be called.")},htmlBuilder:t.htmlBuilder,mathmlBuilder:t.mathmlBuilder})}var ee=function(t){var e=Vt(t,"ordgroup");return e?e.body:[t]},re=Dt.makeSpan,ae=["leftmost","mbin","mopen","mrel","mop","mpunct"],ne=["rightmost","mrel","mclose","mpunct"],ie={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT},oe={mord:"mord",mop:"mop",mbin:"mbin",mrel:"mrel",mopen:"mopen",mclose:"mclose",mpunct:"mpunct",minner:"minner"},se=function(t,e,r,a){void 0===a&&(a=[null,null]);for(var n=[],i=0;i<t.length;i++){var o=ue(t[i],e);if(o instanceof A){var s=o.children;n.push.apply(n,s)}else n.push(o)}if(!r)return n;var h=e;if(1===t.length){var l=Vt(t[0],"sizing")||Vt(t[0],"styling");l&&("sizing"===l.type?h=e.havingSize(l.size):"styling"===l.type&&(h=e.havingStyle(ie[l.style])))}var m=re([a[0]||"leftmost"],[],e),u=re([a[1]||"rightmost"],[],e);return he(n,function(t,e){var r=e.classes[0],a=t.classes[0];"mbin"===r&&c.contains(ne,a)?e.classes[0]="mord":"mbin"===a&&c.contains(ae,r)&&(t.classes[0]="mord")},{node:m},u),he(n,function(t,e){var r=me(e),a=me(t),n=r&&a?t.hasClass("mtight")?$t[r][a]:jt[r][a]:null;if(n)return Dt.makeGlue(n,h)},{node:m},u),n},he=function t(e,r,a,n){n&&e.push(n);for(var i=0;i<e.length;i++){var o=e[i],s=le(o);if(s)t(s.children,r,a);else if("mspace"!==o.classes[0]){var h=r(o,a.node);h&&(a.insertAfter?a.insertAfter(h):(e.unshift(h),i++)),a.node=o,a.insertAfter=function(t){return function(r){e.splice(t+1,0,r),i++}}(i)}}n&&e.pop()},le=function(t){return t instanceof A||t instanceof I?t:null},me=function(t,e){return t?(e&&(t=function t(e,r){var a=le(e);if(a){var n=a.children;if(n.length){if("right"===r)return t(n[n.length-1],"right");if("left"===r)return t(n[0],"left")}}return e}(t,e)),oe[t.classes[0]]||null):null},ce=function(t,e){var r=["nulldelimiter"].concat(t.baseSizingClasses());return re(e.concat(r))},ue=function(t,e,r){if(!t)return re();if(Kt[t.type]){var a=Kt[t.type](t,e);if(r&&e.size!==r.size){a=re(e.sizingClasses(r),[a],e);var n=e.sizeMultiplier/r.sizeMultiplier;a.height*=n,a.depth*=n}return a}throw new o("Got group of unknown type: '"+t.type+"'")};function pe(t,e){var r=re(["base"],t,e),a=re(["strut"]);return a.style.height=r.height+r.depth+"em",a.style.verticalAlign=-r.depth+"em",r.children.unshift(a),r}function de(t,e){var r=null;1===t.length&&"tag"===t[0].type&&(r=t[0].tag,t=t[0].body);for(var a,n=se(t,e,!0),i=[],o=[],s=0;s<n.length;s++)if(o.push(n[s]),n[s].hasClass("mbin")||n[s].hasClass("mrel")||n[s].hasClass("allowbreak")){for(var h=!1;s<n.length-1&&n[s+1].hasClass("mspace")&&!n[s+1].hasClass("newline");)s++,o.push(n[s]),n[s].hasClass("nobreak")&&(h=!0);h||(i.push(pe(o,e)),o=[])}else n[s].hasClass("newline")&&(o.pop(),o.length>0&&(i.push(pe(o,e)),o=[]),i.push(n[s]));o.length>0&&i.push(pe(o,e)),r&&((a=pe(se(r,e,!0))).classes=["tag"],i.push(a));var l=re(["katex-html"],i);if(l.setAttribute("aria-hidden","true"),a){var m=a.children[0];m.style.height=l.height+l.depth+"em",m.style.verticalAlign=-l.depth+"em"}return l}function fe(t){return new A(t)}var ge=function(){function t(t,e){this.type=void 0,this.attributes=void 0,this.children=void 0,this.type=t,this.attributes={},this.children=e||[]}var e=t.prototype;return e.setAttribute=function(t,e){this.attributes[t]=e},e.getAttribute=function(t){return this.attributes[t]},e.toNode=function(){var t=document.createElementNS("http://www.w3.org/1998/Math/MathML",this.type);for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&t.setAttribute(e,this.attributes[e]);for(var r=0;r<this.children.length;r++)t.appendChild(this.children[r].toNode());return t},e.toMarkup=function(){var t="<"+this.type;for(var e in this.attributes)Object.prototype.hasOwnProperty.call(this.attributes,e)&&(t+=" "+e+'="',t+=c.escape(this.attributes[e]),t+='"');t+=">";for(var r=0;r<this.children.length;r++)t+=this.children[r].toMarkup();return t+="</"+this.type+">"},e.toText=function(){return this.children.map(function(t){return t.toText()}).join("")},t}(),xe=function(){function t(t){this.text=void 0,this.text=t}var e=t.prototype;return e.toNode=function(){return document.createTextNode(this.text)},e.toMarkup=function(){return c.escape(this.toText())},e.toText=function(){return this.text},t}(),ve={MathNode:ge,TextNode:xe,SpaceNode:function(){function t(t){this.width=void 0,this.character=void 0,this.width=t,this.character=t>=.05555&&t<=.05556?"\u200a":t>=.1666&&t<=.1667?"\u2009":t>=.2222&&t<=.2223?"\u2005":t>=.2777&&t<=.2778?"\u2005\u200a":t>=-.05556&&t<=-.05555?"\u200a\u2063":t>=-.1667&&t<=-.1666?"\u2009\u2063":t>=-.2223&&t<=-.2222?"\u205f\u2063":t>=-.2778&&t<=-.2777?"\u2005\u2063":null}var e=t.prototype;return e.toNode=function(){if(this.character)return document.createTextNode(this.character);var t=document.createElementNS("http://www.w3.org/1998/Math/MathML","mspace");return t.setAttribute("width",this.width+"em"),t},e.toMarkup=function(){return this.character?"<mtext>"+this.character+"</mtext>":'<mspace width="'+this.width+'em"/>'},e.toText=function(){return this.character?this.character:" "},t}(),newDocumentFragment:fe},be=function(t,e,r){return!j[e][t]||!j[e][t].replace||55349===t.charCodeAt(0)||rt.hasOwnProperty(t)&&r&&(r.fontFamily&&"tt"===r.fontFamily.substr(4,2)||r.font&&"tt"===r.font.substr(4,2))||(t=j[e][t].replace),new ve.TextNode(t)},ye=function(t){return 1===t.length?t[0]:new ve.MathNode("mrow",t)},we=function(t,e){if("texttt"===e.fontFamily)return"monospace";if("textsf"===e.fontFamily)return"textit"===e.fontShape&&"textbf"===e.fontWeight?"sans-serif-bold-italic":"textit"===e.fontShape?"sans-serif-italic":"textbf"===e.fontWeight?"bold-sans-serif":"sans-serif";if("textit"===e.fontShape&&"textbf"===e.fontWeight)return"bold-italic";if("textit"===e.fontShape)return"italic";if("textbf"===e.fontWeight)return"bold";var r=e.font;if(!r||"mathnormal"===r)return null;var a=t.mode;if("mathit"===r)return"italic";if("boldsymbol"===r)return"bold-italic";if("mathbf"===r)return"bold";if("mathbb"===r)return"double-struck";if("mathfrak"===r)return"fraktur";if("mathscr"===r||"mathcal"===r)return"script";if("mathsf"===r)return"sans-serif";if("mathtt"===r)return"monospace";var n=t.text;return c.contains(["\\imath","\\jmath"],n)?null:(j[a][n]&&j[a][n].replace&&(n=j[a][n].replace),G(n,Dt.fontMap[r].fontName,a)?Dt.fontMap[r].variant:null)},ke=function(t,e,r){if(1===t.length){var a=Me(t[0],e);return r&&a instanceof ge&&"mo"===a.type&&(a.setAttribute("lspace","0em"),a.setAttribute("rspace","0em")),[a]}for(var n,i=[],o=0;o<t.length;o++){var s=Me(t[o],e);if(s instanceof ge&&n instanceof ge){if("mtext"===s.type&&"mtext"===n.type&&s.getAttribute("mathvariant")===n.getAttribute("mathvariant")){var h;(h=n.children).push.apply(h,s.children);continue}if("mn"===s.type&&"mn"===n.type){var l;(l=n.children).push.apply(l,s.children);continue}if("mi"===s.type&&1===s.children.length&&"mn"===n.type){var m=s.children[0];if(m instanceof xe&&"."===m.text){var c;(c=n.children).push.apply(c,s.children);continue}}else if("mi"===n.type&&1===n.children.length){var u=n.children[0];if(u instanceof xe&&"\u0338"===u.text&&("mo"===s.type||"mi"===s.type||"mn"===s.type)){var p=s.children[0];p instanceof xe&&p.text.length>0&&(p.text=p.text.slice(0,1)+"\u0338"+p.text.slice(1),i.pop())}}}i.push(s),n=s}return i},Se=function(t,e,r){return ye(ke(t,e,r))},Me=function(t,e){if(!t)return new ve.MathNode("mrow");if(Jt[t.type])return Jt[t.type](t,e);throw new o("Got group of unknown type: '"+t.type+"'")};function ze(t,e,r,a){var n,i=ke(t,r);n=1===i.length&&i[0]instanceof ge&&c.contains(["mrow","mtable"],i[0].type)?i[0]:new ve.MathNode("mrow",i);var o=new ve.MathNode("annotation",[new ve.TextNode(e)]);o.setAttribute("encoding","application/x-tex");var s=new ve.MathNode("semantics",[n,o]),h=new ve.MathNode("math",[s]);h.setAttribute("xmlns","http://www.w3.org/1998/Math/MathML");var l=a?"katex":"katex-mathml";return Dt.makeSpan([l],[h])}var Ae=function(t){return new St({style:t.displayMode?w.DISPLAY:w.TEXT,maxSize:t.maxSize,minRuleThickness:t.minRuleThickness})},Te=function(t,e){if(e.displayMode){var r=["katex-display"];e.leqno&&r.push("leqno"),e.fleqn&&r.push("fleqn"),t=Dt.makeSpan(r,[t])}return t},Be=function(t,e,r){var a,n=Ae(r);if("mathml"===r.output)return ze(t,e,n,!0);if("html"===r.output){var i=de(t,n);a=Dt.makeSpan(["katex"],[i])}else{var o=ze(t,e,n,!1),s=de(t,n);a=Dt.makeSpan(["katex"],[o,s])}return Te(a,r)},Ce={widehat:"^",widecheck:"\u02c7",widetilde:"~",utilde:"~",overleftarrow:"\u2190",underleftarrow:"\u2190",xleftarrow:"\u2190",overrightarrow:"\u2192",underrightarrow:"\u2192",xrightarrow:"\u2192",underbrace:"\u23df",overbrace:"\u23de",overgroup:"\u23e0",undergroup:"\u23e1",overleftrightarrow:"\u2194",underleftrightarrow:"\u2194",xleftrightarrow:"\u2194",Overrightarrow:"\u21d2",xRightarrow:"\u21d2",overleftharpoon:"\u21bc",xleftharpoonup:"\u21bc",overrightharpoon:"\u21c0",xrightharpoonup:"\u21c0",xLeftarrow:"\u21d0",xLeftrightarrow:"\u21d4",xhookleftarrow:"\u21a9",xhookrightarrow:"\u21aa",xmapsto:"\u21a6",xrightharpoondown:"\u21c1",xleftharpoondown:"\u21bd",xrightleftharpoons:"\u21cc",xleftrightharpoons:"\u21cb",xtwoheadleftarrow:"\u219e",xtwoheadrightarrow:"\u21a0",xlongequal:"=",xtofrom:"\u21c4",xrightleftarrows:"\u21c4",xrightequilibrium:"\u21cc",xleftequilibrium:"\u21cb"},qe={overrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],overleftarrow:[["leftarrow"],.888,522,"xMinYMin"],underrightarrow:[["rightarrow"],.888,522,"xMaxYMin"],underleftarrow:[["leftarrow"],.888,522,"xMinYMin"],xrightarrow:[["rightarrow"],1.469,522,"xMaxYMin"],xleftarrow:[["leftarrow"],1.469,522,"xMinYMin"],Overrightarrow:[["doublerightarrow"],.888,560,"xMaxYMin"],xRightarrow:[["doublerightarrow"],1.526,560,"xMaxYMin"],xLeftarrow:[["doubleleftarrow"],1.526,560,"xMinYMin"],overleftharpoon:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoonup:[["leftharpoon"],.888,522,"xMinYMin"],xleftharpoondown:[["leftharpoondown"],.888,522,"xMinYMin"],overrightharpoon:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoonup:[["rightharpoon"],.888,522,"xMaxYMin"],xrightharpoondown:[["rightharpoondown"],.888,522,"xMaxYMin"],xlongequal:[["longequal"],.888,334,"xMinYMin"],xtwoheadleftarrow:[["twoheadleftarrow"],.888,334,"xMinYMin"],xtwoheadrightarrow:[["twoheadrightarrow"],.888,334,"xMaxYMin"],overleftrightarrow:[["leftarrow","rightarrow"],.888,522],overbrace:[["leftbrace","midbrace","rightbrace"],1.6,548],underbrace:[["leftbraceunder","midbraceunder","rightbraceunder"],1.6,548],underleftrightarrow:[["leftarrow","rightarrow"],.888,522],xleftrightarrow:[["leftarrow","rightarrow"],1.75,522],xLeftrightarrow:[["doubleleftarrow","doublerightarrow"],1.75,560],xrightleftharpoons:[["leftharpoondownplus","rightharpoonplus"],1.75,716],xleftrightharpoons:[["leftharpoonplus","rightharpoondownplus"],1.75,716],xhookleftarrow:[["leftarrow","righthook"],1.08,522],xhookrightarrow:[["lefthook","rightarrow"],1.08,522],overlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],underlinesegment:[["leftlinesegment","rightlinesegment"],.888,522],overgroup:[["leftgroup","rightgroup"],.888,342],undergroup:[["leftgroupunder","rightgroupunder"],.888,342],xmapsto:[["leftmapsto","rightarrow"],1.5,522],xtofrom:[["leftToFrom","rightToFrom"],1.75,528],xrightleftarrows:[["baraboveleftarrow","rightarrowabovebar"],1.75,901],xrightequilibrium:[["baraboveshortleftharpoon","rightharpoonaboveshortbar"],1.75,716],xleftequilibrium:[["shortbaraboveleftharpoon","shortrightharpoonabovebar"],1.75,716]},Ne=function(t){return"ordgroup"===t.type?t.body.length:1},Ie=function(t,e,r,a){var n,i=t.height+t.depth+2*r;if(/fbox|color/.test(e)){if(n=Dt.makeSpan(["stretchy",e],[],a),"fbox"===e){var o=a.color&&a.getColor();o&&(n.style.borderColor=o)}}else{var s=[];/^[bx]cancel$/.test(e)&&s.push(new P({x1:"0",y1:"0",x2:"100%",y2:"100%","stroke-width":"0.046em"})),/^x?cancel$/.test(e)&&s.push(new P({x1:"0",y1:"100%",x2:"100%",y2:"0","stroke-width":"0.046em"}));var h=new L(s,{width:"100%",height:i+"em"});n=Dt.makeSvgSpan([],[h],a)}return n.height=i,n.style.height=i+"em",n},Re=function(t){var e=new ve.MathNode("mo",[new ve.TextNode(Ce[t.substr(1)])]);return e.setAttribute("stretchy","true"),e},Oe=function(t,e){var r=function(){var r=4e5,a=t.label.substr(1);if(c.contains(["widehat","widecheck","widetilde","utilde"],a)){var n,i,o,s=Ne(t.base);if(s>5)"widehat"===a||"widecheck"===a?(n=420,r=2364,o=.42,i=a+"4"):(n=312,r=2340,o=.34,i="tilde4");else{var h=[1,1,2,2,3,3][s];"widehat"===a||"widecheck"===a?(r=[0,1062,2364,2364,2364][h],n=[0,239,300,360,420][h],o=[0,.24,.3,.3,.36,.42][h],i=a+h):(r=[0,600,1033,2339,2340][h],n=[0,260,286,306,312][h],o=[0,.26,.286,.3,.306,.34][h],i="tilde"+h)}var l=new H(i),m=new L([l],{width:"100%",height:o+"em",viewBox:"0 0 "+r+" "+n,preserveAspectRatio:"none"});return{span:Dt.makeSvgSpan([],[m],e),minWidth:0,height:o}}var u,p,d=[],f=qe[a],g=f[0],x=f[1],v=f[2],b=v/1e3,y=g.length;if(1===y)u=["hide-tail"],p=[f[3]];else if(2===y)u=["halfarrow-left","halfarrow-right"],p=["xMinYMin","xMaxYMin"];else{if(3!==y)throw new Error("Correct katexImagesData or update code here to support\n "+y+" children.");u=["brace-left","brace-center","brace-right"],p=["xMinYMin","xMidYMin","xMaxYMin"]}for(var w=0;w<y;w++){var k=new H(g[w]),S=new L([k],{width:"400em",height:b+"em",viewBox:"0 0 "+r+" "+v,preserveAspectRatio:p[w]+" slice"}),M=Dt.makeSvgSpan([u[w]],[S],e);if(1===y)return{span:M,minWidth:x,height:b};M.style.height=b+"em",d.push(M)}return{span:Dt.makeSpan(["stretchy"],d,e),minWidth:x,height:b}}(),a=r.span,n=r.minWidth,i=r.height;return a.height=i,a.style.height=i+"em",n>0&&(a.style.minWidth=n+"em"),a},Ee=function(t,e){var r,a,n,i=Vt(t,"supsub");i?(r=(a=Ft(i.base,"accent")).base,i.base=r,n=function(t){if(t instanceof N)return t;throw new Error("Expected span<HtmlDomNode> but got "+String(t)+".")}(ue(i,e)),i.base=a):r=(a=Ft(t,"accent")).base;var o=ue(r,e.havingCrampedStyle()),s=0;if(a.isShifty&&c.isCharacterBox(r)){var h=c.getBaseElem(r);s=D(ue(h,e.havingCrampedStyle())).skew}var l,m=Math.min(o.height,e.fontMetrics().xHeight);if(a.isStretchy)l=Oe(a,e),l=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:o},{type:"elem",elem:l,wrapperClasses:["svg-align"],wrapperStyle:s>0?{width:"calc(100% - "+2*s+"em)",marginLeft:2*s+"em"}:void 0}]},e);else{var u,p;"\\vec"===a.label?(u=Dt.staticSvg("vec",e),p=Dt.svgData.vec[1]):((u=D(u=Dt.makeOrd({mode:a.mode,text:a.label},e,"textord"))).italic=0,p=u.width),l=Dt.makeSpan(["accent-body"],[u]);var d="\\textcircled"===a.label;d&&(l.classes.push("accent-full"),m=o.height);var f=s;d||(f-=p/2),l.style.left=f+"em","\\textcircled"===a.label&&(l.style.top=".2em"),l=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:o},{type:"kern",size:-m},{type:"elem",elem:l}]},e)}var g=Dt.makeSpan(["mord","accent"],[l],e);return n?(n.children[0]=g,n.height=Math.max(g.height,n.height),n.classes[0]="mord",n):g},Le=function(t,e){var r=t.isStretchy?Re(t.label):new ve.MathNode("mo",[be(t.label,t.mode)]),a=new ve.MathNode("mover",[Me(t.base,e),r]);return a.setAttribute("accent","true"),a},He=new RegExp(["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring"].map(function(t){return"\\"+t}).join("|"));Qt({type:"accent",names:["\\acute","\\grave","\\ddot","\\tilde","\\bar","\\breve","\\check","\\hat","\\vec","\\dot","\\mathring","\\widecheck","\\widehat","\\widetilde","\\overrightarrow","\\overleftarrow","\\Overrightarrow","\\overleftrightarrow","\\overgroup","\\overlinesegment","\\overleftharpoon","\\overrightharpoon"],props:{numArgs:1},handler:function(t,e){var r=e[0],a=!He.test(t.funcName),n=!a||"\\widehat"===t.funcName||"\\widetilde"===t.funcName||"\\widecheck"===t.funcName;return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:a,isShifty:n,base:r}},htmlBuilder:Ee,mathmlBuilder:Le}),Qt({type:"accent",names:["\\'","\\`","\\^","\\~","\\=","\\u","\\.",'\\"',"\\r","\\H","\\v","\\textcircled"],props:{numArgs:1,allowedInText:!0,allowedInMath:!1},handler:function(t,e){var r=e[0];return{type:"accent",mode:t.parser.mode,label:t.funcName,isStretchy:!1,isShifty:!0,base:r}},htmlBuilder:Ee,mathmlBuilder:Le}),Qt({type:"accentUnder",names:["\\underleftarrow","\\underrightarrow","\\underleftrightarrow","\\undergroup","\\underlinesegment","\\utilde"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"accentUnder",mode:r.mode,label:a,base:n}},htmlBuilder:function(t,e){var r=ue(t.base,e),a=Oe(t,e),n="\\utilde"===t.label?.12:0,i=Dt.makeVList({positionType:"bottom",positionData:a.height+n,children:[{type:"elem",elem:a,wrapperClasses:["svg-align"]},{type:"kern",size:n},{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord","accentunder"],[i],e)},mathmlBuilder:function(t,e){var r=Re(t.label),a=new ve.MathNode("munder",[Me(t.base,e),r]);return a.setAttribute("accentunder","true"),a}});var Pe=function(t){var e=new ve.MathNode("mpadded",t?[t]:[]);return e.setAttribute("width","+0.6em"),e.setAttribute("lspace","0.3em"),e};Qt({type:"xArrow",names:["\\xleftarrow","\\xrightarrow","\\xLeftarrow","\\xRightarrow","\\xleftrightarrow","\\xLeftrightarrow","\\xhookleftarrow","\\xhookrightarrow","\\xmapsto","\\xrightharpoondown","\\xrightharpoonup","\\xleftharpoondown","\\xleftharpoonup","\\xrightleftharpoons","\\xleftrightharpoons","\\xlongequal","\\xtwoheadrightarrow","\\xtwoheadleftarrow","\\xtofrom","\\xrightleftarrows","\\xrightequilibrium","\\xleftequilibrium"],props:{numArgs:1,numOptionalArgs:1},handler:function(t,e,r){var a=t.parser,n=t.funcName;return{type:"xArrow",mode:a.mode,label:n,body:e[0],below:r[0]}},htmlBuilder:function(t,e){var r,a=e.style,n=e.havingStyle(a.sup()),i=Dt.wrapFragment(ue(t.body,n,e),e);i.classes.push("x-arrow-pad"),t.below&&(n=e.havingStyle(a.sub()),(r=Dt.wrapFragment(ue(t.below,n,e),e)).classes.push("x-arrow-pad"));var o,s=Oe(t,e),h=-e.fontMetrics().axisHeight+.5*s.height,l=-e.fontMetrics().axisHeight-.5*s.height-.111;if((i.depth>.25||"\\xleftequilibrium"===t.label)&&(l-=i.depth),r){var m=-e.fontMetrics().axisHeight+r.height+.5*s.height+.111;o=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:l},{type:"elem",elem:s,shift:h},{type:"elem",elem:r,shift:m}]},e)}else o=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:i,shift:l},{type:"elem",elem:s,shift:h}]},e);return o.children[0].children[0].children[1].classes.push("svg-align"),Dt.makeSpan(["mrel","x-arrow"],[o],e)},mathmlBuilder:function(t,e){var r,a=Re(t.label);if(t.body){var n=Pe(Me(t.body,e));if(t.below){var i=Pe(Me(t.below,e));r=new ve.MathNode("munderover",[a,i,n])}else r=new ve.MathNode("mover",[a,n])}else if(t.below){var o=Pe(Me(t.below,e));r=new ve.MathNode("munder",[a,o])}else r=Pe(),r=new ve.MathNode("mover",[a,r]);return r}}),Qt({type:"textord",names:["\\@char"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){for(var r=t.parser,a=Ft(e[0],"ordgroup").body,n="",i=0;i<a.length;i++){n+=Ft(a[i],"textord").text}var s=parseInt(n);if(isNaN(s))throw new o("\\@char has non-numeric argument "+n);return{type:"textord",mode:r.mode,text:String.fromCharCode(s)}}});var De=function(t,e){var r=se(t.body,e.withColor(t.color),!1);return Dt.makeFragment(r)},Fe=function(t,e){var r=ke(t.body,e.withColor(t.color)),a=new ve.MathNode("mstyle",r);return a.setAttribute("mathcolor",t.color),a};Qt({type:"color",names:["\\textcolor"],props:{numArgs:2,allowedInText:!0,greediness:3,argTypes:["color","original"]},handler:function(t,e){var r=t.parser,a=Ft(e[0],"color-token").color,n=e[1];return{type:"color",mode:r.mode,color:a,body:ee(n)}},htmlBuilder:De,mathmlBuilder:Fe}),Qt({type:"color",names:["\\color"],props:{numArgs:1,allowedInText:!0,greediness:3,argTypes:["color"]},handler:function(t,e){var r=t.parser,a=t.breakOnTokenText,n=Ft(e[0],"color-token").color;r.gullet.macros.set("\\current@color",n);var i=r.parseExpression(!0,a);return{type:"color",mode:r.mode,color:n,body:i}},htmlBuilder:De,mathmlBuilder:Fe}),Qt({type:"cr",names:["\\cr","\\newline"],props:{numArgs:0,numOptionalArgs:1,argTypes:["size"],allowedInText:!0},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=r[0],o="\\cr"===n,s=!1;return o||(s=!a.settings.displayMode||!a.settings.useStrictBehavior("newLineInDisplayMode","In LaTeX, \\\\ or \\newline does nothing in display mode")),{type:"cr",mode:a.mode,newLine:s,newRow:o,size:i&&Ft(i,"size").value}},htmlBuilder:function(t,e){if(t.newRow)throw new o("\\cr valid only within a tabular/array environment");var r=Dt.makeSpan(["mspace"],[],e);return t.newLine&&(r.classes.push("newline"),t.size&&(r.style.marginTop=Tt(t.size,e)+"em")),r},mathmlBuilder:function(t,e){var r=new ve.MathNode("mspace");return t.newLine&&(r.setAttribute("linebreak","newline"),t.size&&r.setAttribute("height",Tt(t.size,e)+"em")),r}});var Ve=function(t,e,r){var a=G(j.math[t]&&j.math[t].replace||t,e,r);if(!a)throw new Error("Unsupported symbol "+t+" and font size "+e+".");return a},Ue=function(t,e,r,a){var n=r.havingBaseStyle(e),i=Dt.makeSpan(a.concat(n.sizingClasses(r)),[t],r),o=n.sizeMultiplier/r.sizeMultiplier;return i.height*=o,i.depth*=o,i.maxFontSize=n.sizeMultiplier,i},Ge=function(t,e,r){var a=e.havingBaseStyle(r),n=(1-e.sizeMultiplier/a.sizeMultiplier)*e.fontMetrics().axisHeight;t.classes.push("delimcenter"),t.style.top=n+"em",t.height-=n,t.depth+=n},Ye=function(t,e,r,a,n,i){var o=function(t,e,r,a){return Dt.makeSymbol(t,"Size"+e+"-Regular",r,a)}(t,e,n,a),s=Ue(Dt.makeSpan(["delimsizing","size"+e],[o],a),w.TEXT,a,i);return r&&Ge(s,a,w.TEXT),s},We=function(t,e,r){var a;return a="Size1-Regular"===e?"delim-size1":"delim-size4",{type:"elem",elem:Dt.makeSpan(["delimsizinginner",a],[Dt.makeSpan([],[Dt.makeSymbol(t,e,r)])])}},Xe={type:"kern",size:-.005},_e=function(t,e,r,a,n,i){var o,s,h,l;o=h=l=t,s=null;var m="Size1-Regular";"\\uparrow"===t?h=l="\u23d0":"\\Uparrow"===t?h=l="\u2016":"\\downarrow"===t?o=h="\u23d0":"\\Downarrow"===t?o=h="\u2016":"\\updownarrow"===t?(o="\\uparrow",h="\u23d0",l="\\downarrow"):"\\Updownarrow"===t?(o="\\Uparrow",h="\u2016",l="\\Downarrow"):"["===t||"\\lbrack"===t?(o="\u23a1",h="\u23a2",l="\u23a3",m="Size4-Regular"):"]"===t||"\\rbrack"===t?(o="\u23a4",h="\u23a5",l="\u23a6",m="Size4-Regular"):"\\lfloor"===t||"\u230a"===t?(h=o="\u23a2",l="\u23a3",m="Size4-Regular"):"\\lceil"===t||"\u2308"===t?(o="\u23a1",h=l="\u23a2",m="Size4-Regular"):"\\rfloor"===t||"\u230b"===t?(h=o="\u23a5",l="\u23a6",m="Size4-Regular"):"\\rceil"===t||"\u2309"===t?(o="\u23a4",h=l="\u23a5",m="Size4-Regular"):"("===t||"\\lparen"===t?(o="\u239b",h="\u239c",l="\u239d",m="Size4-Regular"):")"===t||"\\rparen"===t?(o="\u239e",h="\u239f",l="\u23a0",m="Size4-Regular"):"\\{"===t||"\\lbrace"===t?(o="\u23a7",s="\u23a8",l="\u23a9",h="\u23aa",m="Size4-Regular"):"\\}"===t||"\\rbrace"===t?(o="\u23ab",s="\u23ac",l="\u23ad",h="\u23aa",m="Size4-Regular"):"\\lgroup"===t||"\u27ee"===t?(o="\u23a7",l="\u23a9",h="\u23aa",m="Size4-Regular"):"\\rgroup"===t||"\u27ef"===t?(o="\u23ab",l="\u23ad",h="\u23aa",m="Size4-Regular"):"\\lmoustache"===t||"\u23b0"===t?(o="\u23a7",l="\u23ad",h="\u23aa",m="Size4-Regular"):"\\rmoustache"!==t&&"\u23b1"!==t||(o="\u23ab",l="\u23a9",h="\u23aa",m="Size4-Regular");var c=Ve(o,m,n),u=c.height+c.depth,p=Ve(h,m,n),d=p.height+p.depth,f=Ve(l,m,n),g=f.height+f.depth,x=0,v=1;if(null!==s){var b=Ve(s,m,n);x=b.height+b.depth,v=2}var y=u+g+x,k=Math.max(0,Math.ceil((e-y)/(v*d))),S=y+k*v*d,M=a.fontMetrics().axisHeight;r&&(M*=a.sizeMultiplier);var z=S/2-M,A=.005*(k+1)-d,T=[];if(T.push(We(l,m,n)),null===s)for(var B=0;B<k;B++)T.push(Xe),T.push(We(h,m,n));else{for(var C=0;C<k;C++)T.push(Xe),T.push(We(h,m,n));T.push({type:"kern",size:A}),T.push(We(h,m,n)),T.push(Xe),T.push(We(s,m,n));for(var q=0;q<k;q++)T.push(Xe),T.push(We(h,m,n))}T.push({type:"kern",size:A}),T.push(We(h,m,n)),T.push(Xe),T.push(We(o,m,n));var N=a.havingBaseStyle(w.TEXT),I=Dt.makeVList({positionType:"bottom",positionData:z,children:T},N);return Ue(Dt.makeSpan(["delimsizing","mult"],[I],N),w.TEXT,a,i)},je=function(t,e,r,a,n){var i=function(t,e,r){e*=1e3;var a="";switch(t){case"sqrtMain":a=function(t,e){return"M95,"+(622+t+e)+"\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl"+t/2.075+" -"+t+"\nc5.3,-9.3,12,-14,20,-14\nH400000v"+(40+t)+"H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM"+(834+t)+" "+e+"h400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize1":a=function(t,e){return"M263,"+(601+t+e)+"c0.7,0,18,39.7,52,119\nc34,79.3,68.167,158.7,102.5,238c34.3,79.3,51.8,119.3,52.5,120\nc340,-704.7,510.7,-1060.3,512,-1067\nl"+t/2.084+" -"+t+"\nc4.7,-7.3,11,-11,19,-11\nH40000v"+(40+t)+"H1012.3\ns-271.3,567,-271.3,567c-38.7,80.7,-84,175,-136,283c-52,108,-89.167,185.3,-111.5,232\nc-22.3,46.7,-33.8,70.3,-34.5,71c-4.7,4.7,-12.3,7,-23,7s-12,-1,-12,-1\ns-109,-253,-109,-253c-72.7,-168,-109.3,-252,-110,-252c-10.7,8,-22,16.7,-34,26\nc-22,17.3,-33.3,26,-34,26s-26,-26,-26,-26s76,-59,76,-59s76,-60,76,-60z\nM"+(1001+t)+" "+e+"h400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize2":a=function(t,e){return"M983 "+(10+t+e)+"\nl"+t/3.13+" -"+t+"\nc4,-6.7,10,-10,18,-10 H400000v"+(40+t)+"\nH1013.1s-83.4,268,-264.1,840c-180.7,572,-277,876.3,-289,913c-4.7,4.7,-12.7,7,-24,7\ns-12,0,-12,0c-1.3,-3.3,-3.7,-11.7,-7,-25c-35.3,-125.3,-106.7,-373.3,-214,-744\nc-10,12,-21,25,-33,39s-32,39,-32,39c-6,-5.3,-15,-14,-27,-26s25,-30,25,-30\nc26.7,-32.7,52,-63,76,-91s52,-60,52,-60s208,722,208,722\nc56,-175.3,126.3,-397.3,211,-666c84.7,-268.7,153.8,-488.2,207.5,-658.5\nc53.7,-170.3,84.5,-266.8,92.5,-289.5z\nM"+(1001+t)+" "+e+"h400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize3":a=function(t,e){return"M424,"+(2398+t+e)+"\nc-1.3,-0.7,-38.5,-172,-111.5,-514c-73,-342,-109.8,-513.3,-110.5,-514\nc0,-2,-10.7,14.3,-32,49c-4.7,7.3,-9.8,15.7,-15.5,25c-5.7,9.3,-9.8,16,-12.5,20\ns-5,7,-5,7c-4,-3.3,-8.3,-7.7,-13,-13s-13,-13,-13,-13s76,-122,76,-122s77,-121,77,-121\ns209,968,209,968c0,-2,84.7,-361.7,254,-1079c169.3,-717.3,254.7,-1077.7,256,-1081\nl"+t/4.223+" -"+t+"c4,-6.7,10,-10,18,-10 H400000\nv"+(40+t)+"H1014.6\ns-87.3,378.7,-272.6,1166c-185.3,787.3,-279.3,1182.3,-282,1185\nc-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2z M"+(1001+t)+" "+e+"\nh400000v"+(40+t)+"h-400000z"}(e,80);break;case"sqrtSize4":a=function(t,e){return"M473,"+(2713+t+e)+"\nc339.3,-1799.3,509.3,-2700,510,-2702 l"+t/5.298+" -"+t+"\nc3.3,-7.3,9.3,-11,18,-11 H400000v"+(40+t)+"H1017.7\ns-90.5,478,-276.2,1466c-185.7,988,-279.5,1483,-281.5,1485c-2,6,-10,9,-24,9\nc-8,0,-12,-0.7,-12,-2c0,-1.3,-5.3,-32,-16,-92c-50.7,-293.3,-119.7,-693.3,-207,-1200\nc0,-1.3,-5.3,8.7,-16,30c-10.7,21.3,-21.3,42.7,-32,64s-16,33,-16,33s-26,-26,-26,-26\ns76,-153,76,-153s77,-151,77,-151c0.7,0.7,35.7,202,105,604c67.3,400.7,102,602.7,104,\n606zM"+(1001+t)+" "+e+"h400000v"+(40+t)+"H1017.7z"}(e,80);break;case"sqrtTall":a=function(t,e,r){return"M702 "+(t+e)+"H400000"+(40+t)+"\nH742v"+(r-54-e-t)+"l-4 4-4 4c-.667.7 -2 1.5-4 2.5s-4.167 1.833-6.5 2.5-5.5 1-9.5 1\nh-12l-28-84c-16.667-52-96.667 -294.333-240-727l-212 -643 -85 170\nc-4-3.333-8.333-7.667-13 -13l-13-13l77-155 77-156c66 199.333 139 419.667\n219 661 l218 661zM702 "+e+"H400000v"+(40+t)+"H742z"}(e,80,r)}return a}(t,a,r),o=new H(t,i),s=new L([o],{width:"400em",height:e+"em",viewBox:"0 0 400000 "+r,preserveAspectRatio:"xMinYMin slice"});return Dt.makeSvgSpan(["hide-tail"],[s],n)},$e=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","\\surd"],Ze=["\\uparrow","\\downarrow","\\updownarrow","\\Uparrow","\\Downarrow","\\Updownarrow","|","\\|","\\vert","\\Vert","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1"],Ke=["<",">","\\langle","\\rangle","/","\\backslash","\\lt","\\gt"],Je=[0,1.2,1.8,2.4,3],Qe=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4}],tr=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"stack"}],er=[{type:"small",style:w.SCRIPTSCRIPT},{type:"small",style:w.SCRIPT},{type:"small",style:w.TEXT},{type:"large",size:1},{type:"large",size:2},{type:"large",size:3},{type:"large",size:4},{type:"stack"}],rr=function(t){if("small"===t.type)return"Main-Regular";if("large"===t.type)return"Size"+t.size+"-Regular";if("stack"===t.type)return"Size4-Regular";throw new Error("Add support for delim type '"+t.type+"' here.")},ar=function(t,e,r,a){for(var n=Math.min(2,3-a.style.size);n<r.length&&"stack"!==r[n].type;n++){var i=Ve(t,rr(r[n]),"math"),o=i.height+i.depth;if("small"===r[n].type&&(o*=a.havingBaseStyle(r[n].style).sizeMultiplier),o>e)return r[n]}return r[r.length-1]},nr=function(t,e,r,a,n,i){var o;"<"===t||"\\lt"===t||"\u27e8"===t?t="\\langle":">"!==t&&"\\gt"!==t&&"\u27e9"!==t||(t="\\rangle"),o=c.contains(Ke,t)?Qe:c.contains($e,t)?er:tr;var s=ar(t,e,o,a);return"small"===s.type?function(t,e,r,a,n,i){var o=Dt.makeSymbol(t,"Main-Regular",n,a),s=Ue(o,e,a,i);return r&&Ge(s,a,e),s}(t,s.style,r,a,n,i):"large"===s.type?Ye(t,s.size,r,a,n,i):_e(t,e,r,a,n,i)},ir=function(t,e){var r,a,n=e.havingBaseSizing(),i=ar("\\surd",t*n.sizeMultiplier,er,n),o=n.sizeMultiplier,s=Math.max(0,e.minRuleThickness-e.fontMetrics().sqrtRuleThickness),h=0,l=0,m=0;return"small"===i.type?(t<1?o=1:t<1.4&&(o=.7),l=(1+s)/o,(r=je("sqrtMain",h=(1+s+.08)/o,m=1e3+1e3*s+80,s,e)).style.minWidth="0.853em",a=.833/o):"large"===i.type?(m=1080*Je[i.size],l=(Je[i.size]+s)/o,h=(Je[i.size]+s+.08)/o,(r=je("sqrtSize"+i.size,h,m,s,e)).style.minWidth="1.02em",a=1/o):(h=t+s+.08,l=t+s,m=Math.floor(1e3*t+s)+80,(r=je("sqrtTall",h,m,s,e)).style.minWidth="0.742em",a=1.056),r.height=l,r.style.height=h+"em",{span:r,advanceWidth:a,ruleWidth:(e.fontMetrics().sqrtRuleThickness+s)*o}},or=function(t,e,r,a,n){if("<"===t||"\\lt"===t||"\u27e8"===t?t="\\langle":">"!==t&&"\\gt"!==t&&"\u27e9"!==t||(t="\\rangle"),c.contains($e,t)||c.contains(Ke,t))return Ye(t,e,!1,r,a,n);if(c.contains(Ze,t))return _e(t,Je[e],!1,r,a,n);throw new o("Illegal delimiter: '"+t+"'")},sr=nr,hr=function(t,e,r,a,n,i){var o=a.fontMetrics().axisHeight*a.sizeMultiplier,s=5/a.fontMetrics().ptPerEm,h=Math.max(e-o,r+o),l=Math.max(h/500*901,2*h-s);return nr(t,l,!0,a,n,i)},lr={"\\bigl":{mclass:"mopen",size:1},"\\Bigl":{mclass:"mopen",size:2},"\\biggl":{mclass:"mopen",size:3},"\\Biggl":{mclass:"mopen",size:4},"\\bigr":{mclass:"mclose",size:1},"\\Bigr":{mclass:"mclose",size:2},"\\biggr":{mclass:"mclose",size:3},"\\Biggr":{mclass:"mclose",size:4},"\\bigm":{mclass:"mrel",size:1},"\\Bigm":{mclass:"mrel",size:2},"\\biggm":{mclass:"mrel",size:3},"\\Biggm":{mclass:"mrel",size:4},"\\big":{mclass:"mord",size:1},"\\Big":{mclass:"mord",size:2},"\\bigg":{mclass:"mord",size:3},"\\Bigg":{mclass:"mord",size:4}},mr=["(","\\lparen",")","\\rparen","[","\\lbrack","]","\\rbrack","\\{","\\lbrace","\\}","\\rbrace","\\lfloor","\\rfloor","\u230a","\u230b","\\lceil","\\rceil","\u2308","\u2309","<",">","\\langle","\u27e8","\\rangle","\u27e9","\\lt","\\gt","\\lvert","\\rvert","\\lVert","\\rVert","\\lgroup","\\rgroup","\u27ee","\u27ef","\\lmoustache","\\rmoustache","\u23b0","\u23b1","/","\\backslash","|","\\vert","\\|","\\Vert","\\uparrow","\\Uparrow","\\downarrow","\\Downarrow","\\updownarrow","\\Updownarrow","."];function cr(t,e){var r=Yt(t);if(r&&c.contains(mr,r.text))return r;throw new o("Invalid delimiter: '"+(r?r.text:JSON.stringify(t))+"' after '"+e.funcName+"'",t)}function ur(t){if(!t.body)throw new Error("Bug: The leftright ParseNode wasn't fully parsed.")}Qt({type:"delimsizing",names:["\\bigl","\\Bigl","\\biggl","\\Biggl","\\bigr","\\Bigr","\\biggr","\\Biggr","\\bigm","\\Bigm","\\biggm","\\Biggm","\\big","\\Big","\\bigg","\\Bigg"],props:{numArgs:1},handler:function(t,e){var r=cr(e[0],t);return{type:"delimsizing",mode:t.parser.mode,size:lr[t.funcName].size,mclass:lr[t.funcName].mclass,delim:r.text}},htmlBuilder:function(t,e){return"."===t.delim?Dt.makeSpan([t.mclass]):or(t.delim,t.size,e,t.mode,[t.mclass])},mathmlBuilder:function(t){var e=[];"."!==t.delim&&e.push(be(t.delim,t.mode));var r=new ve.MathNode("mo",e);return"mopen"===t.mclass||"mclose"===t.mclass?r.setAttribute("fence","true"):r.setAttribute("fence","false"),r}}),Qt({type:"leftright-right",names:["\\right"],props:{numArgs:1},handler:function(t,e){var r=t.parser.gullet.macros.get("\\current@color");if(r&&"string"!=typeof r)throw new o("\\current@color set to non-string in \\right");return{type:"leftright-right",mode:t.parser.mode,delim:cr(e[0],t).text,color:r}}}),Qt({type:"leftright",names:["\\left"],props:{numArgs:1},handler:function(t,e){var r=cr(e[0],t),a=t.parser;++a.leftrightDepth;var n=a.parseExpression(!1);--a.leftrightDepth,a.expect("\\right",!1);var i=Ft(a.parseFunction(),"leftright-right");return{type:"leftright",mode:a.mode,body:n,left:r.text,right:i.delim,rightColor:i.color}},htmlBuilder:function(t,e){ur(t);for(var r,a,n=se(t.body,e,!0,["mopen","mclose"]),i=0,o=0,s=!1,h=0;h<n.length;h++)n[h].isMiddle?s=!0:(i=Math.max(n[h].height,i),o=Math.max(n[h].depth,o));if(i*=e.sizeMultiplier,o*=e.sizeMultiplier,r="."===t.left?ce(e,["mopen"]):hr(t.left,i,o,e,t.mode,["mopen"]),n.unshift(r),s)for(var l=1;l<n.length;l++){var m=n[l].isMiddle;m&&(n[l]=hr(m.delim,i,o,m.options,t.mode,[]))}if("."===t.right)a=ce(e,["mclose"]);else{var c=t.rightColor?e.withColor(t.rightColor):e;a=hr(t.right,i,o,c,t.mode,["mclose"])}return n.push(a),Dt.makeSpan(["minner"],n,e)},mathmlBuilder:function(t,e){ur(t);var r=ke(t.body,e);if("."!==t.left){var a=new ve.MathNode("mo",[be(t.left,t.mode)]);a.setAttribute("fence","true"),r.unshift(a)}if("."!==t.right){var n=new ve.MathNode("mo",[be(t.right,t.mode)]);n.setAttribute("fence","true"),t.rightColor&&n.setAttribute("mathcolor",t.rightColor),r.push(n)}return ye(r)}}),Qt({type:"middle",names:["\\middle"],props:{numArgs:1},handler:function(t,e){var r=cr(e[0],t);if(!t.parser.leftrightDepth)throw new o("\\middle without preceding \\left",r);return{type:"middle",mode:t.parser.mode,delim:r.text}},htmlBuilder:function(t,e){var r;if("."===t.delim)r=ce(e,[]);else{r=or(t.delim,1,e,t.mode,[]);var a={delim:t.delim,options:e};r.isMiddle=a}return r},mathmlBuilder:function(t,e){var r="\\vert"===t.delim||"|"===t.delim?be("|","text"):be(t.delim,t.mode),a=new ve.MathNode("mo",[r]);return a.setAttribute("fence","true"),a.setAttribute("lspace","0.05em"),a.setAttribute("rspace","0.05em"),a}});var pr=function(t,e){var r,a,n=Dt.wrapFragment(ue(t.body,e),e),i=t.label.substr(1),o=e.sizeMultiplier,s=0,h=c.isCharacterBox(t.body);if("sout"===i)(r=Dt.makeSpan(["stretchy","sout"])).height=e.fontMetrics().defaultRuleThickness/o,s=-.5*e.fontMetrics().xHeight;else{/cancel/.test(i)?h||n.classes.push("cancel-pad"):n.classes.push("boxpad");var l=0,m=0;/box/.test(i)?(m=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness),l=e.fontMetrics().fboxsep+("colorbox"===i?0:m)):l=h?.2:0,r=Ie(n,i,l,e),/fbox|boxed|fcolorbox/.test(i)&&(r.style.borderStyle="solid",r.style.borderWidth=m+"em"),s=n.depth+l,t.backgroundColor&&(r.style.backgroundColor=t.backgroundColor,t.borderColor&&(r.style.borderColor=t.borderColor))}return a=t.backgroundColor?Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:r,shift:s},{type:"elem",elem:n,shift:0}]},e):Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:n,shift:0},{type:"elem",elem:r,shift:s,wrapperClasses:/cancel/.test(i)?["svg-align"]:[]}]},e),/cancel/.test(i)&&(a.height=n.height,a.depth=n.depth),/cancel/.test(i)&&!h?Dt.makeSpan(["mord","cancel-lap"],[a],e):Dt.makeSpan(["mord"],[a],e)},dr=function(t,e){var r=0,a=new ve.MathNode(t.label.indexOf("colorbox")>-1?"mpadded":"menclose",[Me(t.body,e)]);switch(t.label){case"\\cancel":a.setAttribute("notation","updiagonalstrike");break;case"\\bcancel":a.setAttribute("notation","downdiagonalstrike");break;case"\\sout":a.setAttribute("notation","horizontalstrike");break;case"\\fbox":a.setAttribute("notation","box");break;case"\\fcolorbox":case"\\colorbox":if(r=e.fontMetrics().fboxsep*e.fontMetrics().ptPerEm,a.setAttribute("width","+"+2*r+"pt"),a.setAttribute("height","+"+2*r+"pt"),a.setAttribute("lspace",r+"pt"),a.setAttribute("voffset",r+"pt"),"\\fcolorbox"===t.label){var n=Math.max(e.fontMetrics().fboxrule,e.minRuleThickness);a.setAttribute("style","border: "+n+"em solid "+String(t.borderColor))}break;case"\\xcancel":a.setAttribute("notation","updiagonalstrike downdiagonalstrike")}return t.backgroundColor&&a.setAttribute("mathbackground",t.backgroundColor),a};Qt({type:"enclose",names:["\\colorbox"],props:{numArgs:2,allowedInText:!0,greediness:3,argTypes:["color","text"]},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=Ft(e[0],"color-token").color,o=e[1];return{type:"enclose",mode:a.mode,label:n,backgroundColor:i,body:o}},htmlBuilder:pr,mathmlBuilder:dr}),Qt({type:"enclose",names:["\\fcolorbox"],props:{numArgs:3,allowedInText:!0,greediness:3,argTypes:["color","color","text"]},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=Ft(e[0],"color-token").color,o=Ft(e[1],"color-token").color,s=e[2];return{type:"enclose",mode:a.mode,label:n,backgroundColor:o,borderColor:i,body:s}},htmlBuilder:pr,mathmlBuilder:dr}),Qt({type:"enclose",names:["\\fbox"],props:{numArgs:1,argTypes:["hbox"],allowedInText:!0},handler:function(t,e){return{type:"enclose",mode:t.parser.mode,label:"\\fbox",body:e[0]}}}),Qt({type:"enclose",names:["\\cancel","\\bcancel","\\xcancel","\\sout"],props:{numArgs:1},handler:function(t,e,r){var a=t.parser,n=t.funcName,i=e[0];return{type:"enclose",mode:a.mode,label:n,body:i}},htmlBuilder:pr,mathmlBuilder:dr});var fr={};function gr(t){for(var e=t.type,r=t.names,a=t.props,n=t.handler,i=t.htmlBuilder,o=t.mathmlBuilder,s={type:e,numArgs:a.numArgs||0,greediness:1,allowedInText:!1,numOptionalArgs:0,handler:n},h=0;h<r.length;++h)fr[r[h]]=s;i&&(Kt[e]=i),o&&(Jt[e]=o)}function xr(t){var e=[];t.consumeSpaces();for(var r=t.fetch().text;"\\hline"===r||"\\hdashline"===r;)t.consume(),e.push("\\hdashline"===r),t.consumeSpaces(),r=t.fetch().text;return e}function vr(t,e,r){var a=e.hskipBeforeAndAfter,n=e.addJot,i=e.cols,s=e.arraystretch,h=e.colSeparationType;if(t.gullet.beginGroup(),t.gullet.macros.set("\\\\","\\cr"),!s){var l=t.gullet.expandMacroAsText("\\arraystretch");if(null==l)s=1;else if(!(s=parseFloat(l))||s<0)throw new o("Invalid \\arraystretch: "+l)}t.gullet.beginGroup();var m=[],c=[m],u=[],p=[];for(p.push(xr(t));;){var d=t.parseExpression(!1,"\\cr");t.gullet.endGroup(),t.gullet.beginGroup(),d={type:"ordgroup",mode:t.mode,body:d},r&&(d={type:"styling",mode:t.mode,style:r,body:[d]}),m.push(d);var f=t.fetch().text;if("&"===f)t.consume();else{if("\\end"===f){1===m.length&&"styling"===d.type&&0===d.body[0].body.length&&c.pop(),p.length<c.length+1&&p.push([]);break}if("\\cr"!==f)throw new o("Expected & or \\\\ or \\cr or \\end",t.nextToken);var g=Ft(t.parseFunction(),"cr");u.push(g.size),p.push(xr(t)),m=[],c.push(m)}}return t.gullet.endGroup(),t.gullet.endGroup(),{type:"array",mode:t.mode,addJot:n,arraystretch:s,body:c,cols:i,rowGaps:u,hskipBeforeAndAfter:a,hLinesBeforeRow:p,colSeparationType:h}}function br(t){return"d"===t.substr(0,1)?"display":"text"}var yr=function(t,e){var r,a,n=t.body.length,i=t.hLinesBeforeRow,s=0,h=new Array(n),l=[],m=Math.max(e.fontMetrics().arrayRuleWidth,e.minRuleThickness),u=1/e.fontMetrics().ptPerEm,p=5*u;t.colSeparationType&&"small"===t.colSeparationType&&(p=e.havingStyle(w.SCRIPT).sizeMultiplier/e.sizeMultiplier*.2778);var d=12*u,f=3*u,g=t.arraystretch*d,x=.7*g,v=.3*g,b=0;function y(t){for(var e=0;e<t.length;++e)e>0&&(b+=.25),l.push({pos:b,isDashed:t[e]})}for(y(i[0]),r=0;r<t.body.length;++r){var k=t.body[r],S=x,M=v;s<k.length&&(s=k.length);var z=new Array(k.length);for(a=0;a<k.length;++a){var A=ue(k[a],e);M<A.depth&&(M=A.depth),S<A.height&&(S=A.height),z[a]=A}var T=t.rowGaps[r],B=0;T&&(B=Tt(T,e))>0&&(M<(B+=v)&&(M=B),B=0),t.addJot&&(M+=f),z.height=S,z.depth=M,b+=S,z.pos=b,b+=M+B,h[r]=z,y(i[r+1])}var C,q,N=b/2+e.fontMetrics().axisHeight,I=t.cols||[],R=[];for(a=0,q=0;a<s||q<I.length;++a,++q){for(var O=I[q]||{},E=!0;"separator"===O.type;){if(E||((C=Dt.makeSpan(["arraycolsep"],[])).style.width=e.fontMetrics().doubleRuleSep+"em",R.push(C)),"|"!==O.separator&&":"!==O.separator)throw new o("Invalid separator type: "+O.separator);var L="|"===O.separator?"solid":"dashed",H=Dt.makeSpan(["vertical-separator"],[],e);H.style.height=b+"em",H.style.borderRightWidth=m+"em",H.style.borderRightStyle=L,H.style.margin="0 -"+m/2+"em",H.style.verticalAlign=-(b-N)+"em",R.push(H),O=I[++q]||{},E=!1}if(!(a>=s)){var P=void 0;(a>0||t.hskipBeforeAndAfter)&&0!==(P=c.deflt(O.pregap,p))&&((C=Dt.makeSpan(["arraycolsep"],[])).style.width=P+"em",R.push(C));var D=[];for(r=0;r<n;++r){var F=h[r],V=F[a];if(V){var U=F.pos-N;V.depth=F.depth,V.height=F.height,D.push({type:"elem",elem:V,shift:U})}}D=Dt.makeVList({positionType:"individualShift",children:D},e),D=Dt.makeSpan(["col-align-"+(O.align||"c")],[D]),R.push(D),(a<s-1||t.hskipBeforeAndAfter)&&0!==(P=c.deflt(O.postgap,p))&&((C=Dt.makeSpan(["arraycolsep"],[])).style.width=P+"em",R.push(C))}}if(h=Dt.makeSpan(["mtable"],R),l.length>0){for(var G=Dt.makeLineSpan("hline",e,m),Y=Dt.makeLineSpan("hdashline",e,m),W=[{type:"elem",elem:h,shift:0}];l.length>0;){var X=l.pop(),_=X.pos-N;X.isDashed?W.push({type:"elem",elem:Y,shift:_}):W.push({type:"elem",elem:G,shift:_})}h=Dt.makeVList({positionType:"individualShift",children:W},e)}return Dt.makeSpan(["mord"],[h],e)},wr={c:"center ",l:"left ",r:"right "},kr=function(t,e){var r=new ve.MathNode("mtable",t.body.map(function(t){return new ve.MathNode("mtr",t.map(function(t){return new ve.MathNode("mtd",[Me(t,e)])}))})),a=.5===t.arraystretch?.1:.16+t.arraystretch-1+(t.addJot?.09:0);r.setAttribute("rowspacing",a+"em");var n="",i="";if(t.cols){var o=t.cols,s="",h=!1,l=0,m=o.length;"separator"===o[0].type&&(n+="top ",l=1),"separator"===o[o.length-1].type&&(n+="bottom ",m-=1);for(var c=l;c<m;c++)"align"===o[c].type?(i+=wr[o[c].align],h&&(s+="none "),h=!0):"separator"===o[c].type&&h&&(s+="|"===o[c].separator?"solid ":"dashed ",h=!1);r.setAttribute("columnalign",i.trim()),/[sd]/.test(s)&&r.setAttribute("columnlines",s.trim())}if("align"===t.colSeparationType){for(var u=t.cols||[],p="",d=1;d<u.length;d++)p+=d%2?"0em ":"1em ";r.setAttribute("columnspacing",p.trim())}else"alignat"===t.colSeparationType?r.setAttribute("columnspacing","0em"):"small"===t.colSeparationType?r.setAttribute("columnspacing","0.2778em"):r.setAttribute("columnspacing","1em");var f="",g=t.hLinesBeforeRow;n+=g[0].length>0?"left ":"",n+=g[g.length-1].length>0?"right ":"";for(var x=1;x<g.length-1;x++)f+=0===g[x].length?"none ":g[x][0]?"dashed ":"solid ";return/[sd]/.test(f)&&r.setAttribute("rowlines",f.trim()),""!==n&&(r=new ve.MathNode("menclose",[r])).setAttribute("notation",n.trim()),t.arraystretch&&t.arraystretch<1&&(r=new ve.MathNode("mstyle",[r])).setAttribute("scriptlevel","1"),r},Sr=function(t,e){var r,a=[],n=vr(t.parser,{cols:a,addJot:!0},"display"),i=0,s={type:"ordgroup",mode:t.mode,body:[]},h=Vt(e[0],"ordgroup");if(h){for(var l="",m=0;m<h.body.length;m++){l+=Ft(h.body[m],"textord").text}r=Number(l),i=2*r}var c=!i;n.body.forEach(function(t){for(var e=1;e<t.length;e+=2){var a=Ft(t[e],"styling");Ft(a.body[0],"ordgroup").body.unshift(s)}if(c)i<t.length&&(i=t.length);else{var n=t.length/2;if(r<n)throw new o("Too many math in a row: expected "+r+", but got "+n,t[0])}});for(var u=0;u<i;++u){var p="r",d=0;u%2==1?p="l":u>0&&c&&(d=1),a[u]={type:"align",align:p,pregap:d,postgap:0}}return n.colSeparationType=c?"align":"alignat",n};gr({type:"array",names:["array","darray"],props:{numArgs:1},handler:function(t,e){var r={cols:(Yt(e[0])?[e[0]]:Ft(e[0],"ordgroup").body).map(function(t){var e=Gt(t).text;if(-1!=="lcr".indexOf(e))return{type:"align",align:e};if("|"===e)return{type:"separator",separator:"|"};if(":"===e)return{type:"separator",separator:":"};throw new o("Unknown column alignment: "+e,t)}),hskipBeforeAndAfter:!0};return vr(t.parser,r,br(t.envName))},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["matrix","pmatrix","bmatrix","Bmatrix","vmatrix","Vmatrix"],props:{numArgs:0},handler:function(t){var e={matrix:null,pmatrix:["(",")"],bmatrix:["[","]"],Bmatrix:["\\{","\\}"],vmatrix:["|","|"],Vmatrix:["\\Vert","\\Vert"]}[t.envName],r=vr(t.parser,{hskipBeforeAndAfter:!1},br(t.envName));return e?{type:"leftright",mode:t.mode,body:[r],left:e[0],right:e[1],rightColor:void 0}:r},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["smallmatrix"],props:{numArgs:0},handler:function(t){var e=vr(t.parser,{arraystretch:.5},"script");return e.colSeparationType="small",e},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["subarray"],props:{numArgs:1},handler:function(t,e){var r=(Yt(e[0])?[e[0]]:Ft(e[0],"ordgroup").body).map(function(t){var e=Gt(t).text;if(-1!=="lc".indexOf(e))return{type:"align",align:e};throw new o("Unknown column alignment: "+e,t)});if(r.length>1)throw new o("{subarray} can contain only one column");var a={cols:r,hskipBeforeAndAfter:!1,arraystretch:.5};if((a=vr(t.parser,a,"script")).body[0].length>1)throw new o("{subarray} can contain only one column");return a},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["cases","dcases"],props:{numArgs:0},handler:function(t){var e=vr(t.parser,{arraystretch:1.2,cols:[{type:"align",align:"l",pregap:0,postgap:1},{type:"align",align:"l",pregap:0,postgap:0}]},br(t.envName));return{type:"leftright",mode:t.mode,body:[e],left:"\\{",right:".",rightColor:void 0}},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["aligned"],props:{numArgs:0},handler:Sr,htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["gathered"],props:{numArgs:0},handler:function(t){return vr(t.parser,{cols:[{type:"align",align:"c"}],addJot:!0},"display")},htmlBuilder:yr,mathmlBuilder:kr}),gr({type:"array",names:["alignedat"],props:{numArgs:1},handler:Sr,htmlBuilder:yr,mathmlBuilder:kr}),Qt({type:"text",names:["\\hline","\\hdashline"],props:{numArgs:0,allowedInText:!0,allowedInMath:!0},handler:function(t,e){throw new o(t.funcName+" valid only within array environment")}});var Mr=fr;Qt({type:"environment",names:["\\begin","\\end"],props:{numArgs:1,argTypes:["text"]},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];if("ordgroup"!==n.type)throw new o("Invalid environment name",n);for(var i="",s=0;s<n.body.length;++s)i+=Ft(n.body[s],"textord").text;if("\\begin"===a){if(!Mr.hasOwnProperty(i))throw new o("No such environment: "+i,n);var h=Mr[i],l=r.parseArguments("\\begin{"+i+"}",h),m=l.args,c=l.optArgs,u={mode:r.mode,envName:i,parser:r},p=h.handler(u,m,c);r.expect("\\end",!1);var d=r.nextToken,f=Ft(r.parseFunction(),"environment");if(f.name!==i)throw new o("Mismatch: \\begin{"+i+"} matched by \\end{"+f.name+"}",d);return p}return{type:"environment",mode:r.mode,name:i,nameGroup:n}}});var zr=Dt.makeSpan;function Ar(t,e){var r=se(t.body,e,!0);return zr([t.mclass],r,e)}function Tr(t,e){var r,a=ke(t.body,e);return"minner"===t.mclass?ve.newDocumentFragment(a):("mord"===t.mclass?t.isCharacterBox?(r=a[0]).type="mi":r=new ve.MathNode("mi",a):(t.isCharacterBox?(r=a[0]).type="mo":r=new ve.MathNode("mo",a),"mbin"===t.mclass?(r.attributes.lspace="0.22em",r.attributes.rspace="0.22em"):"mpunct"===t.mclass?(r.attributes.lspace="0em",r.attributes.rspace="0.17em"):"mopen"!==t.mclass&&"mclose"!==t.mclass||(r.attributes.lspace="0em",r.attributes.rspace="0em")),r)}Qt({type:"mclass",names:["\\mathord","\\mathbin","\\mathrel","\\mathopen","\\mathclose","\\mathpunct","\\mathinner"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"mclass",mode:r.mode,mclass:"m"+a.substr(5),body:ee(n),isCharacterBox:c.isCharacterBox(n)}},htmlBuilder:Ar,mathmlBuilder:Tr});var Br=function(t){var e="ordgroup"===t.type&&t.body.length?t.body[0]:t;return"atom"!==e.type||"bin"!==e.family&&"rel"!==e.family?"mord":"m"+e.family};Qt({type:"mclass",names:["\\@binrel"],props:{numArgs:2},handler:function(t,e){return{type:"mclass",mode:t.parser.mode,mclass:Br(e[0]),body:[e[1]],isCharacterBox:c.isCharacterBox(e[1])}}}),Qt({type:"mclass",names:["\\stackrel","\\overset","\\underset"],props:{numArgs:2},handler:function(t,e){var r,a=t.parser,n=t.funcName,i=e[1],o=e[0];r="\\stackrel"!==n?Br(i):"mrel";var s={type:"op",mode:i.mode,limits:!0,alwaysHandleSupSub:!0,parentIsSupSub:!1,symbol:!1,suppressBaseShift:"\\stackrel"!==n,body:ee(i)},h={type:"supsub",mode:o.mode,base:s,sup:"\\underset"===n?null:o,sub:"\\underset"===n?o:null};return{type:"mclass",mode:a.mode,mclass:r,body:[h],isCharacterBox:c.isCharacterBox(h)}},htmlBuilder:Ar,mathmlBuilder:Tr});var Cr=function(t,e){var r=t.font,a=e.withFont(r);return ue(t.body,a)},qr=function(t,e){var r=t.font,a=e.withFont(r);return Me(t.body,a)},Nr={"\\Bbb":"\\mathbb","\\bold":"\\mathbf","\\frak":"\\mathfrak","\\bm":"\\boldsymbol"};Qt({type:"font",names:["\\mathrm","\\mathit","\\mathbf","\\mathnormal","\\mathbb","\\mathcal","\\mathfrak","\\mathscr","\\mathsf","\\mathtt","\\Bbb","\\bold","\\frak"],props:{numArgs:1,greediness:2},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0],i=a;return i in Nr&&(i=Nr[i]),{type:"font",mode:r.mode,font:i.slice(1),body:n}},htmlBuilder:Cr,mathmlBuilder:qr}),Qt({type:"mclass",names:["\\boldsymbol","\\bm"],props:{numArgs:1,greediness:2},handler:function(t,e){var r=t.parser,a=e[0],n=c.isCharacterBox(a);return{type:"mclass",mode:r.mode,mclass:Br(a),body:[{type:"font",mode:r.mode,font:"boldsymbol",body:a}],isCharacterBox:n}}}),Qt({type:"font",names:["\\rm","\\sf","\\tt","\\bf","\\it"],props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=t.breakOnTokenText,i=r.mode,o=r.parseExpression(!0,n);return{type:"font",mode:i,font:"math"+a.slice(1),body:{type:"ordgroup",mode:r.mode,body:o}}},htmlBuilder:Cr,mathmlBuilder:qr});var Ir=function(t,e){var r=e;return"display"===t?r=r.id>=w.SCRIPT.id?r.text():w.DISPLAY:"text"===t&&r.size===w.DISPLAY.size?r=w.TEXT:"script"===t?r=w.SCRIPT:"scriptscript"===t&&(r=w.SCRIPTSCRIPT),r},Rr=function(t,e){var r,a=Ir(t.size,e.style),n=a.fracNum(),i=a.fracDen();r=e.havingStyle(n);var o=ue(t.numer,r,e);if(t.continued){var s=8.5/e.fontMetrics().ptPerEm,h=3.5/e.fontMetrics().ptPerEm;o.height=o.height<s?s:o.height,o.depth=o.depth<h?h:o.depth}r=e.havingStyle(i);var l,m,c,u,p,d,f,g,x,v,b=ue(t.denom,r,e);if(t.hasBarLine?(t.barSize?(m=Tt(t.barSize,e),l=Dt.makeLineSpan("frac-line",e,m)):l=Dt.makeLineSpan("frac-line",e),m=l.height,c=l.height):(l=null,m=0,c=e.fontMetrics().defaultRuleThickness),a.size===w.DISPLAY.size||"display"===t.size?(u=e.fontMetrics().num1,p=m>0?3*c:7*c,d=e.fontMetrics().denom1):(m>0?(u=e.fontMetrics().num2,p=c):(u=e.fontMetrics().num3,p=3*c),d=e.fontMetrics().denom2),l){var y=e.fontMetrics().axisHeight;u-o.depth-(y+.5*m)<p&&(u+=p-(u-o.depth-(y+.5*m))),y-.5*m-(b.height-d)<p&&(d+=p-(y-.5*m-(b.height-d)));var k=-(y-.5*m);f=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:b,shift:d},{type:"elem",elem:l,shift:k},{type:"elem",elem:o,shift:-u}]},e)}else{var S=u-o.depth-(b.height-d);S<p&&(u+=.5*(p-S),d+=.5*(p-S)),f=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:b,shift:d},{type:"elem",elem:o,shift:-u}]},e)}return r=e.havingStyle(a),f.height*=r.sizeMultiplier/e.sizeMultiplier,f.depth*=r.sizeMultiplier/e.sizeMultiplier,g=a.size===w.DISPLAY.size?e.fontMetrics().delim1:e.fontMetrics().delim2,x=null==t.leftDelim?ce(e,["mopen"]):sr(t.leftDelim,g,!0,e.havingStyle(a),t.mode,["mopen"]),v=t.continued?Dt.makeSpan([]):null==t.rightDelim?ce(e,["mclose"]):sr(t.rightDelim,g,!0,e.havingStyle(a),t.mode,["mclose"]),Dt.makeSpan(["mord"].concat(r.sizingClasses(e)),[x,Dt.makeSpan(["mfrac"],[f]),v],e)},Or=function(t,e){var r=new ve.MathNode("mfrac",[Me(t.numer,e),Me(t.denom,e)]);if(t.hasBarLine){if(t.barSize){var a=Tt(t.barSize,e);r.setAttribute("linethickness",a+"em")}}else r.setAttribute("linethickness","0px");var n=Ir(t.size,e.style);if(n.size!==e.style.size){r=new ve.MathNode("mstyle",[r]);var i=n.size===w.DISPLAY.size?"true":"false";r.setAttribute("displaystyle",i),r.setAttribute("scriptlevel","0")}if(null!=t.leftDelim||null!=t.rightDelim){var o=[];if(null!=t.leftDelim){var s=new ve.MathNode("mo",[new ve.TextNode(t.leftDelim.replace("\\",""))]);s.setAttribute("fence","true"),o.push(s)}if(o.push(r),null!=t.rightDelim){var h=new ve.MathNode("mo",[new ve.TextNode(t.rightDelim.replace("\\",""))]);h.setAttribute("fence","true"),o.push(h)}return ye(o)}return r};Qt({type:"genfrac",names:["\\cfrac","\\dfrac","\\frac","\\tfrac","\\dbinom","\\binom","\\tbinom","\\\\atopfrac","\\\\bracefrac","\\\\brackfrac"],props:{numArgs:2,greediness:2},handler:function(t,e){var r,a=t.parser,n=t.funcName,i=e[0],o=e[1],s=null,h=null,l="auto";switch(n){case"\\cfrac":case"\\dfrac":case"\\frac":case"\\tfrac":r=!0;break;case"\\\\atopfrac":r=!1;break;case"\\dbinom":case"\\binom":case"\\tbinom":r=!1,s="(",h=")";break;case"\\\\bracefrac":r=!1,s="\\{",h="\\}";break;case"\\\\brackfrac":r=!1,s="[",h="]";break;default:throw new Error("Unrecognized genfrac command")}switch(n){case"\\cfrac":case"\\dfrac":case"\\dbinom":l="display";break;case"\\tfrac":case"\\tbinom":l="text"}return{type:"genfrac",mode:a.mode,continued:"\\cfrac"===n,numer:i,denom:o,hasBarLine:r,leftDelim:s,rightDelim:h,size:l,barSize:null}},htmlBuilder:Rr,mathmlBuilder:Or}),Qt({type:"infix",names:["\\over","\\choose","\\atop","\\brace","\\brack"],props:{numArgs:0,infix:!0},handler:function(t){var e,r=t.parser,a=t.funcName,n=t.token;switch(a){case"\\over":e="\\frac";break;case"\\choose":e="\\binom";break;case"\\atop":e="\\\\atopfrac";break;case"\\brace":e="\\\\bracefrac";break;case"\\brack":e="\\\\brackfrac";break;default:throw new Error("Unrecognized infix genfrac command")}return{type:"infix",mode:r.mode,replaceWith:e,token:n}}});var Er=["display","text","script","scriptscript"],Lr=function(t){var e=null;return t.length>0&&(e="."===(e=t)?null:e),e};Qt({type:"genfrac",names:["\\genfrac"],props:{numArgs:6,greediness:6,argTypes:["math","math","size","text","math","math"]},handler:function(t,e){var r=t.parser,a=e[4],n=e[5],i=Vt(e[0],"atom");i&&(i=Ut(e[0],"open"));var o=i?Lr(i.text):null,s=Vt(e[1],"atom");s&&(s=Ut(e[1],"close"));var h,l=s?Lr(s.text):null,m=Ft(e[2],"size"),c=null;h=!!m.isBlank||(c=m.value).number>0;var u="auto",p=Vt(e[3],"ordgroup");if(p){if(p.body.length>0){var d=Ft(p.body[0],"textord");u=Er[Number(d.text)]}}else p=Ft(e[3],"textord"),u=Er[Number(p.text)];return{type:"genfrac",mode:r.mode,numer:a,denom:n,continued:!1,hasBarLine:h,barSize:c,leftDelim:o,rightDelim:l,size:u}},htmlBuilder:Rr,mathmlBuilder:Or}),Qt({type:"infix",names:["\\above"],props:{numArgs:1,argTypes:["size"],infix:!0},handler:function(t,e){var r=t.parser,a=(t.funcName,t.token);return{type:"infix",mode:r.mode,replaceWith:"\\\\abovefrac",size:Ft(e[0],"size").value,token:a}}}),Qt({type:"genfrac",names:["\\\\abovefrac"],props:{numArgs:3,argTypes:["math","size","math"]},handler:function(t,e){var r=t.parser,a=(t.funcName,e[0]),n=function(t){if(!t)throw new Error("Expected non-null, but got "+String(t));return t}(Ft(e[1],"infix").size),i=e[2],o=n.number>0;return{type:"genfrac",mode:r.mode,numer:a,denom:i,continued:!1,hasBarLine:o,barSize:n,leftDelim:null,rightDelim:null,size:"auto"}},htmlBuilder:Rr,mathmlBuilder:Or});var Hr=function(t,e){var r,a,n=e.style,i=Vt(t,"supsub");i?(r=i.sup?ue(i.sup,e.havingStyle(n.sup()),e):ue(i.sub,e.havingStyle(n.sub()),e),a=Ft(i.base,"horizBrace")):a=Ft(t,"horizBrace");var o,s=ue(a.base,e.havingBaseStyle(w.DISPLAY)),h=Oe(a,e);if(a.isOver?(o=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:s},{type:"kern",size:.1},{type:"elem",elem:h}]},e)).children[0].children[0].children[1].classes.push("svg-align"):(o=Dt.makeVList({positionType:"bottom",positionData:s.depth+.1+h.height,children:[{type:"elem",elem:h},{type:"kern",size:.1},{type:"elem",elem:s}]},e)).children[0].children[0].children[0].classes.push("svg-align"),r){var l=Dt.makeSpan(["mord",a.isOver?"mover":"munder"],[o],e);o=a.isOver?Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:l},{type:"kern",size:.2},{type:"elem",elem:r}]},e):Dt.makeVList({positionType:"bottom",positionData:l.depth+.2+r.height+r.depth,children:[{type:"elem",elem:r},{type:"kern",size:.2},{type:"elem",elem:l}]},e)}return Dt.makeSpan(["mord",a.isOver?"mover":"munder"],[o],e)};Qt({type:"horizBrace",names:["\\overbrace","\\underbrace"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName;return{type:"horizBrace",mode:r.mode,label:a,isOver:/^\\over/.test(a),base:e[0]}},htmlBuilder:Hr,mathmlBuilder:function(t,e){var r=Re(t.label);return new ve.MathNode(t.isOver?"mover":"munder",[Me(t.base,e),r])}}),Qt({type:"href",names:["\\href"],props:{numArgs:2,argTypes:["url","original"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[1],n=Ft(e[0],"url").url;return r.settings.isTrusted({command:"\\href",url:n})?{type:"href",mode:r.mode,href:n,body:ee(a)}:r.formatUnsupportedCmd("\\href")},htmlBuilder:function(t,e){var r=se(t.body,e,!1);return Dt.makeAnchor(t.href,[],r,e)},mathmlBuilder:function(t,e){var r=Se(t.body,e);return r instanceof ge||(r=new ge("mrow",[r])),r.setAttribute("href",t.href),r}}),Qt({type:"href",names:["\\url"],props:{numArgs:1,argTypes:["url"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=Ft(e[0],"url").url;if(!r.settings.isTrusted({command:"\\url",url:a}))return r.formatUnsupportedCmd("\\url");for(var n=[],i=0;i<a.length;i++){var o=a[i];"~"===o&&(o="\\textasciitilde"),n.push({type:"textord",mode:"text",text:o})}var s={type:"text",mode:r.mode,font:"\\texttt",body:n};return{type:"href",mode:r.mode,href:a,body:ee(s)}}}),Qt({type:"htmlmathml",names:["\\html@mathml"],props:{numArgs:2,allowedInText:!0},handler:function(t,e){return{type:"htmlmathml",mode:t.parser.mode,html:ee(e[0]),mathml:ee(e[1])}},htmlBuilder:function(t,e){var r=se(t.html,e,!1);return Dt.makeFragment(r)},mathmlBuilder:function(t,e){return Se(t.mathml,e)}});var Pr=function(t){if(/^[-+]? *(\d+(\.\d*)?|\.\d+)$/.test(t))return{number:+t,unit:"bp"};var e=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(t);if(!e)throw new o("Invalid size: '"+t+"' in \\includegraphics");var r={number:+(e[1]+e[2]),unit:e[3]};if(!At(r))throw new o("Invalid unit: '"+r.unit+"' in \\includegraphics.");return r};Qt({type:"includegraphics",names:["\\includegraphics"],props:{numArgs:1,numOptionalArgs:1,argTypes:["raw","url"],allowedInText:!1},handler:function(t,e,r){var a=t.parser,n={number:0,unit:"em"},i={number:.9,unit:"em"},s={number:0,unit:"em"},h="";if(r[0])for(var l=Ft(r[0],"raw").string.split(","),m=0;m<l.length;m++){var c=l[m].split("=");if(2===c.length){var u=c[1].trim();switch(c[0].trim()){case"alt":h=u;break;case"width":n=Pr(u);break;case"height":i=Pr(u);break;case"totalheight":s=Pr(u);break;default:throw new o("Invalid key: '"+c[0]+"' in \\includegraphics.")}}}var p=Ft(e[0],"url").url;return""===h&&(h=(h=(h=p).replace(/^.*[\\\/]/,"")).substring(0,h.lastIndexOf("."))),a.settings.isTrusted({command:"\\includegraphics",url:p})?{type:"includegraphics",mode:a.mode,alt:h,width:n,height:i,totalheight:s,src:p}:a.formatUnsupportedCmd("\\includegraphics")},htmlBuilder:function(t,e){var r=Tt(t.height,e),a=0;t.totalheight.number>0&&(a=Tt(t.totalheight,e)-r,a=Number(a.toFixed(2)));var n=0;t.width.number>0&&(n=Tt(t.width,e));var i={height:r+a+"em"};n>0&&(i.width=n+"em"),a>0&&(i.verticalAlign=-a+"em");var o=new R(t.src,t.alt,i);return o.height=r,o.depth=a,o},mathmlBuilder:function(t,e){var r=new ve.MathNode("mglyph",[]);r.setAttribute("alt",t.alt);var a=Tt(t.height,e),n=0;if(t.totalheight.number>0&&(n=(n=Tt(t.totalheight,e)-a).toFixed(2),r.setAttribute("valign","-"+n+"em")),r.setAttribute("height",a+n+"em"),t.width.number>0){var i=Tt(t.width,e);r.setAttribute("width",i+"em")}return r.setAttribute("src",t.src),r}}),Qt({type:"kern",names:["\\kern","\\mkern","\\hskip","\\mskip"],props:{numArgs:1,argTypes:["size"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=Ft(e[0],"size");if(r.settings.strict){var i="m"===a[1],o="mu"===n.value.unit;i?(o||r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" supports only mu units, not "+n.value.unit+" units"),"math"!==r.mode&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" works only in math mode")):o&&r.settings.reportNonstrict("mathVsTextUnits","LaTeX's "+a+" doesn't support mu units")}return{type:"kern",mode:r.mode,dimension:n.value}},htmlBuilder:function(t,e){return Dt.makeGlue(t.dimension,e)},mathmlBuilder:function(t,e){var r=Tt(t.dimension,e);return new ve.SpaceNode(r)}}),Qt({type:"lap",names:["\\mathllap","\\mathrlap","\\mathclap"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"lap",mode:r.mode,alignment:a.slice(5),body:n}},htmlBuilder:function(t,e){var r;"clap"===t.alignment?(r=Dt.makeSpan([],[ue(t.body,e)]),r=Dt.makeSpan(["inner"],[r],e)):r=Dt.makeSpan(["inner"],[ue(t.body,e)]);var a=Dt.makeSpan(["fix"],[]),n=Dt.makeSpan([t.alignment],[r,a],e),i=Dt.makeSpan(["strut"]);return i.style.height=n.height+n.depth+"em",i.style.verticalAlign=-n.depth+"em",n.children.unshift(i),n=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:n}]},e),Dt.makeSpan(["mord"],[n],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mpadded",[Me(t.body,e)]);if("rlap"!==t.alignment){var a="llap"===t.alignment?"-1":"-0.5";r.setAttribute("lspace",a+"width")}return r.setAttribute("width","0px"),r}}),Qt({type:"styling",names:["\\(","$"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler:function(t,e){var r=t.funcName,a=t.parser,n=a.mode;a.switchMode("math");var i="\\("===r?"\\)":"$",o=a.parseExpression(!1,i);return a.expect(i),a.switchMode(n),{type:"styling",mode:a.mode,style:"text",body:o}}}),Qt({type:"text",names:["\\)","\\]"],props:{numArgs:0,allowedInText:!0,allowedInMath:!1},handler:function(t,e){throw new o("Mismatched "+t.funcName)}});var Dr=function(t,e){switch(e.style.size){case w.DISPLAY.size:return t.display;case w.TEXT.size:return t.text;case w.SCRIPT.size:return t.script;case w.SCRIPTSCRIPT.size:return t.scriptscript;default:return t.text}};Qt({type:"mathchoice",names:["\\mathchoice"],props:{numArgs:4},handler:function(t,e){return{type:"mathchoice",mode:t.parser.mode,display:ee(e[0]),text:ee(e[1]),script:ee(e[2]),scriptscript:ee(e[3])}},htmlBuilder:function(t,e){var r=Dr(t,e),a=se(r,e,!1);return Dt.makeFragment(a)},mathmlBuilder:function(t,e){var r=Dr(t,e);return Se(r,e)}});var Fr=function(t,e,r,a,n,i,o){var s,h,l;if(t=Dt.makeSpan([],[t]),e){var m=ue(e,a.havingStyle(n.sup()),a);h={elem:m,kern:Math.max(a.fontMetrics().bigOpSpacing1,a.fontMetrics().bigOpSpacing3-m.depth)}}if(r){var c=ue(r,a.havingStyle(n.sub()),a);s={elem:c,kern:Math.max(a.fontMetrics().bigOpSpacing2,a.fontMetrics().bigOpSpacing4-c.height)}}if(h&&s){var u=a.fontMetrics().bigOpSpacing5+s.elem.height+s.elem.depth+s.kern+t.depth+o;l=Dt.makeVList({positionType:"bottom",positionData:u,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:s.elem,marginLeft:-i+"em"},{type:"kern",size:s.kern},{type:"elem",elem:t},{type:"kern",size:h.kern},{type:"elem",elem:h.elem,marginLeft:i+"em"},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}else if(s){var p=t.height-o;l=Dt.makeVList({positionType:"top",positionData:p,children:[{type:"kern",size:a.fontMetrics().bigOpSpacing5},{type:"elem",elem:s.elem,marginLeft:-i+"em"},{type:"kern",size:s.kern},{type:"elem",elem:t}]},a)}else{if(!h)return t;var d=t.depth+o;l=Dt.makeVList({positionType:"bottom",positionData:d,children:[{type:"elem",elem:t},{type:"kern",size:h.kern},{type:"elem",elem:h.elem,marginLeft:i+"em"},{type:"kern",size:a.fontMetrics().bigOpSpacing5}]},a)}return Dt.makeSpan(["mop","op-limits"],[l],a)},Vr=["\\smallint"],Ur=function(t,e){var r,a,n,i=!1,o=Vt(t,"supsub");o?(r=o.sup,a=o.sub,n=Ft(o.base,"op"),i=!0):n=Ft(t,"op");var s,h=e.style,l=!1;if(h.size===w.DISPLAY.size&&n.symbol&&!c.contains(Vr,n.name)&&(l=!0),n.symbol){var m=l?"Size2-Regular":"Size1-Regular",u="";if("\\oiint"!==n.name&&"\\oiiint"!==n.name||(u=n.name.substr(1),n.name="oiint"===u?"\\iint":"\\iiint"),s=Dt.makeSymbol(n.name,m,"math",e,["mop","op-symbol",l?"large-op":"small-op"]),u.length>0){var p=s.italic,d=Dt.staticSvg(u+"Size"+(l?"2":"1"),e);s=Dt.makeVList({positionType:"individualShift",children:[{type:"elem",elem:s,shift:0},{type:"elem",elem:d,shift:l?.08:0}]},e),n.name="\\"+u,s.classes.unshift("mop"),s.italic=p}}else if(n.body){var f=se(n.body,e,!0);1===f.length&&f[0]instanceof E?(s=f[0]).classes[0]="mop":s=Dt.makeSpan(["mop"],Dt.tryCombineChars(f),e)}else{for(var g=[],x=1;x<n.name.length;x++)g.push(Dt.mathsym(n.name[x],n.mode,e));s=Dt.makeSpan(["mop"],g,e)}var v=0,b=0;return(s instanceof E||"\\oiint"===n.name||"\\oiiint"===n.name)&&!n.suppressBaseShift&&(v=(s.height-s.depth)/2-e.fontMetrics().axisHeight,b=s.italic),i?Fr(s,r,a,e,h,b,v):(v&&(s.style.position="relative",s.style.top=v+"em"),s)},Gr=function(t,e){var r;if(t.symbol)r=new ge("mo",[be(t.name,t.mode)]),c.contains(Vr,t.name)&&r.setAttribute("largeop","false");else if(t.body)r=new ge("mo",ke(t.body,e));else{r=new ge("mi",[new xe(t.name.slice(1))]);var a=new ge("mo",[be("\u2061","text")]);r=t.parentIsSupSub?new ge("mo",[r,a]):fe([r,a])}return r},Yr={"\u220f":"\\prod","\u2210":"\\coprod","\u2211":"\\sum","\u22c0":"\\bigwedge","\u22c1":"\\bigvee","\u22c2":"\\bigcap","\u22c3":"\\bigcup","\u2a00":"\\bigodot","\u2a01":"\\bigoplus","\u2a02":"\\bigotimes","\u2a04":"\\biguplus","\u2a06":"\\bigsqcup"};Qt({type:"op",names:["\\coprod","\\bigvee","\\bigwedge","\\biguplus","\\bigcap","\\bigcup","\\intop","\\prod","\\sum","\\bigotimes","\\bigoplus","\\bigodot","\\bigsqcup","\\smallint","\u220f","\u2210","\u2211","\u22c0","\u22c1","\u22c2","\u22c3","\u2a00","\u2a01","\u2a02","\u2a04","\u2a06"],props:{numArgs:0},handler:function(t,e){var r=t.parser,a=t.funcName;return 1===a.length&&(a=Yr[a]),{type:"op",mode:r.mode,limits:!0,parentIsSupSub:!1,symbol:!0,name:a}},htmlBuilder:Ur,mathmlBuilder:Gr}),Qt({type:"op",names:["\\mathop"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=e[0];return{type:"op",mode:r.mode,limits:!1,parentIsSupSub:!1,symbol:!1,body:ee(a)}},htmlBuilder:Ur,mathmlBuilder:Gr});var Wr={"\u222b":"\\int","\u222c":"\\iint","\u222d":"\\iiint","\u222e":"\\oint","\u222f":"\\oiint","\u2230":"\\oiiint"};Qt({type:"op",names:["\\arcsin","\\arccos","\\arctan","\\arctg","\\arcctg","\\arg","\\ch","\\cos","\\cosec","\\cosh","\\cot","\\cotg","\\coth","\\csc","\\ctg","\\cth","\\deg","\\dim","\\exp","\\hom","\\ker","\\lg","\\ln","\\log","\\sec","\\sin","\\sinh","\\sh","\\tan","\\tanh","\\tg","\\th"],props:{numArgs:0},handler:function(t){var e=t.parser,r=t.funcName;return{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:Ur,mathmlBuilder:Gr}),Qt({type:"op",names:["\\det","\\gcd","\\inf","\\lim","\\max","\\min","\\Pr","\\sup"],props:{numArgs:0},handler:function(t){var e=t.parser,r=t.funcName;return{type:"op",mode:e.mode,limits:!0,parentIsSupSub:!1,symbol:!1,name:r}},htmlBuilder:Ur,mathmlBuilder:Gr}),Qt({type:"op",names:["\\int","\\iint","\\iiint","\\oint","\\oiint","\\oiiint","\u222b","\u222c","\u222d","\u222e","\u222f","\u2230"],props:{numArgs:0},handler:function(t){var e=t.parser,r=t.funcName;return 1===r.length&&(r=Wr[r]),{type:"op",mode:e.mode,limits:!1,parentIsSupSub:!1,symbol:!0,name:r}},htmlBuilder:Ur,mathmlBuilder:Gr});var Xr=function(t,e){var r,a,n,i,o=!1,s=Vt(t,"supsub");if(s?(r=s.sup,a=s.sub,n=Ft(s.base,"operatorname"),o=!0):n=Ft(t,"operatorname"),n.body.length>0){for(var h=n.body.map(function(t){var e=t.text;return"string"==typeof e?{type:"textord",mode:t.mode,text:e}:t}),l=se(h,e.withFont("mathrm"),!0),m=0;m<l.length;m++){var c=l[m];c instanceof E&&(c.text=c.text.replace(/\u2212/,"-").replace(/\u2217/,"*"))}i=Dt.makeSpan(["mop"],l,e)}else i=Dt.makeSpan(["mop"],[],e);return o?Fr(i,r,a,e,e.style,0,0):i};function _r(t,e,r){for(var a=se(t,e,!1),n=e.sizeMultiplier/r.sizeMultiplier,i=0;i<a.length;i++){var o=a[i].classes.indexOf("sizing");o<0?Array.prototype.push.apply(a[i].classes,e.sizingClasses(r)):a[i].classes[o+1]==="reset-size"+e.size&&(a[i].classes[o+1]="reset-size"+r.size),a[i].height*=n,a[i].depth*=n}return Dt.makeFragment(a)}Qt({type:"operatorname",names:["\\operatorname","\\operatorname*"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"operatorname",mode:r.mode,body:ee(n),alwaysHandleSupSub:"\\operatorname*"===a,limits:!1,parentIsSupSub:!1}},htmlBuilder:Xr,mathmlBuilder:function(t,e){for(var r=ke(t.body,e.withFont("mathrm")),a=!0,n=0;n<r.length;n++){var i=r[n];if(i instanceof ve.SpaceNode);else if(i instanceof ve.MathNode)switch(i.type){case"mi":case"mn":case"ms":case"mspace":case"mtext":break;case"mo":var o=i.children[0];1===i.children.length&&o instanceof ve.TextNode?o.text=o.text.replace(/\u2212/,"-").replace(/\u2217/,"*"):a=!1;break;default:a=!1}else a=!1}if(a){var s=r.map(function(t){return t.toText()}).join("");r=[new ve.TextNode(s)]}var h=new ve.MathNode("mi",r);h.setAttribute("mathvariant","normal");var l=new ve.MathNode("mo",[be("\u2061","text")]);return t.parentIsSupSub?new ve.MathNode("mo",[h,l]):ve.newDocumentFragment([h,l])}}),te({type:"ordgroup",htmlBuilder:function(t,e){return t.semisimple?Dt.makeFragment(se(t.body,e,!1)):Dt.makeSpan(["mord"],se(t.body,e,!0),e)},mathmlBuilder:function(t,e){return Se(t.body,e,!0)}}),Qt({type:"overline",names:["\\overline"],props:{numArgs:1},handler:function(t,e){var r=t.parser,a=e[0];return{type:"overline",mode:r.mode,body:a}},htmlBuilder:function(t,e){var r=ue(t.body,e.havingCrampedStyle()),a=Dt.makeLineSpan("overline-line",e),n=e.fontMetrics().defaultRuleThickness,i=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r},{type:"kern",size:3*n},{type:"elem",elem:a},{type:"kern",size:n}]},e);return Dt.makeSpan(["mord","overline"],[i],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[new ve.TextNode("\u203e")]);r.setAttribute("stretchy","true");var a=new ve.MathNode("mover",[Me(t.body,e),r]);return a.setAttribute("accent","true"),a}}),Qt({type:"phantom",names:["\\phantom"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[0];return{type:"phantom",mode:r.mode,body:ee(a)}},htmlBuilder:function(t,e){var r=se(t.body,e.withPhantom(),!1);return Dt.makeFragment(r)},mathmlBuilder:function(t,e){var r=ke(t.body,e);return new ve.MathNode("mphantom",r)}}),Qt({type:"hphantom",names:["\\hphantom"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[0];return{type:"hphantom",mode:r.mode,body:a}},htmlBuilder:function(t,e){var r=Dt.makeSpan([],[ue(t.body,e.withPhantom())]);if(r.height=0,r.depth=0,r.children)for(var a=0;a<r.children.length;a++)r.children[a].height=0,r.children[a].depth=0;return r=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r}]},e),Dt.makeSpan(["mord"],[r],e)},mathmlBuilder:function(t,e){var r=ke(ee(t.body),e),a=new ve.MathNode("mphantom",r),n=new ve.MathNode("mpadded",[a]);return n.setAttribute("height","0px"),n.setAttribute("depth","0px"),n}}),Qt({type:"vphantom",names:["\\vphantom"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){var r=t.parser,a=e[0];return{type:"vphantom",mode:r.mode,body:a}},htmlBuilder:function(t,e){var r=Dt.makeSpan(["inner"],[ue(t.body,e.withPhantom())]),a=Dt.makeSpan(["fix"],[]);return Dt.makeSpan(["mord","rlap"],[r,a],e)},mathmlBuilder:function(t,e){var r=ke(ee(t.body),e),a=new ve.MathNode("mphantom",r),n=new ve.MathNode("mpadded",[a]);return n.setAttribute("width","0px"),n}}),Qt({type:"raisebox",names:["\\raisebox"],props:{numArgs:2,argTypes:["size","hbox"],allowedInText:!0},handler:function(t,e){var r=t.parser,a=Ft(e[0],"size").value,n=e[1];return{type:"raisebox",mode:r.mode,dy:a,body:n}},htmlBuilder:function(t,e){var r=ue(t.body,e),a=Tt(t.dy,e);return Dt.makeVList({positionType:"shift",positionData:-a,children:[{type:"elem",elem:r}]},e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mpadded",[Me(t.body,e)]),a=t.dy.number+t.dy.unit;return r.setAttribute("voffset",a),r}}),Qt({type:"rule",names:["\\rule"],props:{numArgs:2,numOptionalArgs:1,argTypes:["size","size","size"]},handler:function(t,e,r){var a=t.parser,n=r[0],i=Ft(e[0],"size"),o=Ft(e[1],"size");return{type:"rule",mode:a.mode,shift:n&&Ft(n,"size").value,width:i.value,height:o.value}},htmlBuilder:function(t,e){var r=Dt.makeSpan(["mord","rule"],[],e),a=Tt(t.width,e),n=Tt(t.height,e),i=t.shift?Tt(t.shift,e):0;return r.style.borderRightWidth=a+"em",r.style.borderTopWidth=n+"em",r.style.bottom=i+"em",r.width=a,r.height=n+i,r.depth=-i,r.maxFontSize=1.125*n*e.sizeMultiplier,r},mathmlBuilder:function(t,e){var r=Tt(t.width,e),a=Tt(t.height,e),n=t.shift?Tt(t.shift,e):0,i=e.color&&e.getColor()||"black",o=new ve.MathNode("mspace");o.setAttribute("mathbackground",i),o.setAttribute("width",r+"em"),o.setAttribute("height",a+"em");var s=new ve.MathNode("mpadded",[o]);return n>=0?s.setAttribute("height","+"+n+"em"):(s.setAttribute("height",n+"em"),s.setAttribute("depth","+"+-n+"em")),s.setAttribute("voffset",n+"em"),s}});var jr=["\\tiny","\\sixptsize","\\scriptsize","\\footnotesize","\\small","\\normalsize","\\large","\\Large","\\LARGE","\\huge","\\Huge"];Qt({type:"sizing",names:jr,props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.breakOnTokenText,a=t.funcName,n=t.parser,i=n.parseExpression(!1,r);return{type:"sizing",mode:n.mode,size:jr.indexOf(a)+1,body:i}},htmlBuilder:function(t,e){var r=e.havingSize(t.size);return _r(t.body,r,e)},mathmlBuilder:function(t,e){var r=e.havingSize(t.size),a=ke(t.body,r),n=new ve.MathNode("mstyle",a);return n.setAttribute("mathsize",r.sizeMultiplier+"em"),n}}),Qt({type:"smash",names:["\\smash"],props:{numArgs:1,numOptionalArgs:1,allowedInText:!0},handler:function(t,e,r){var a=t.parser,n=!1,i=!1,o=r[0]&&Ft(r[0],"ordgroup");if(o)for(var s="",h=0;h<o.body.length;++h){if("t"===(s=o.body[h].text))n=!0;else{if("b"!==s){n=!1,i=!1;break}i=!0}}else n=!0,i=!0;var l=e[0];return{type:"smash",mode:a.mode,body:l,smashHeight:n,smashDepth:i}},htmlBuilder:function(t,e){var r=Dt.makeSpan([],[ue(t.body,e)]);if(!t.smashHeight&&!t.smashDepth)return r;if(t.smashHeight&&(r.height=0,r.children))for(var a=0;a<r.children.length;a++)r.children[a].height=0;if(t.smashDepth&&(r.depth=0,r.children))for(var n=0;n<r.children.length;n++)r.children[n].depth=0;var i=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord"],[i],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mpadded",[Me(t.body,e)]);return t.smashHeight&&r.setAttribute("height","0px"),t.smashDepth&&r.setAttribute("depth","0px"),r}}),Qt({type:"sqrt",names:["\\sqrt"],props:{numArgs:1,numOptionalArgs:1},handler:function(t,e,r){var a=t.parser,n=r[0],i=e[0];return{type:"sqrt",mode:a.mode,body:i,index:n}},htmlBuilder:function(t,e){var r=ue(t.body,e.havingCrampedStyle());0===r.height&&(r.height=e.fontMetrics().xHeight),r=Dt.wrapFragment(r,e);var a=e.fontMetrics().defaultRuleThickness,n=a;e.style.id<w.TEXT.id&&(n=e.fontMetrics().xHeight);var i=a+n/4,o=r.height+r.depth+i+a,s=ir(o,e),h=s.span,l=s.ruleWidth,m=s.advanceWidth,c=h.height-l;c>r.height+r.depth+i&&(i=(i+c-r.height-r.depth)/2);var u=h.height-r.height-i-l;r.style.paddingLeft=m+"em";var p=Dt.makeVList({positionType:"firstBaseline",children:[{type:"elem",elem:r,wrapperClasses:["svg-align"]},{type:"kern",size:-(r.height+u)},{type:"elem",elem:h},{type:"kern",size:l}]},e);if(t.index){var d=e.havingStyle(w.SCRIPTSCRIPT),f=ue(t.index,d,e),g=.6*(p.height-p.depth),x=Dt.makeVList({positionType:"shift",positionData:-g,children:[{type:"elem",elem:f}]},e),v=Dt.makeSpan(["root"],[x]);return Dt.makeSpan(["mord","sqrt"],[v,p],e)}return Dt.makeSpan(["mord","sqrt"],[p],e)},mathmlBuilder:function(t,e){var r=t.body,a=t.index;return a?new ve.MathNode("mroot",[Me(r,e),Me(a,e)]):new ve.MathNode("msqrt",[Me(r,e)])}});var $r={display:w.DISPLAY,text:w.TEXT,script:w.SCRIPT,scriptscript:w.SCRIPTSCRIPT};Qt({type:"styling",names:["\\displaystyle","\\textstyle","\\scriptstyle","\\scriptscriptstyle"],props:{numArgs:0,allowedInText:!0},handler:function(t,e){var r=t.breakOnTokenText,a=t.funcName,n=t.parser,i=n.parseExpression(!0,r),o=a.slice(1,a.length-5);return{type:"styling",mode:n.mode,style:o,body:i}},htmlBuilder:function(t,e){var r=$r[t.style],a=e.havingStyle(r).withFont("");return _r(t.body,a,e)},mathmlBuilder:function(t,e){var r=$r[t.style],a=e.havingStyle(r),n=ke(t.body,a),i=new ve.MathNode("mstyle",n),o={display:["0","true"],text:["0","false"],script:["1","false"],scriptscript:["2","false"]}[t.style];return i.setAttribute("scriptlevel",o[0]),i.setAttribute("displaystyle",o[1]),i}});te({type:"supsub",htmlBuilder:function(t,e){var r=function(t,e){var r=t.base;return r?"op"===r.type?r.limits&&(e.style.size===w.DISPLAY.size||r.alwaysHandleSupSub)?Ur:null:"operatorname"===r.type?r.alwaysHandleSupSub&&(e.style.size===w.DISPLAY.size||r.limits)?Xr:null:"accent"===r.type?c.isCharacterBox(r.base)?Ee:null:"horizBrace"===r.type&&!t.sub===r.isOver?Hr:null:null}(t,e);if(r)return r(t,e);var a,n,i,o=t.base,s=t.sup,h=t.sub,l=ue(o,e),m=e.fontMetrics(),u=0,p=0,d=o&&c.isCharacterBox(o);if(s){var f=e.havingStyle(e.style.sup());a=ue(s,f,e),d||(u=l.height-f.fontMetrics().supDrop*f.sizeMultiplier/e.sizeMultiplier)}if(h){var g=e.havingStyle(e.style.sub());n=ue(h,g,e),d||(p=l.depth+g.fontMetrics().subDrop*g.sizeMultiplier/e.sizeMultiplier)}i=e.style===w.DISPLAY?m.sup1:e.style.cramped?m.sup3:m.sup2;var x,v=e.sizeMultiplier,b=.5/m.ptPerEm/v+"em",y=null;if(n){var k=t.base&&"op"===t.base.type&&t.base.name&&("\\oiint"===t.base.name||"\\oiiint"===t.base.name);(l instanceof E||k)&&(y=-l.italic+"em")}if(a&&n){u=Math.max(u,i,a.depth+.25*m.xHeight),p=Math.max(p,m.sub2);var S=4*m.defaultRuleThickness;if(u-a.depth-(n.height-p)<S){p=S-(u-a.depth)+n.height;var M=.8*m.xHeight-(u-a.depth);M>0&&(u+=M,p-=M)}var z=[{type:"elem",elem:n,shift:p,marginRight:b,marginLeft:y},{type:"elem",elem:a,shift:-u,marginRight:b}];x=Dt.makeVList({positionType:"individualShift",children:z},e)}else if(n){p=Math.max(p,m.sub1,n.height-.8*m.xHeight);var A=[{type:"elem",elem:n,marginLeft:y,marginRight:b}];x=Dt.makeVList({positionType:"shift",positionData:p,children:A},e)}else{if(!a)throw new Error("supsub must have either sup or sub.");u=Math.max(u,i,a.depth+.25*m.xHeight),x=Dt.makeVList({positionType:"shift",positionData:-u,children:[{type:"elem",elem:a,marginRight:b}]},e)}var T=me(l,"right")||"mord";return Dt.makeSpan([T],[l,Dt.makeSpan(["msupsub"],[x])],e)},mathmlBuilder:function(t,e){var r,a=!1,n=Vt(t.base,"horizBrace");n&&!!t.sup===n.isOver&&(a=!0,r=n.isOver),!t.base||"op"!==t.base.type&&"operatorname"!==t.base.type||(t.base.parentIsSupSub=!0);var i,o=[Me(t.base,e)];if(t.sub&&o.push(Me(t.sub,e)),t.sup&&o.push(Me(t.sup,e)),a)i=r?"mover":"munder";else if(t.sub)if(t.sup){var s=t.base;i=s&&"op"===s.type&&s.limits&&e.style===w.DISPLAY?"munderover":s&&"operatorname"===s.type&&s.alwaysHandleSupSub&&(e.style===w.DISPLAY||s.limits)?"munderover":"msubsup"}else{var h=t.base;i=h&&"op"===h.type&&h.limits&&(e.style===w.DISPLAY||h.alwaysHandleSupSub)?"munder":h&&"operatorname"===h.type&&h.alwaysHandleSupSub&&(h.limits||e.style===w.DISPLAY)?"munder":"msub"}else{var l=t.base;i=l&&"op"===l.type&&l.limits&&(e.style===w.DISPLAY||l.alwaysHandleSupSub)?"mover":l&&"operatorname"===l.type&&l.alwaysHandleSupSub&&(l.limits||e.style===w.DISPLAY)?"mover":"msup"}return new ve.MathNode(i,o)}}),te({type:"atom",htmlBuilder:function(t,e){return Dt.mathsym(t.text,t.mode,e,["m"+t.family])},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[be(t.text,t.mode)]);if("bin"===t.family){var a=we(t,e);"bold-italic"===a&&r.setAttribute("mathvariant",a)}else"punct"===t.family?r.setAttribute("separator","true"):"open"!==t.family&&"close"!==t.family||r.setAttribute("stretchy","false");return r}});var Zr={mi:"italic",mn:"normal",mtext:"normal"};te({type:"mathord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"mathord")},mathmlBuilder:function(t,e){var r=new ve.MathNode("mi",[be(t.text,t.mode,e)]),a=we(t,e)||"italic";return a!==Zr[r.type]&&r.setAttribute("mathvariant",a),r}}),te({type:"textord",htmlBuilder:function(t,e){return Dt.makeOrd(t,e,"textord")},mathmlBuilder:function(t,e){var r,a=be(t.text,t.mode,e),n=we(t,e)||"normal";return r="text"===t.mode?new ve.MathNode("mtext",[a]):/[0-9]/.test(t.text)?new ve.MathNode("mn",[a]):"\\prime"===t.text?new ve.MathNode("mo",[a]):new ve.MathNode("mi",[a]),n!==Zr[r.type]&&r.setAttribute("mathvariant",n),r}});var Kr={"\\nobreak":"nobreak","\\allowbreak":"allowbreak"},Jr={" ":{},"\\ ":{},"~":{className:"nobreak"},"\\space":{},"\\nobreakspace":{className:"nobreak"}};te({type:"spacing",htmlBuilder:function(t,e){if(Jr.hasOwnProperty(t.text)){var r=Jr[t.text].className||"";if("text"===t.mode){var a=Dt.makeOrd(t,e,"textord");return a.classes.push(r),a}return Dt.makeSpan(["mspace",r],[Dt.mathsym(t.text,t.mode,e)],e)}if(Kr.hasOwnProperty(t.text))return Dt.makeSpan(["mspace",Kr[t.text]],[],e);throw new o('Unknown type of space "'+t.text+'"')},mathmlBuilder:function(t,e){if(!Jr.hasOwnProperty(t.text)){if(Kr.hasOwnProperty(t.text))return new ve.MathNode("mspace");throw new o('Unknown type of space "'+t.text+'"')}return new ve.MathNode("mtext",[new ve.TextNode("\xa0")])}});var Qr=function(){var t=new ve.MathNode("mtd",[]);return t.setAttribute("width","50%"),t};te({type:"tag",mathmlBuilder:function(t,e){var r=new ve.MathNode("mtable",[new ve.MathNode("mtr",[Qr(),new ve.MathNode("mtd",[Se(t.body,e)]),Qr(),new ve.MathNode("mtd",[Se(t.tag,e)])])]);return r.setAttribute("width","100%"),r}});var ta={"\\text":void 0,"\\textrm":"textrm","\\textsf":"textsf","\\texttt":"texttt","\\textnormal":"textrm"},ea={"\\textbf":"textbf","\\textmd":"textmd"},ra={"\\textit":"textit","\\textup":"textup"},aa=function(t,e){var r=t.font;return r?ta[r]?e.withTextFontFamily(ta[r]):ea[r]?e.withTextFontWeight(ea[r]):e.withTextFontShape(ra[r]):e};Qt({type:"text",names:["\\text","\\textrm","\\textsf","\\texttt","\\textnormal","\\textbf","\\textmd","\\textit","\\textup"],props:{numArgs:1,argTypes:["text"],greediness:2,allowedInText:!0},handler:function(t,e){var r=t.parser,a=t.funcName,n=e[0];return{type:"text",mode:r.mode,body:ee(n),font:a}},htmlBuilder:function(t,e){var r=aa(t,e),a=se(t.body,r,!0);return Dt.makeSpan(["mord","text"],Dt.tryCombineChars(a),r)},mathmlBuilder:function(t,e){var r=aa(t,e);return Se(t.body,r)}}),Qt({type:"underline",names:["\\underline"],props:{numArgs:1,allowedInText:!0},handler:function(t,e){return{type:"underline",mode:t.parser.mode,body:e[0]}},htmlBuilder:function(t,e){var r=ue(t.body,e),a=Dt.makeLineSpan("underline-line",e),n=e.fontMetrics().defaultRuleThickness,i=Dt.makeVList({positionType:"top",positionData:r.height,children:[{type:"kern",size:n},{type:"elem",elem:a},{type:"kern",size:3*n},{type:"elem",elem:r}]},e);return Dt.makeSpan(["mord","underline"],[i],e)},mathmlBuilder:function(t,e){var r=new ve.MathNode("mo",[new ve.TextNode("\u203e")]);r.setAttribute("stretchy","true");var a=new ve.MathNode("munder",[Me(t.body,e),r]);return a.setAttribute("accentunder","true"),a}}),Qt({type:"verb",names:["\\verb"],props:{numArgs:0,allowedInText:!0},handler:function(t,e,r){throw new o("\\verb ended by end of line instead of matching delimiter")},htmlBuilder:function(t,e){for(var r=na(t),a=[],n=e.havingStyle(e.style.text()),i=0;i<r.length;i++){var o=r[i];"~"===o&&(o="\\textasciitilde"),a.push(Dt.makeSymbol(o,"Typewriter-Regular",t.mode,n,["mord","texttt"]))}return Dt.makeSpan(["mord","text"].concat(n.sizingClasses(e)),Dt.tryCombineChars(a),n)},mathmlBuilder:function(t,e){var r=new ve.TextNode(na(t)),a=new ve.MathNode("mtext",[r]);return a.setAttribute("mathvariant","monospace"),a}});var na=function(t){return t.body.replace(/ /g,t.star?"\u2423":"\xa0")},ia=Zt,oa=new RegExp("^(\\\\[a-zA-Z@]+)[ \r\n\t]*$"),sa=new RegExp("[\u0300-\u036f]+$"),ha="([ \r\n\t]+)|([!-\\[\\]-\u2027\u202a-\ud7ff\uf900-\uffff][\u0300-\u036f]*|[\ud800-\udbff][\udc00-\udfff][\u0300-\u036f]*|\\\\verb\\*([^]).*?\\3|\\\\verb([^*a-zA-Z]).*?\\4|\\\\operatorname\\*|\\\\[a-zA-Z@]+[ \r\n\t]*|\\\\[^\ud800-\udfff])",la=function(){function t(t,e){this.input=void 0,this.settings=void 0,this.tokenRegex=void 0,this.catcodes=void 0,this.input=t,this.settings=e,this.tokenRegex=new RegExp(ha,"g"),this.catcodes={"%":14}}var e=t.prototype;return e.setCatcode=function(t,e){this.catcodes[t]=e},e.lex=function(){var t=this.input,e=this.tokenRegex.lastIndex;if(e===t.length)return new n("EOF",new a(this,e,e));var r=this.tokenRegex.exec(t);if(null===r||r.index!==e)throw new o("Unexpected character: '"+t[e]+"'",new n(t[e],new a(this,e,e+1)));var i=r[2]||" ";if(14===this.catcodes[i]){var s=t.indexOf("\n",this.tokenRegex.lastIndex);return-1===s?(this.tokenRegex.lastIndex=t.length,this.settings.reportNonstrict("commentAtEnd","% comment has no terminating newline; LaTeX would fail because of commenting the end of math mode (e.g. $)")):this.tokenRegex.lastIndex=s+1,this.lex()}var h=i.match(oa);return h&&(i=h[1]),new n(i,new a(this,e,this.tokenRegex.lastIndex))},t}(),ma=function(){function t(t,e){void 0===t&&(t={}),void 0===e&&(e={}),this.current=void 0,this.builtins=void 0,this.undefStack=void 0,this.current=e,this.builtins=t,this.undefStack=[]}var e=t.prototype;return e.beginGroup=function(){this.undefStack.push({})},e.endGroup=function(){if(0===this.undefStack.length)throw new o("Unbalanced namespace destruction: attempt to pop global namespace; please report this as a bug");var t=this.undefStack.pop();for(var e in t)t.hasOwnProperty(e)&&(void 0===t[e]?delete this.current[e]:this.current[e]=t[e])},e.has=function(t){return this.current.hasOwnProperty(t)||this.builtins.hasOwnProperty(t)},e.get=function(t){return this.current.hasOwnProperty(t)?this.current[t]:this.builtins[t]},e.set=function(t,e,r){if(void 0===r&&(r=!1),r){for(var a=0;a<this.undefStack.length;a++)delete this.undefStack[a][t];this.undefStack.length>0&&(this.undefStack[this.undefStack.length-1][t]=e)}else{var n=this.undefStack[this.undefStack.length-1];n&&!n.hasOwnProperty(t)&&(n[t]=this.current[t])}this.current[t]=e},t}(),ca={},ua=ca;function pa(t,e){ca[t]=e}pa("\\@firstoftwo",function(t){return{tokens:t.consumeArgs(2)[0],numArgs:0}}),pa("\\@secondoftwo",function(t){return{tokens:t.consumeArgs(2)[1],numArgs:0}}),pa("\\@ifnextchar",function(t){var e=t.consumeArgs(3),r=t.future();return 1===e[0].length&&e[0][0].text===r.text?{tokens:e[1],numArgs:0}:{tokens:e[2],numArgs:0}}),pa("\\@ifstar","\\@ifnextchar *{\\@firstoftwo{#1}}"),pa("\\TextOrMath",function(t){var e=t.consumeArgs(2);return"text"===t.mode?{tokens:e[0],numArgs:0}:{tokens:e[1],numArgs:0}});var da={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,a:10,A:10,b:11,B:11,c:12,C:12,d:13,D:13,e:14,E:14,f:15,F:15};pa("\\char",function(t){var e,r=t.popToken(),a="";if("'"===r.text)e=8,r=t.popToken();else if('"'===r.text)e=16,r=t.popToken();else if("`"===r.text)if("\\"===(r=t.popToken()).text[0])a=r.text.charCodeAt(1);else{if("EOF"===r.text)throw new o("\\char` missing argument");a=r.text.charCodeAt(0)}else e=10;if(e){if(null==(a=da[r.text])||a>=e)throw new o("Invalid base-"+e+" digit "+r.text);for(var n;null!=(n=da[t.future().text])&&n<e;)a*=e,a+=n,t.popToken()}return"\\@char{"+a+"}"});var fa=function(t,e){var r=t.consumeArgs(1)[0];if(1!==r.length)throw new o("\\gdef's first argument must be a macro name");var a=r[0].text,n=0;for(r=t.consumeArgs(1)[0];1===r.length&&"#"===r[0].text;){if(1!==(r=t.consumeArgs(1)[0]).length)throw new o('Invalid argument number length "'+r.length+'"');if(!/^[1-9]$/.test(r[0].text))throw new o('Invalid argument number "'+r[0].text+'"');if(n++,parseInt(r[0].text)!==n)throw new o('Argument number "'+r[0].text+'" out of order');r=t.consumeArgs(1)[0]}return t.macros.set(a,{tokens:r,numArgs:n},e),""};pa("\\gdef",function(t){return fa(t,!0)}),pa("\\def",function(t){return fa(t,!1)}),pa("\\global",function(t){var e=t.consumeArgs(1)[0];if(1!==e.length)throw new o("Invalid command after \\global");var r=e[0].text;if("\\def"===r)return fa(t,!0);throw new o("Invalid command '"+r+"' after \\global")});var ga=function(t,e,r){var a=t.consumeArgs(1)[0];if(1!==a.length)throw new o("\\newcommand's first argument must be a macro name");var n=a[0].text,i=t.isDefined(n);if(i&&!e)throw new o("\\newcommand{"+n+"} attempting to redefine "+n+"; use \\renewcommand");if(!i&&!r)throw new o("\\renewcommand{"+n+"} when command "+n+" does not yet exist; use \\newcommand");var s=0;if(1===(a=t.consumeArgs(1)[0]).length&&"["===a[0].text){for(var h="",l=t.expandNextToken();"]"!==l.text&&"EOF"!==l.text;)h+=l.text,l=t.expandNextToken();if(!h.match(/^\s*[0-9]+\s*$/))throw new o("Invalid number of arguments: "+h);s=parseInt(h),a=t.consumeArgs(1)[0]}return t.macros.set(n,{tokens:a,numArgs:s}),""};pa("\\newcommand",function(t){return ga(t,!1,!0)}),pa("\\renewcommand",function(t){return ga(t,!0,!1)}),pa("\\providecommand",function(t){return ga(t,!0,!0)}),pa("\\bgroup","{"),pa("\\egroup","}"),pa("\\lq","`"),pa("\\rq","'"),pa("\\aa","\\r a"),pa("\\AA","\\r A"),pa("\\textcopyright","\\html@mathml{\\textcircled{c}}{\\char`\xa9}"),pa("\\copyright","\\TextOrMath{\\textcopyright}{\\text{\\textcopyright}}"),pa("\\textregistered","\\html@mathml{\\textcircled{\\scriptsize R}}{\\char`\xae}"),pa("\u212c","\\mathscr{B}"),pa("\u2130","\\mathscr{E}"),pa("\u2131","\\mathscr{F}"),pa("\u210b","\\mathscr{H}"),pa("\u2110","\\mathscr{I}"),pa("\u2112","\\mathscr{L}"),pa("\u2133","\\mathscr{M}"),pa("\u211b","\\mathscr{R}"),pa("\u212d","\\mathfrak{C}"),pa("\u210c","\\mathfrak{H}"),pa("\u2128","\\mathfrak{Z}"),pa("\\Bbbk","\\Bbb{k}"),pa("\xb7","\\cdotp"),pa("\\llap","\\mathllap{\\textrm{#1}}"),pa("\\rlap","\\mathrlap{\\textrm{#1}}"),pa("\\clap","\\mathclap{\\textrm{#1}}"),pa("\\not",'\\html@mathml{\\mathrel{\\mathrlap\\@not}}{\\char"338}'),pa("\\neq","\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`\u2260}}"),pa("\\ne","\\neq"),pa("\u2260","\\neq"),pa("\\notin","\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`\u2209}}"),pa("\u2209","\\notin"),pa("\u2258","\\html@mathml{\\mathrel{=\\kern{-1em}\\raisebox{0.4em}{$\\scriptsize\\frown$}}}{\\mathrel{\\char`\u2258}}"),pa("\u2259","\\html@mathml{\\stackrel{\\tiny\\wedge}{=}}{\\mathrel{\\char`\u2258}}"),pa("\u225a","\\html@mathml{\\stackrel{\\tiny\\vee}{=}}{\\mathrel{\\char`\u225a}}"),pa("\u225b","\\html@mathml{\\stackrel{\\scriptsize\\star}{=}}{\\mathrel{\\char`\u225b}}"),pa("\u225d","\\html@mathml{\\stackrel{\\tiny\\mathrm{def}}{=}}{\\mathrel{\\char`\u225d}}"),pa("\u225e","\\html@mathml{\\stackrel{\\tiny\\mathrm{m}}{=}}{\\mathrel{\\char`\u225e}}"),pa("\u225f","\\html@mathml{\\stackrel{\\tiny?}{=}}{\\mathrel{\\char`\u225f}}"),pa("\u27c2","\\perp"),pa("\u203c","\\mathclose{!\\mkern-0.8mu!}"),pa("\u220c","\\notni"),pa("\u231c","\\ulcorner"),pa("\u231d","\\urcorner"),pa("\u231e","\\llcorner"),pa("\u231f","\\lrcorner"),pa("\xa9","\\copyright"),pa("\xae","\\textregistered"),pa("\ufe0f","\\textregistered"),pa("\\vdots","\\mathord{\\varvdots\\rule{0pt}{15pt}}"),pa("\u22ee","\\vdots"),pa("\\varGamma","\\mathit{\\Gamma}"),pa("\\varDelta","\\mathit{\\Delta}"),pa("\\varTheta","\\mathit{\\Theta}"),pa("\\varLambda","\\mathit{\\Lambda}"),pa("\\varXi","\\mathit{\\Xi}"),pa("\\varPi","\\mathit{\\Pi}"),pa("\\varSigma","\\mathit{\\Sigma}"),pa("\\varUpsilon","\\mathit{\\Upsilon}"),pa("\\varPhi","\\mathit{\\Phi}"),pa("\\varPsi","\\mathit{\\Psi}"),pa("\\varOmega","\\mathit{\\Omega}"),pa("\\substack","\\begin{subarray}{c}#1\\end{subarray}"),pa("\\colon","\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu"),pa("\\boxed","\\fbox{$\\displaystyle{#1}$}"),pa("\\iff","\\DOTSB\\;\\Longleftrightarrow\\;"),pa("\\implies","\\DOTSB\\;\\Longrightarrow\\;"),pa("\\impliedby","\\DOTSB\\;\\Longleftarrow\\;");var xa={",":"\\dotsc","\\not":"\\dotsb","+":"\\dotsb","=":"\\dotsb","<":"\\dotsb",">":"\\dotsb","-":"\\dotsb","*":"\\dotsb",":":"\\dotsb","\\DOTSB":"\\dotsb","\\coprod":"\\dotsb","\\bigvee":"\\dotsb","\\bigwedge":"\\dotsb","\\biguplus":"\\dotsb","\\bigcap":"\\dotsb","\\bigcup":"\\dotsb","\\prod":"\\dotsb","\\sum":"\\dotsb","\\bigotimes":"\\dotsb","\\bigoplus":"\\dotsb","\\bigodot":"\\dotsb","\\bigsqcup":"\\dotsb","\\And":"\\dotsb","\\longrightarrow":"\\dotsb","\\Longrightarrow":"\\dotsb","\\longleftarrow":"\\dotsb","\\Longleftarrow":"\\dotsb","\\longleftrightarrow":"\\dotsb","\\Longleftrightarrow":"\\dotsb","\\mapsto":"\\dotsb","\\longmapsto":"\\dotsb","\\hookrightarrow":"\\dotsb","\\doteq":"\\dotsb","\\mathbin":"\\dotsb","\\mathrel":"\\dotsb","\\relbar":"\\dotsb","\\Relbar":"\\dotsb","\\xrightarrow":"\\dotsb","\\xleftarrow":"\\dotsb","\\DOTSI":"\\dotsi","\\int":"\\dotsi","\\oint":"\\dotsi","\\iint":"\\dotsi","\\iiint":"\\dotsi","\\iiiint":"\\dotsi","\\idotsint":"\\dotsi","\\DOTSX":"\\dotsx"};pa("\\dots",function(t){var e="\\dotso",r=t.expandAfterFuture().text;return r in xa?e=xa[r]:"\\not"===r.substr(0,4)?e="\\dotsb":r in j.math&&c.contains(["bin","rel"],j.math[r].group)&&(e="\\dotsb"),e});var va={")":!0,"]":!0,"\\rbrack":!0,"\\}":!0,"\\rbrace":!0,"\\rangle":!0,"\\rceil":!0,"\\rfloor":!0,"\\rgroup":!0,"\\rmoustache":!0,"\\right":!0,"\\bigr":!0,"\\biggr":!0,"\\Bigr":!0,"\\Biggr":!0,$:!0,";":!0,".":!0,",":!0};pa("\\dotso",function(t){return t.future().text in va?"\\ldots\\,":"\\ldots"}),pa("\\dotsc",function(t){var e=t.future().text;return e in va&&","!==e?"\\ldots\\,":"\\ldots"}),pa("\\cdots",function(t){return t.future().text in va?"\\@cdots\\,":"\\@cdots"}),pa("\\dotsb","\\cdots"),pa("\\dotsm","\\cdots"),pa("\\dotsi","\\!\\cdots"),pa("\\dotsx","\\ldots\\,"),pa("\\DOTSI","\\relax"),pa("\\DOTSB","\\relax"),pa("\\DOTSX","\\relax"),pa("\\tmspace","\\TextOrMath{\\kern#1#3}{\\mskip#1#2}\\relax"),pa("\\,","\\tmspace+{3mu}{.1667em}"),pa("\\thinspace","\\,"),pa("\\>","\\mskip{4mu}"),pa("\\:","\\tmspace+{4mu}{.2222em}"),pa("\\medspace","\\:"),pa("\\;","\\tmspace+{5mu}{.2777em}"),pa("\\thickspace","\\;"),pa("\\!","\\tmspace-{3mu}{.1667em}"),pa("\\negthinspace","\\!"),pa("\\negmedspace","\\tmspace-{4mu}{.2222em}"),pa("\\negthickspace","\\tmspace-{5mu}{.277em}"),pa("\\enspace","\\kern.5em "),pa("\\enskip","\\hskip.5em\\relax"),pa("\\quad","\\hskip1em\\relax"),pa("\\qquad","\\hskip2em\\relax"),pa("\\tag","\\@ifstar\\tag@literal\\tag@paren"),pa("\\tag@paren","\\tag@literal{({#1})}"),pa("\\tag@literal",function(t){if(t.macros.get("\\df@tag"))throw new o("Multiple \\tag");return"\\gdef\\df@tag{\\text{#1}}"}),pa("\\bmod","\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),pa("\\pod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),pa("\\pmod","\\pod{{\\rm mod}\\mkern6mu#1}"),pa("\\mod","\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),pa("\\pmb","\\html@mathml{\\@binrel{#1}{\\mathrlap{#1}\\kern0.5px#1}}{\\mathbf{#1}}"),pa("\\\\","\\newline"),pa("\\TeX","\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}");var ba=F["Main-Regular"]["T".charCodeAt(0)][1]-.7*F["Main-Regular"]["A".charCodeAt(0)][1]+"em";pa("\\LaTeX","\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{"+ba+"}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),pa("\\KaTeX","\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{"+ba+"}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),pa("\\hspace","\\@ifstar\\@hspacer\\@hspace"),pa("\\@hspace","\\hskip #1\\relax"),pa("\\@hspacer","\\rule{0pt}{0pt}\\hskip #1\\relax"),pa("\\ordinarycolon",":"),pa("\\vcentcolon","\\mathrel{\\mathop\\ordinarycolon}"),pa("\\dblcolon",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char"2237}}'),pa("\\coloneqq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2254}}'),pa("\\Coloneqq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char"2237\\char"3d}}'),pa("\\coloneq",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"3a\\char"2212}}'),pa("\\Coloneq",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char"2237\\char"2212}}'),pa("\\eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2255}}'),pa("\\Eqqcolon",'\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"3d\\char"2237}}'),pa("\\eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char"2239}}'),pa("\\Eqcolon",'\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char"2212\\char"2237}}'),pa("\\colonapprox",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"3a\\char"2248}}'),pa("\\Colonapprox",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char"2237\\char"2248}}'),pa("\\colonsim",'\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"3a\\char"223c}}'),pa("\\Colonsim",'\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char"2237\\char"223c}}'),pa("\u2237","\\dblcolon"),pa("\u2239","\\eqcolon"),pa("\u2254","\\coloneqq"),pa("\u2255","\\eqqcolon"),pa("\u2a74","\\Coloneqq"),pa("\\ratio","\\vcentcolon"),pa("\\coloncolon","\\dblcolon"),pa("\\colonequals","\\coloneqq"),pa("\\coloncolonequals","\\Coloneqq"),pa("\\equalscolon","\\eqqcolon"),pa("\\equalscoloncolon","\\Eqqcolon"),pa("\\colonminus","\\coloneq"),pa("\\coloncolonminus","\\Coloneq"),pa("\\minuscolon","\\eqcolon"),pa("\\minuscoloncolon","\\Eqcolon"),pa("\\coloncolonapprox","\\Colonapprox"),pa("\\coloncolonsim","\\Colonsim"),pa("\\simcolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),pa("\\simcoloncolon","\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),pa("\\approxcolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),pa("\\approxcoloncolon","\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),pa("\\notni","\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u220c}}"),pa("\\limsup","\\DOTSB\\operatorname*{lim\\,sup}"),pa("\\liminf","\\DOTSB\\operatorname*{lim\\,inf}"),pa("\\gvertneqq","\\html@mathml{\\@gvertneqq}{\u2269}"),pa("\\lvertneqq","\\html@mathml{\\@lvertneqq}{\u2268}"),pa("\\ngeqq","\\html@mathml{\\@ngeqq}{\u2271}"),pa("\\ngeqslant","\\html@mathml{\\@ngeqslant}{\u2271}"),pa("\\nleqq","\\html@mathml{\\@nleqq}{\u2270}"),pa("\\nleqslant","\\html@mathml{\\@nleqslant}{\u2270}"),pa("\\nshortmid","\\html@mathml{\\@nshortmid}{\u2224}"),pa("\\nshortparallel","\\html@mathml{\\@nshortparallel}{\u2226}"),pa("\\nsubseteqq","\\html@mathml{\\@nsubseteqq}{\u2288}"),pa("\\nsupseteqq","\\html@mathml{\\@nsupseteqq}{\u2289}"),pa("\\varsubsetneq","\\html@mathml{\\@varsubsetneq}{\u228a}"),pa("\\varsubsetneqq","\\html@mathml{\\@varsubsetneqq}{\u2acb}"),pa("\\varsupsetneq","\\html@mathml{\\@varsupsetneq}{\u228b}"),pa("\\varsupsetneqq","\\html@mathml{\\@varsupsetneqq}{\u2acc}"),pa("\\llbracket","\\html@mathml{\\mathopen{[\\mkern-3.2mu[}}{\\mathopen{\\char`\u27e6}}"),pa("\\rrbracket","\\html@mathml{\\mathclose{]\\mkern-3.2mu]}}{\\mathclose{\\char`\u27e7}}"),pa("\u27e6","\\llbracket"),pa("\u27e7","\\rrbracket"),pa("\\lBrace","\\html@mathml{\\mathopen{\\{\\mkern-3.2mu[}}{\\mathopen{\\char`\u2983}}"),pa("\\rBrace","\\html@mathml{\\mathclose{]\\mkern-3.2mu\\}}}{\\mathclose{\\char`\u2984}}"),pa("\u2983","\\lBrace"),pa("\u2984","\\rBrace"),pa("\\darr","\\downarrow"),pa("\\dArr","\\Downarrow"),pa("\\Darr","\\Downarrow"),pa("\\lang","\\langle"),pa("\\rang","\\rangle"),pa("\\uarr","\\uparrow"),pa("\\uArr","\\Uparrow"),pa("\\Uarr","\\Uparrow"),pa("\\N","\\mathbb{N}"),pa("\\R","\\mathbb{R}"),pa("\\Z","\\mathbb{Z}"),pa("\\alef","\\aleph"),pa("\\alefsym","\\aleph"),pa("\\Alpha","\\mathrm{A}"),pa("\\Beta","\\mathrm{B}"),pa("\\bull","\\bullet"),pa("\\Chi","\\mathrm{X}"),pa("\\clubs","\\clubsuit"),pa("\\cnums","\\mathbb{C}"),pa("\\Complex","\\mathbb{C}"),pa("\\Dagger","\\ddagger"),pa("\\diamonds","\\diamondsuit"),pa("\\empty","\\emptyset"),pa("\\Epsilon","\\mathrm{E}"),pa("\\Eta","\\mathrm{H}"),pa("\\exist","\\exists"),pa("\\harr","\\leftrightarrow"),pa("\\hArr","\\Leftrightarrow"),pa("\\Harr","\\Leftrightarrow"),pa("\\hearts","\\heartsuit"),pa("\\image","\\Im"),pa("\\infin","\\infty"),pa("\\Iota","\\mathrm{I}"),pa("\\isin","\\in"),pa("\\Kappa","\\mathrm{K}"),pa("\\larr","\\leftarrow"),pa("\\lArr","\\Leftarrow"),pa("\\Larr","\\Leftarrow"),pa("\\lrarr","\\leftrightarrow"),pa("\\lrArr","\\Leftrightarrow"),pa("\\Lrarr","\\Leftrightarrow"),pa("\\Mu","\\mathrm{M}"),pa("\\natnums","\\mathbb{N}"),pa("\\Nu","\\mathrm{N}"),pa("\\Omicron","\\mathrm{O}"),pa("\\plusmn","\\pm"),pa("\\rarr","\\rightarrow"),pa("\\rArr","\\Rightarrow"),pa("\\Rarr","\\Rightarrow"),pa("\\real","\\Re"),pa("\\reals","\\mathbb{R}"),pa("\\Reals","\\mathbb{R}"),pa("\\Rho","\\mathrm{P}"),pa("\\sdot","\\cdot"),pa("\\sect","\\S"),pa("\\spades","\\spadesuit"),pa("\\sub","\\subset"),pa("\\sube","\\subseteq"),pa("\\supe","\\supseteq"),pa("\\Tau","\\mathrm{T}"),pa("\\thetasym","\\vartheta"),pa("\\weierp","\\wp"),pa("\\Zeta","\\mathrm{Z}"),pa("\\argmin","\\DOTSB\\operatorname*{arg\\,min}"),pa("\\argmax","\\DOTSB\\operatorname*{arg\\,max}"),pa("\\plim","\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),pa("\\blue","\\textcolor{##6495ed}{#1}"),pa("\\orange","\\textcolor{##ffa500}{#1}"),pa("\\pink","\\textcolor{##ff00af}{#1}"),pa("\\red","\\textcolor{##df0030}{#1}"),pa("\\green","\\textcolor{##28ae7b}{#1}"),pa("\\gray","\\textcolor{gray}{#1}"),pa("\\purple","\\textcolor{##9d38bd}{#1}"),pa("\\blueA","\\textcolor{##ccfaff}{#1}"),pa("\\blueB","\\textcolor{##80f6ff}{#1}"),pa("\\blueC","\\textcolor{##63d9ea}{#1}"),pa("\\blueD","\\textcolor{##11accd}{#1}"),pa("\\blueE","\\textcolor{##0c7f99}{#1}"),pa("\\tealA","\\textcolor{##94fff5}{#1}"),pa("\\tealB","\\textcolor{##26edd5}{#1}"),pa("\\tealC","\\textcolor{##01d1c1}{#1}"),pa("\\tealD","\\textcolor{##01a995}{#1}"),pa("\\tealE","\\textcolor{##208170}{#1}"),pa("\\greenA","\\textcolor{##b6ffb0}{#1}"),pa("\\greenB","\\textcolor{##8af281}{#1}"),pa("\\greenC","\\textcolor{##74cf70}{#1}"),pa("\\greenD","\\textcolor{##1fab54}{#1}"),pa("\\greenE","\\textcolor{##0d923f}{#1}"),pa("\\goldA","\\textcolor{##ffd0a9}{#1}"),pa("\\goldB","\\textcolor{##ffbb71}{#1}"),pa("\\goldC","\\textcolor{##ff9c39}{#1}"),pa("\\goldD","\\textcolor{##e07d10}{#1}"),pa("\\goldE","\\textcolor{##a75a05}{#1}"),pa("\\redA","\\textcolor{##fca9a9}{#1}"),pa("\\redB","\\textcolor{##ff8482}{#1}"),pa("\\redC","\\textcolor{##f9685d}{#1}"),pa("\\redD","\\textcolor{##e84d39}{#1}"),pa("\\redE","\\textcolor{##bc2612}{#1}"),pa("\\maroonA","\\textcolor{##ffbde0}{#1}"),pa("\\maroonB","\\textcolor{##ff92c6}{#1}"),pa("\\maroonC","\\textcolor{##ed5fa6}{#1}"),pa("\\maroonD","\\textcolor{##ca337c}{#1}"),pa("\\maroonE","\\textcolor{##9e034e}{#1}"),pa("\\purpleA","\\textcolor{##ddd7ff}{#1}"),pa("\\purpleB","\\textcolor{##c6b9fc}{#1}"),pa("\\purpleC","\\textcolor{##aa87ff}{#1}"),pa("\\purpleD","\\textcolor{##7854ab}{#1}"),pa("\\purpleE","\\textcolor{##543b78}{#1}"),pa("\\mintA","\\textcolor{##f5f9e8}{#1}"),pa("\\mintB","\\textcolor{##edf2df}{#1}"),pa("\\mintC","\\textcolor{##e0e5cc}{#1}"),pa("\\grayA","\\textcolor{##f6f7f7}{#1}"),pa("\\grayB","\\textcolor{##f0f1f2}{#1}"),pa("\\grayC","\\textcolor{##e3e5e6}{#1}"),pa("\\grayD","\\textcolor{##d6d8da}{#1}"),pa("\\grayE","\\textcolor{##babec2}{#1}"),pa("\\grayF","\\textcolor{##888d93}{#1}"),pa("\\grayG","\\textcolor{##626569}{#1}"),pa("\\grayH","\\textcolor{##3b3e40}{#1}"),pa("\\grayI","\\textcolor{##21242c}{#1}"),pa("\\kaBlue","\\textcolor{##314453}{#1}"),pa("\\kaGreen","\\textcolor{##71B307}{#1}");var ya={"\\relax":!0,"^":!0,_:!0,"\\limits":!0,"\\nolimits":!0},wa=function(){function t(t,e,r){this.settings=void 0,this.expansionCount=void 0,this.lexer=void 0,this.macros=void 0,this.stack=void 0,this.mode=void 0,this.settings=e,this.expansionCount=0,this.feed(t),this.macros=new ma(ua,e.macros),this.mode=r,this.stack=[]}var e=t.prototype;return e.feed=function(t){this.lexer=new la(t,this.settings)},e.switchMode=function(t){this.mode=t},e.beginGroup=function(){this.macros.beginGroup()},e.endGroup=function(){this.macros.endGroup()},e.future=function(){return 0===this.stack.length&&this.pushToken(this.lexer.lex()),this.stack[this.stack.length-1]},e.popToken=function(){return this.future(),this.stack.pop()},e.pushToken=function(t){this.stack.push(t)},e.pushTokens=function(t){var e;(e=this.stack).push.apply(e,t)},e.consumeSpaces=function(){for(;;){if(" "!==this.future().text)break;this.stack.pop()}},e.consumeArgs=function(t){for(var e=[],r=0;r<t;++r){this.consumeSpaces();var a=this.popToken();if("{"===a.text){for(var n=[],i=1;0!==i;){var s=this.popToken();if(n.push(s),"{"===s.text)++i;else if("}"===s.text)--i;else if("EOF"===s.text)throw new o("End of input in macro argument",a)}n.pop(),n.reverse(),e[r]=n}else{if("EOF"===a.text)throw new o("End of input expecting macro argument");e[r]=[a]}}return e},e.expandOnce=function(){var t=this.popToken(),e=t.text,r=this._getExpansion(e);if(null==r)return this.pushToken(t),t;if(this.expansionCount++,this.expansionCount>this.settings.maxExpand)throw new o("Too many expansions: infinite loop or need to increase maxExpand setting");var a=r.tokens;if(r.numArgs)for(var n=this.consumeArgs(r.numArgs),i=(a=a.slice()).length-1;i>=0;--i){var s=a[i];if("#"===s.text){if(0===i)throw new o("Incomplete placeholder at end of macro body",s);if("#"===(s=a[--i]).text)a.splice(i+1,1);else{if(!/^[1-9]$/.test(s.text))throw new o("Not a valid argument number",s);var h;(h=a).splice.apply(h,[i,2].concat(n[+s.text-1]))}}}return this.pushTokens(a),a},e.expandAfterFuture=function(){return this.expandOnce(),this.future()},e.expandNextToken=function(){for(;;){var t=this.expandOnce();if(t instanceof n){if("\\relax"!==t.text)return this.stack.pop();this.stack.pop()}}throw new Error},e.expandMacro=function(t){if(this.macros.get(t)){var e=[],r=this.stack.length;for(this.pushToken(new n(t));this.stack.length>r;){this.expandOnce()instanceof n&&e.push(this.stack.pop())}return e}},e.expandMacroAsText=function(t){var e=this.expandMacro(t);return e?e.map(function(t){return t.text}).join(""):e},e._getExpansion=function(t){var e=this.macros.get(t);if(null==e)return e;var r="function"==typeof e?e(this):e;if("string"==typeof r){var a=0;if(-1!==r.indexOf("#"))for(var n=r.replace(/##/g,"");-1!==n.indexOf("#"+(a+1));)++a;for(var i=new la(r,this.settings),o=[],s=i.lex();"EOF"!==s.text;)o.push(s),s=i.lex();return o.reverse(),{tokens:o,numArgs:a}}return r},e.isDefined=function(t){return this.macros.has(t)||ia.hasOwnProperty(t)||j.math.hasOwnProperty(t)||j.text.hasOwnProperty(t)||ya.hasOwnProperty(t)},t}(),ka={"\u0301":{text:"\\'",math:"\\acute"},"\u0300":{text:"\\`",math:"\\grave"},"\u0308":{text:'\\"',math:"\\ddot"},"\u0303":{text:"\\~",math:"\\tilde"},"\u0304":{text:"\\=",math:"\\bar"},"\u0306":{text:"\\u",math:"\\breve"},"\u030c":{text:"\\v",math:"\\check"},"\u0302":{text:"\\^",math:"\\hat"},"\u0307":{text:"\\.",math:"\\dot"},"\u030a":{text:"\\r",math:"\\mathring"},"\u030b":{text:"\\H"}},Sa={"\xe1":"a\u0301","\xe0":"a\u0300","\xe4":"a\u0308","\u01df":"a\u0308\u0304","\xe3":"a\u0303","\u0101":"a\u0304","\u0103":"a\u0306","\u1eaf":"a\u0306\u0301","\u1eb1":"a\u0306\u0300","\u1eb5":"a\u0306\u0303","\u01ce":"a\u030c","\xe2":"a\u0302","\u1ea5":"a\u0302\u0301","\u1ea7":"a\u0302\u0300","\u1eab":"a\u0302\u0303","\u0227":"a\u0307","\u01e1":"a\u0307\u0304","\xe5":"a\u030a","\u01fb":"a\u030a\u0301","\u1e03":"b\u0307","\u0107":"c\u0301","\u010d":"c\u030c","\u0109":"c\u0302","\u010b":"c\u0307","\u010f":"d\u030c","\u1e0b":"d\u0307","\xe9":"e\u0301","\xe8":"e\u0300","\xeb":"e\u0308","\u1ebd":"e\u0303","\u0113":"e\u0304","\u1e17":"e\u0304\u0301","\u1e15":"e\u0304\u0300","\u0115":"e\u0306","\u011b":"e\u030c","\xea":"e\u0302","\u1ebf":"e\u0302\u0301","\u1ec1":"e\u0302\u0300","\u1ec5":"e\u0302\u0303","\u0117":"e\u0307","\u1e1f":"f\u0307","\u01f5":"g\u0301","\u1e21":"g\u0304","\u011f":"g\u0306","\u01e7":"g\u030c","\u011d":"g\u0302","\u0121":"g\u0307","\u1e27":"h\u0308","\u021f":"h\u030c","\u0125":"h\u0302","\u1e23":"h\u0307","\xed":"i\u0301","\xec":"i\u0300","\xef":"i\u0308","\u1e2f":"i\u0308\u0301","\u0129":"i\u0303","\u012b":"i\u0304","\u012d":"i\u0306","\u01d0":"i\u030c","\xee":"i\u0302","\u01f0":"j\u030c","\u0135":"j\u0302","\u1e31":"k\u0301","\u01e9":"k\u030c","\u013a":"l\u0301","\u013e":"l\u030c","\u1e3f":"m\u0301","\u1e41":"m\u0307","\u0144":"n\u0301","\u01f9":"n\u0300","\xf1":"n\u0303","\u0148":"n\u030c","\u1e45":"n\u0307","\xf3":"o\u0301","\xf2":"o\u0300","\xf6":"o\u0308","\u022b":"o\u0308\u0304","\xf5":"o\u0303","\u1e4d":"o\u0303\u0301","\u1e4f":"o\u0303\u0308","\u022d":"o\u0303\u0304","\u014d":"o\u0304","\u1e53":"o\u0304\u0301","\u1e51":"o\u0304\u0300","\u014f":"o\u0306","\u01d2":"o\u030c","\xf4":"o\u0302","\u1ed1":"o\u0302\u0301","\u1ed3":"o\u0302\u0300","\u1ed7":"o\u0302\u0303","\u022f":"o\u0307","\u0231":"o\u0307\u0304","\u0151":"o\u030b","\u1e55":"p\u0301","\u1e57":"p\u0307","\u0155":"r\u0301","\u0159":"r\u030c","\u1e59":"r\u0307","\u015b":"s\u0301","\u1e65":"s\u0301\u0307","\u0161":"s\u030c","\u1e67":"s\u030c\u0307","\u015d":"s\u0302","\u1e61":"s\u0307","\u1e97":"t\u0308","\u0165":"t\u030c","\u1e6b":"t\u0307","\xfa":"u\u0301","\xf9":"u\u0300","\xfc":"u\u0308","\u01d8":"u\u0308\u0301","\u01dc":"u\u0308\u0300","\u01d6":"u\u0308\u0304","\u01da":"u\u0308\u030c","\u0169":"u\u0303","\u1e79":"u\u0303\u0301","\u016b":"u\u0304","\u1e7b":"u\u0304\u0308","\u016d":"u\u0306","\u01d4":"u\u030c","\xfb":"u\u0302","\u016f":"u\u030a","\u0171":"u\u030b","\u1e7d":"v\u0303","\u1e83":"w\u0301","\u1e81":"w\u0300","\u1e85":"w\u0308","\u0175":"w\u0302","\u1e87":"w\u0307","\u1e98":"w\u030a","\u1e8d":"x\u0308","\u1e8b":"x\u0307","\xfd":"y\u0301","\u1ef3":"y\u0300","\xff":"y\u0308","\u1ef9":"y\u0303","\u0233":"y\u0304","\u0177":"y\u0302","\u1e8f":"y\u0307","\u1e99":"y\u030a","\u017a":"z\u0301","\u017e":"z\u030c","\u1e91":"z\u0302","\u017c":"z\u0307","\xc1":"A\u0301","\xc0":"A\u0300","\xc4":"A\u0308","\u01de":"A\u0308\u0304","\xc3":"A\u0303","\u0100":"A\u0304","\u0102":"A\u0306","\u1eae":"A\u0306\u0301","\u1eb0":"A\u0306\u0300","\u1eb4":"A\u0306\u0303","\u01cd":"A\u030c","\xc2":"A\u0302","\u1ea4":"A\u0302\u0301","\u1ea6":"A\u0302\u0300","\u1eaa":"A\u0302\u0303","\u0226":"A\u0307","\u01e0":"A\u0307\u0304","\xc5":"A\u030a","\u01fa":"A\u030a\u0301","\u1e02":"B\u0307","\u0106":"C\u0301","\u010c":"C\u030c","\u0108":"C\u0302","\u010a":"C\u0307","\u010e":"D\u030c","\u1e0a":"D\u0307","\xc9":"E\u0301","\xc8":"E\u0300","\xcb":"E\u0308","\u1ebc":"E\u0303","\u0112":"E\u0304","\u1e16":"E\u0304\u0301","\u1e14":"E\u0304\u0300","\u0114":"E\u0306","\u011a":"E\u030c","\xca":"E\u0302","\u1ebe":"E\u0302\u0301","\u1ec0":"E\u0302\u0300","\u1ec4":"E\u0302\u0303","\u0116":"E\u0307","\u1e1e":"F\u0307","\u01f4":"G\u0301","\u1e20":"G\u0304","\u011e":"G\u0306","\u01e6":"G\u030c","\u011c":"G\u0302","\u0120":"G\u0307","\u1e26":"H\u0308","\u021e":"H\u030c","\u0124":"H\u0302","\u1e22":"H\u0307","\xcd":"I\u0301","\xcc":"I\u0300","\xcf":"I\u0308","\u1e2e":"I\u0308\u0301","\u0128":"I\u0303","\u012a":"I\u0304","\u012c":"I\u0306","\u01cf":"I\u030c","\xce":"I\u0302","\u0130":"I\u0307","\u0134":"J\u0302","\u1e30":"K\u0301","\u01e8":"K\u030c","\u0139":"L\u0301","\u013d":"L\u030c","\u1e3e":"M\u0301","\u1e40":"M\u0307","\u0143":"N\u0301","\u01f8":"N\u0300","\xd1":"N\u0303","\u0147":"N\u030c","\u1e44":"N\u0307","\xd3":"O\u0301","\xd2":"O\u0300","\xd6":"O\u0308","\u022a":"O\u0308\u0304","\xd5":"O\u0303","\u1e4c":"O\u0303\u0301","\u1e4e":"O\u0303\u0308","\u022c":"O\u0303\u0304","\u014c":"O\u0304","\u1e52":"O\u0304\u0301","\u1e50":"O\u0304\u0300","\u014e":"O\u0306","\u01d1":"O\u030c","\xd4":"O\u0302","\u1ed0":"O\u0302\u0301","\u1ed2":"O\u0302\u0300","\u1ed6":"O\u0302\u0303","\u022e":"O\u0307","\u0230":"O\u0307\u0304","\u0150":"O\u030b","\u1e54":"P\u0301","\u1e56":"P\u0307","\u0154":"R\u0301","\u0158":"R\u030c","\u1e58":"R\u0307","\u015a":"S\u0301","\u1e64":"S\u0301\u0307","\u0160":"S\u030c","\u1e66":"S\u030c\u0307","\u015c":"S\u0302","\u1e60":"S\u0307","\u0164":"T\u030c","\u1e6a":"T\u0307","\xda":"U\u0301","\xd9":"U\u0300","\xdc":"U\u0308","\u01d7":"U\u0308\u0301","\u01db":"U\u0308\u0300","\u01d5":"U\u0308\u0304","\u01d9":"U\u0308\u030c","\u0168":"U\u0303","\u1e78":"U\u0303\u0301","\u016a":"U\u0304","\u1e7a":"U\u0304\u0308","\u016c":"U\u0306","\u01d3":"U\u030c","\xdb":"U\u0302","\u016e":"U\u030a","\u0170":"U\u030b","\u1e7c":"V\u0303","\u1e82":"W\u0301","\u1e80":"W\u0300","\u1e84":"W\u0308","\u0174":"W\u0302","\u1e86":"W\u0307","\u1e8c":"X\u0308","\u1e8a":"X\u0307","\xdd":"Y\u0301","\u1ef2":"Y\u0300","\u0178":"Y\u0308","\u1ef8":"Y\u0303","\u0232":"Y\u0304","\u0176":"Y\u0302","\u1e8e":"Y\u0307","\u0179":"Z\u0301","\u017d":"Z\u030c","\u1e90":"Z\u0302","\u017b":"Z\u0307","\u03ac":"\u03b1\u0301","\u1f70":"\u03b1\u0300","\u1fb1":"\u03b1\u0304","\u1fb0":"\u03b1\u0306","\u03ad":"\u03b5\u0301","\u1f72":"\u03b5\u0300","\u03ae":"\u03b7\u0301","\u1f74":"\u03b7\u0300","\u03af":"\u03b9\u0301","\u1f76":"\u03b9\u0300","\u03ca":"\u03b9\u0308","\u0390":"\u03b9\u0308\u0301","\u1fd2":"\u03b9\u0308\u0300","\u1fd1":"\u03b9\u0304","\u1fd0":"\u03b9\u0306","\u03cc":"\u03bf\u0301","\u1f78":"\u03bf\u0300","\u03cd":"\u03c5\u0301","\u1f7a":"\u03c5\u0300","\u03cb":"\u03c5\u0308","\u03b0":"\u03c5\u0308\u0301","\u1fe2":"\u03c5\u0308\u0300","\u1fe1":"\u03c5\u0304","\u1fe0":"\u03c5\u0306","\u03ce":"\u03c9\u0301","\u1f7c":"\u03c9\u0300","\u038e":"\u03a5\u0301","\u1fea":"\u03a5\u0300","\u03ab":"\u03a5\u0308","\u1fe9":"\u03a5\u0304","\u1fe8":"\u03a5\u0306","\u038f":"\u03a9\u0301","\u1ffa":"\u03a9\u0300"},Ma=function(){function t(t,e){this.mode=void 0,this.gullet=void 0,this.settings=void 0,this.leftrightDepth=void 0,this.nextToken=void 0,this.mode="math",this.gullet=new wa(t,e,this.mode),this.settings=e,this.leftrightDepth=0}var e=t.prototype;return e.expect=function(t,e){if(void 0===e&&(e=!0),this.fetch().text!==t)throw new o("Expected '"+t+"', got '"+this.fetch().text+"'",this.fetch());e&&this.consume()},e.consume=function(){this.nextToken=null},e.fetch=function(){return null==this.nextToken&&(this.nextToken=this.gullet.expandNextToken()),this.nextToken},e.switchMode=function(t){this.mode=t,this.gullet.switchMode(t)},e.parse=function(){this.gullet.beginGroup(),this.settings.colorIsTextColor&&this.gullet.macros.set("\\color","\\textcolor");var t=this.parseExpression(!1);return this.expect("EOF"),this.gullet.endGroup(),t},e.parseExpression=function(e,r){for(var a=[];;){"math"===this.mode&&this.consumeSpaces();var n=this.fetch();if(-1!==t.endOfExpression.indexOf(n.text))break;if(r&&n.text===r)break;if(e&&ia[n.text]&&ia[n.text].infix)break;var i=this.parseAtom(r);if(!i)break;a.push(i)}return"text"===this.mode&&this.formLigatures(a),this.handleInfixNodes(a)},e.handleInfixNodes=function(t){for(var e,r=-1,a=0;a<t.length;a++){var n=Vt(t[a],"infix");if(n){if(-1!==r)throw new o("only one infix operator per group",n.token);r=a,e=n.replaceWith}}if(-1!==r&&e){var i,s,h=t.slice(0,r),l=t.slice(r+1);return i=1===h.length&&"ordgroup"===h[0].type?h[0]:{type:"ordgroup",mode:this.mode,body:h},s=1===l.length&&"ordgroup"===l[0].type?l[0]:{type:"ordgroup",mode:this.mode,body:l},["\\\\abovefrac"===e?this.callFunction(e,[i,t[r],s],[]):this.callFunction(e,[i,s],[])]}return t},e.handleSupSubscript=function(e){var r=this.fetch(),a=r.text;this.consume();var n=this.parseGroup(e,!1,t.SUPSUB_GREEDINESS,void 0,void 0,!0);if(!n)throw new o("Expected group after '"+a+"'",r);return n},e.formatUnsupportedCmd=function(t){for(var e=[],r=0;r<t.length;r++)e.push({type:"textord",mode:"text",text:t[r]});var a={type:"text",mode:this.mode,body:e};return{type:"color",mode:this.mode,color:this.settings.errorColor,body:[a]}},e.parseAtom=function(t){var e,r,a=this.parseGroup("atom",!1,null,t);if("text"===this.mode)return a;for(;;){this.consumeSpaces();var n=this.fetch();if("\\limits"===n.text||"\\nolimits"===n.text){var i=Vt(a,"op");if(i){var s="\\limits"===n.text;i.limits=s,i.alwaysHandleSupSub=!0}else{if(!(i=Vt(a,"operatorname"))||!i.alwaysHandleSupSub)throw new o("Limit controls must follow a math operator",n);var h="\\limits"===n.text;i.limits=h}this.consume()}else if("^"===n.text){if(e)throw new o("Double superscript",n);e=this.handleSupSubscript("superscript")}else if("_"===n.text){if(r)throw new o("Double subscript",n);r=this.handleSupSubscript("subscript")}else{if("'"!==n.text)break;if(e)throw new o("Double superscript",n);var l={type:"textord",mode:this.mode,text:"\\prime"},m=[l];for(this.consume();"'"===this.fetch().text;)m.push(l),this.consume();"^"===this.fetch().text&&m.push(this.handleSupSubscript("superscript")),e={type:"ordgroup",mode:this.mode,body:m}}}return e||r?{type:"supsub",mode:this.mode,base:a,sup:e,sub:r}:a},e.parseFunction=function(t,e,r){var a=this.fetch(),n=a.text,i=ia[n];if(!i)return null;if(this.consume(),null!=r&&i.greediness<=r)throw new o("Got function '"+n+"' with no arguments"+(e?" as "+e:""),a);if("text"===this.mode&&!i.allowedInText)throw new o("Can't use function '"+n+"' in text mode",a);if("math"===this.mode&&!1===i.allowedInMath)throw new o("Can't use function '"+n+"' in math mode",a);var s=this.parseArguments(n,i),h=s.args,l=s.optArgs;return this.callFunction(n,h,l,a,t)},e.callFunction=function(t,e,r,a,n){var i={funcName:t,parser:this,token:a,breakOnTokenText:n},s=ia[t];if(s&&s.handler)return s.handler(i,e,r);throw new o("No function handler for "+t)},e.parseArguments=function(t,e){var r=e.numArgs+e.numOptionalArgs;if(0===r)return{args:[],optArgs:[]};for(var a=e.greediness,n=[],i=[],s=0;s<r;s++){var h=e.argTypes&&e.argTypes[s],l=s<e.numOptionalArgs,m=s>0&&!l||0===s&&!l&&"math"===this.mode,c=this.parseGroupOfType("argument to '"+t+"'",h,l,a,m);if(!c){if(l){i.push(null);continue}throw new o("Expected group after '"+t+"'",this.fetch())}(l?i:n).push(c)}return{args:n,optArgs:i}},e.parseGroupOfType=function(t,e,r,a,n){switch(e){case"color":return n&&this.consumeSpaces(),this.parseColorGroup(r);case"size":return n&&this.consumeSpaces(),this.parseSizeGroup(r);case"url":return this.parseUrlGroup(r,n);case"math":case"text":return this.parseGroup(t,r,a,void 0,e,n);case"hbox":var i=this.parseGroup(t,r,a,void 0,"text",n);return i?{type:"styling",mode:i.mode,body:[i],style:"text"}:i;case"raw":if(n&&this.consumeSpaces(),r&&"{"===this.fetch().text)return null;var s=this.parseStringGroup("raw",r,!0);if(s)return{type:"raw",mode:"text",string:s.text};throw new o("Expected raw group",this.fetch());case"original":case null:case void 0:return this.parseGroup(t,r,a,void 0,void 0,n);default:throw new o("Unknown group type as "+t,this.fetch())}},e.consumeSpaces=function(){for(;" "===this.fetch().text;)this.consume()},e.parseStringGroup=function(t,e,r){var a=e?"[":"{",n=e?"]":"}",i=this.fetch();if(i.text!==a){if(e)return null;if(r&&"EOF"!==i.text&&/[^{}[\]]/.test(i.text))return this.consume(),i}var s=this.mode;this.mode="text",this.expect(a);for(var h,l="",m=this.fetch(),c=0,u=m;(h=this.fetch()).text!==n||r&&c>0;){switch(h.text){case"EOF":throw new o("Unexpected end of input in "+t,m.range(u,l));case a:c++;break;case n:c--}l+=(u=h).text,this.consume()}return this.expect(n),this.mode=s,m.range(u,l)},e.parseRegexGroup=function(t,e){var r=this.mode;this.mode="text";for(var a,n=this.fetch(),i=n,s="";"EOF"!==(a=this.fetch()).text&&t.test(s+a.text);)s+=(i=a).text,this.consume();if(""===s)throw new o("Invalid "+e+": '"+n.text+"'",n);return this.mode=r,n.range(i,s)},e.parseColorGroup=function(t){var e=this.parseStringGroup("color",t);if(!e)return null;var r=/^(#[a-f0-9]{3}|#?[a-f0-9]{6}|[a-z]+)$/i.exec(e.text);if(!r)throw new o("Invalid color: '"+e.text+"'",e);var a=r[0];return/^[0-9a-f]{6}$/i.test(a)&&(a="#"+a),{type:"color-token",mode:this.mode,color:a}},e.parseSizeGroup=function(t){var e,r=!1;if(!(e=t||"{"===this.fetch().text?this.parseStringGroup("size",t):this.parseRegexGroup(/^[-+]? *(?:$|\d+|\d+\.\d*|\.\d*) *[a-z]{0,2} *$/,"size")))return null;t||0!==e.text.length||(e.text="0pt",r=!0);var a=/([-+]?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/.exec(e.text);if(!a)throw new o("Invalid size: '"+e.text+"'",e);var n={number:+(a[1]+a[2]),unit:a[3]};if(!At(n))throw new o("Invalid unit: '"+n.unit+"'",e);return{type:"size",mode:this.mode,value:n,isBlank:r}},e.parseUrlGroup=function(t,e){this.gullet.lexer.setCatcode("%",13);var r=this.parseStringGroup("url",t,!0);if(this.gullet.lexer.setCatcode("%",14),!r)return null;var a=r.text.replace(/\\([#$%&~_^{}])/g,"$1");return{type:"url",mode:this.mode,url:a}},e.parseGroup=function(e,r,n,i,s,h){var l=this.mode;s&&this.switchMode(s),h&&this.consumeSpaces();var m,c=this.fetch(),u=c.text;if(r?"["===u:"{"===u||"\\begingroup"===u){this.consume();var p=t.endOfGroup[u];this.gullet.beginGroup();var d=this.parseExpression(!1,p),f=this.fetch();this.expect(p),this.gullet.endGroup(),m={type:"ordgroup",mode:this.mode,loc:a.range(c,f),body:d,semisimple:"\\begingroup"===u||void 0}}else if(r)m=null;else if(null==(m=this.parseFunction(i,e,n)||this.parseSymbol())&&"\\"===u[0]&&!ya.hasOwnProperty(u)){if(this.settings.throwOnError)throw new o("Undefined control sequence: "+u,c);m=this.formatUnsupportedCmd(u),this.consume()}return s&&this.switchMode(l),m},e.formLigatures=function(t){for(var e=t.length-1,r=0;r<e;++r){var n=t[r],i=n.text;"-"===i&&"-"===t[r+1].text&&(r+1<e&&"-"===t[r+2].text?(t.splice(r,3,{type:"textord",mode:"text",loc:a.range(n,t[r+2]),text:"---"}),e-=2):(t.splice(r,2,{type:"textord",mode:"text",loc:a.range(n,t[r+1]),text:"--"}),e-=1)),"'"!==i&&"`"!==i||t[r+1].text!==i||(t.splice(r,2,{type:"textord",mode:"text",loc:a.range(n,t[r+1]),text:i+i}),e-=1)}},e.parseSymbol=function(){var t=this.fetch(),e=t.text;if(/^\\verb[^a-zA-Z]/.test(e)){this.consume();var r=e.slice(5),n="*"===r.charAt(0);if(n&&(r=r.slice(1)),r.length<2||r.charAt(0)!==r.slice(-1))throw new o("\\verb assertion failed --\n please report what input caused this bug");return{type:"verb",mode:"text",body:r=r.slice(1,-1),star:n}}Sa.hasOwnProperty(e[0])&&!j[this.mode][e[0]]&&(this.settings.strict&&"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Accented Unicode text character "'+e[0]+'" used in math mode',t),e=Sa[e[0]]+e.substr(1));var i,s=sa.exec(e);if(s&&("i"===(e=e.substring(0,s.index))?e="\u0131":"j"===e&&(e="\u0237")),j[this.mode][e]){this.settings.strict&&"math"===this.mode&&"\xc7\xd0\xde\xe7\xfe".indexOf(e)>=0&&this.settings.reportNonstrict("unicodeTextInMathMode",'Latin-1/Unicode text character "'+e[0]+'" used in math mode',t);var h,l=j[this.mode][e].group,m=a.range(t);if(W.hasOwnProperty(l)){var c=l;h={type:"atom",mode:this.mode,family:c,loc:m,text:e}}else h={type:l,mode:this.mode,loc:m,text:e};i=h}else{if(!(e.charCodeAt(0)>=128))return null;this.settings.strict&&(M(e.charCodeAt(0))?"math"===this.mode&&this.settings.reportNonstrict("unicodeTextInMathMode",'Unicode text character "'+e[0]+'" used in math mode',t):this.settings.reportNonstrict("unknownSymbol",'Unrecognized Unicode character "'+e[0]+'" ('+e.charCodeAt(0)+")",t)),i={type:"textord",mode:"text",loc:a.range(t),text:e}}if(this.consume(),s)for(var u=0;u<s[0].length;u++){var p=s[0][u];if(!ka[p])throw new o("Unknown accent ' "+p+"'",t);var d=ka[p][this.mode];if(!d)throw new o("Accent "+p+" unsupported in "+this.mode+" mode",t);i={type:"accent",mode:this.mode,loc:a.range(t),label:d,isStretchy:!1,isShifty:!0,base:i}}return i},t}();Ma.endOfExpression=["}","\\endgroup","\\end","\\right","&"],Ma.endOfGroup={"[":"]","{":"}","\\begingroup":"\\endgroup"},Ma.SUPSUB_GREEDINESS=1;var za=function(t,e){if(!("string"==typeof t||t instanceof String))throw new TypeError("KaTeX can only parse string typed expression");var r=new Ma(t,e);delete r.gullet.macros.current["\\df@tag"];var a=r.parse();if(r.gullet.macros.get("\\df@tag")){if(!e.displayMode)throw new o("\\tag works only in display equations");r.gullet.feed("\\df@tag"),a=[{type:"tag",mode:"text",body:a,tag:r.parse()}]}return a},Aa=function(t,e,r){e.textContent="";var a=Ba(t,r).toNode();e.appendChild(a)};"undefined"!=typeof document&&"CSS1Compat"!==document.compatMode&&("undefined"!=typeof console&&console.warn("Warning: KaTeX doesn't work in quirks mode. Make sure your website has a suitable doctype."),Aa=function(){throw new o("KaTeX doesn't work in quirks mode.")});var Ta=function(t,e,r){if(r.throwOnError||!(t instanceof o))throw t;var a=Dt.makeSpan(["katex-error"],[new E(e)]);return a.setAttribute("title",t.toString()),a.setAttribute("style","color:"+r.errorColor),a},Ba=function(t,e){var r=new u(e);try{var a=za(t,r);return Be(a,t,r)}catch(e){return Ta(e,t,r)}},Ca={version:"0.11.1",render:Aa,renderToString:function(t,e){return Ba(t,e).toMarkup()},ParseError:o,__parse:function(t,e){var r=new u(e);return za(t,r)},__renderToDomTree:Ba,__renderToHTMLTree:function(t,e){var r=new u(e);try{return function(t,e,r){var a=de(t,Ae(r)),n=Dt.makeSpan(["katex"],[a]);return Te(n,r)}(za(t,r),0,r)}catch(e){return Ta(e,t,r)}},__setFontMetrics:function(t,e){F[t]=e},__defineSymbol:$,__defineMacro:pa,__domTree:{Span:N,Anchor:I,SymbolNode:E,SvgNode:L,PathNode:H,LineNode:P}};e.default=Ca}]).default}); \ No newline at end of file diff --git a/docs/ldsctrlest-logo.png b/docs/ldsctrlest-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..73c068377b7643f5043515a6dff556a73a8964a1 GIT binary patch literal 16128 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 I)y{<fKf>47UjP6A literal 0 HcmV?d00001 diff --git a/docs/manifest.json b/docs/manifest.json new file mode 100644 index 00000000..e4b2fdfa --- /dev/null +++ b/docs/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "LDS C&E", + "short_name": "LDS C&E", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#000000", + "theme_color": "#000000", + "icons": [ + { + "src": "/favicon.svg", + "sizes": "512x512" + } + ] +} diff --git a/docs/mermaid.min.js b/docs/mermaid.min.js new file mode 100644 index 00000000..48da6d6c --- /dev/null +++ b/docs/mermaid.min.js @@ -0,0 +1,32 @@ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.mermaid=e():t.mermaid=e()}("undefined"!=typeof self?self:this,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)n.d(r,i,function(e){return t[e]}.bind(null,i));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=383)}([function(t,e,n){"use strict";n.r(e);var r=function(t,e){return t<e?-1:t>e?1:t>=e?0:NaN},i=function(t){var e;return 1===t.length&&(e=t,t=function(t,n){return r(e(t),n)}),{left:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)<0?r=a+1:i=a}return r},right:function(e,n,r,i){for(null==r&&(r=0),null==i&&(i=e.length);r<i;){var a=r+i>>>1;t(e[a],n)>0?i=a:r=a+1}return r}}};var a=i(r),o=a.right,s=a.left,c=o,u=function(t,e){null==e&&(e=l);for(var n=0,r=t.length-1,i=t[0],a=new Array(r<0?0:r);n<r;)a[n]=e(i,i=t[++n]);return a};function l(t,e){return[t,e]}var h=function(t,e,n){var r,i,a,o,s=t.length,c=e.length,u=new Array(s*c);for(null==n&&(n=l),r=a=0;r<s;++r)for(o=t[r],i=0;i<c;++i,++a)u[a]=n(o,e[i]);return u},f=function(t,e){return e<t?-1:e>t?1:e>=t?0:NaN},d=function(t){return null===t?NaN:+t},p=function(t,e){var n,r,i=t.length,a=0,o=-1,s=0,c=0;if(null==e)for(;++o<i;)isNaN(n=d(t[o]))||(c+=(r=n-s)*(n-(s+=r/++a)));else for(;++o<i;)isNaN(n=d(e(t[o],o,t)))||(c+=(r=n-s)*(n-(s+=r/++a)));if(a>1)return c/(a-1)},g=function(t,e){var n=p(t,e);return n?Math.sqrt(n):n},y=function(t,e){var n,r,i,a=t.length,o=-1;if(null==e){for(;++o<a;)if(null!=(n=t[o])&&n>=n)for(r=i=n;++o<a;)null!=(n=t[o])&&(r>n&&(r=n),i<n&&(i=n))}else for(;++o<a;)if(null!=(n=e(t[o],o,t))&&n>=n)for(r=i=n;++o<a;)null!=(n=e(t[o],o,t))&&(r>n&&(r=n),i<n&&(i=n));return[r,i]},v=Array.prototype,m=v.slice,b=v.map,x=function(t){return function(){return t}},_=function(t){return t},k=function(t,e,n){t=+t,e=+e,n=(i=arguments.length)<2?(e=t,t=0,1):i<3?1:+n;for(var r=-1,i=0|Math.max(0,Math.ceil((e-t)/n)),a=new Array(i);++r<i;)a[r]=t+r*n;return a},w=Math.sqrt(50),E=Math.sqrt(10),T=Math.sqrt(2),C=function(t,e,n){var r,i,a,o,s=-1;if(n=+n,(t=+t)===(e=+e)&&n>0)return[t];if((r=e<t)&&(i=t,t=e,e=i),0===(o=A(t,e,n))||!isFinite(o))return[];if(o>0)for(t=Math.ceil(t/o),e=Math.floor(e/o),a=new Array(i=Math.ceil(e-t+1));++s<i;)a[s]=(t+s)*o;else for(t=Math.floor(t*o),e=Math.ceil(e*o),a=new Array(i=Math.ceil(t-e+1));++s<i;)a[s]=(t-s)/o;return r&&a.reverse(),a};function A(t,e,n){var r=(e-t)/Math.max(0,n),i=Math.floor(Math.log(r)/Math.LN10),a=r/Math.pow(10,i);return i>=0?(a>=w?10:a>=E?5:a>=T?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(a>=w?10:a>=E?5:a>=T?2:1)}function S(t,e,n){var r=Math.abs(e-t)/Math.max(0,n),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),a=r/i;return a>=w?i*=10:a>=E?i*=5:a>=T&&(i*=2),e<t?-i:i}var M=function(t){return Math.ceil(Math.log(t.length)/Math.LN2)+1},O=function(){var t=_,e=y,n=M;function r(r){var i,a,o=r.length,s=new Array(o);for(i=0;i<o;++i)s[i]=t(r[i],i,r);var u=e(s),l=u[0],h=u[1],f=n(s,l,h);Array.isArray(f)||(f=S(l,h,f),f=k(Math.ceil(l/f)*f,h,f));for(var d=f.length;f[0]<=l;)f.shift(),--d;for(;f[d-1]>h;)f.pop(),--d;var p,g=new Array(d+1);for(i=0;i<=d;++i)(p=g[i]=[]).x0=i>0?f[i-1]:l,p.x1=i<d?f[i]:h;for(i=0;i<o;++i)l<=(a=s[i])&&a<=h&&g[c(f,a,0,d)].push(r[i]);return g}return r.value=function(e){return arguments.length?(t="function"==typeof e?e:x(e),r):t},r.domain=function(t){return arguments.length?(e="function"==typeof t?t:x([t[0],t[1]]),r):e},r.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?x(m.call(t)):x(t),r):n},r},D=function(t,e,n){if(null==n&&(n=d),r=t.length){if((e=+e)<=0||r<2)return+n(t[0],0,t);if(e>=1)return+n(t[r-1],r-1,t);var r,i=(r-1)*e,a=Math.floor(i),o=+n(t[a],a,t);return o+(+n(t[a+1],a+1,t)-o)*(i-a)}},N=function(t,e,n){return t=b.call(t,d).sort(r),Math.ceil((n-e)/(2*(D(t,.75)-D(t,.25))*Math.pow(t.length,-1/3)))},B=function(t,e,n){return Math.ceil((n-e)/(3.5*g(t)*Math.pow(t.length,-1/3)))},L=function(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&n>r&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&n>r&&(r=n);return r},P=function(t,e){var n,r=t.length,i=r,a=-1,o=0;if(null==e)for(;++a<r;)isNaN(n=d(t[a]))?--i:o+=n;else for(;++a<r;)isNaN(n=d(e(t[a],a,t)))?--i:o+=n;if(i)return o/i},I=function(t,e){var n,i=t.length,a=-1,o=[];if(null==e)for(;++a<i;)isNaN(n=d(t[a]))||o.push(n);else for(;++a<i;)isNaN(n=d(e(t[a],a,t)))||o.push(n);return D(o.sort(r),.5)},F=function(t){for(var e,n,r,i=t.length,a=-1,o=0;++a<i;)o+=t[a].length;for(n=new Array(o);--i>=0;)for(e=(r=t[i]).length;--e>=0;)n[--o]=r[e];return n},j=function(t,e){var n,r,i=t.length,a=-1;if(null==e){for(;++a<i;)if(null!=(n=t[a])&&n>=n)for(r=n;++a<i;)null!=(n=t[a])&&r>n&&(r=n)}else for(;++a<i;)if(null!=(n=e(t[a],a,t))&&n>=n)for(r=n;++a<i;)null!=(n=e(t[a],a,t))&&r>n&&(r=n);return r},R=function(t,e){for(var n=e.length,r=new Array(n);n--;)r[n]=t[e[n]];return r},Y=function(t,e){if(n=t.length){var n,i,a=0,o=0,s=t[o];for(null==e&&(e=r);++a<n;)(e(i=t[a],s)<0||0!==e(s,s))&&(s=i,o=a);return 0===e(s,s)?o:void 0}},z=function(t,e,n){for(var r,i,a=(null==n?t.length:n)-(e=null==e?0:+e);a;)i=Math.random()*a--|0,r=t[a+e],t[a+e]=t[i+e],t[i+e]=r;return t},U=function(t,e){var n,r=t.length,i=-1,a=0;if(null==e)for(;++i<r;)(n=+t[i])&&(a+=n);else for(;++i<r;)(n=+e(t[i],i,t))&&(a+=n);return a},$=function(t){if(!(i=t.length))return[];for(var e=-1,n=j(t,W),r=new Array(n);++e<n;)for(var i,a=-1,o=r[e]=new Array(i);++a<i;)o[a]=t[a][e];return r};function W(t){return t.length}var H=function(){return $(arguments)},V=Array.prototype.slice,G=function(t){return t};function q(t){return"translate("+(t+.5)+",0)"}function X(t){return"translate(0,"+(t+.5)+")"}function Z(t){return function(e){return+t(e)}}function J(t){var e=Math.max(0,t.bandwidth()-1)/2;return t.round()&&(e=Math.round(e)),function(n){return+t(n)+e}}function K(){return!this.__axis}function Q(t,e){var n=[],r=null,i=null,a=6,o=6,s=3,c=1===t||4===t?-1:1,u=4===t||2===t?"x":"y",l=1===t||3===t?q:X;function h(h){var f=null==r?e.ticks?e.ticks.apply(e,n):e.domain():r,d=null==i?e.tickFormat?e.tickFormat.apply(e,n):G:i,p=Math.max(a,0)+s,g=e.range(),y=+g[0]+.5,v=+g[g.length-1]+.5,m=(e.bandwidth?J:Z)(e.copy()),b=h.selection?h.selection():h,x=b.selectAll(".domain").data([null]),_=b.selectAll(".tick").data(f,e).order(),k=_.exit(),w=_.enter().append("g").attr("class","tick"),E=_.select("line"),T=_.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),_=_.merge(w),E=E.merge(w.append("line").attr("stroke","currentColor").attr(u+"2",c*a)),T=T.merge(w.append("text").attr("fill","currentColor").attr(u,c*p).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==b&&(x=x.transition(h),_=_.transition(h),E=E.transition(h),T=T.transition(h),k=k.transition(h).attr("opacity",1e-6).attr("transform",(function(t){return isFinite(t=m(t))?l(t):this.getAttribute("transform")})),w.attr("opacity",1e-6).attr("transform",(function(t){var e=this.parentNode.__axis;return l(e&&isFinite(e=e(t))?e:m(t))}))),k.remove(),x.attr("d",4===t||2==t?o?"M"+c*o+","+y+"H0.5V"+v+"H"+c*o:"M0.5,"+y+"V"+v:o?"M"+y+","+c*o+"V0.5H"+v+"V"+c*o:"M"+y+",0.5H"+v),_.attr("opacity",1).attr("transform",(function(t){return l(m(t))})),E.attr(u+"2",c*a),T.attr(u,c*p).text(d),b.filter(K).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),b.each((function(){this.__axis=m}))}return h.scale=function(t){return arguments.length?(e=t,h):e},h.ticks=function(){return n=V.call(arguments),h},h.tickArguments=function(t){return arguments.length?(n=null==t?[]:V.call(t),h):n.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:V.call(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(a=o=+t,h):a},h.tickSizeInner=function(t){return arguments.length?(a=+t,h):a},h.tickSizeOuter=function(t){return arguments.length?(o=+t,h):o},h.tickPadding=function(t){return arguments.length?(s=+t,h):s},h}function tt(t){return Q(1,t)}function et(t){return Q(2,t)}function nt(t){return Q(3,t)}function rt(t){return Q(4,t)}var it={value:function(){}};function at(){for(var t,e=0,n=arguments.length,r={};e<n;++e){if(!(t=arguments[e]+"")||t in r||/[\s.]/.test(t))throw new Error("illegal type: "+t);r[t]=[]}return new ot(r)}function ot(t){this._=t}function st(t,e){return t.trim().split(/^|\s+/).map((function(t){var n="",r=t.indexOf(".");if(r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),t&&!e.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:n}}))}function ct(t,e){for(var n,r=0,i=t.length;r<i;++r)if((n=t[r]).name===e)return n.value}function ut(t,e,n){for(var r=0,i=t.length;r<i;++r)if(t[r].name===e){t[r]=it,t=t.slice(0,r).concat(t.slice(r+1));break}return null!=n&&t.push({name:e,value:n}),t}ot.prototype=at.prototype={constructor:ot,on:function(t,e){var n,r=this._,i=st(t+"",r),a=-1,o=i.length;if(!(arguments.length<2)){if(null!=e&&"function"!=typeof e)throw new Error("invalid callback: "+e);for(;++a<o;)if(n=(t=i[a]).type)r[n]=ut(r[n],t.name,e);else if(null==e)for(n in r)r[n]=ut(r[n],t.name,null);return this}for(;++a<o;)if((n=(t=i[a]).type)&&(n=ct(r[n],t.name)))return n},copy:function(){var t={},e=this._;for(var n in e)t[n]=e[n].slice();return new ot(t)},call:function(t,e){if((n=arguments.length-2)>0)for(var n,r,i=new Array(n),a=0;a<n;++a)i[a]=arguments[a+2];if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(a=0,n=(r=this._[t]).length;a<n;++a)r[a].value.apply(e,i)},apply:function(t,e,n){if(!this._.hasOwnProperty(t))throw new Error("unknown type: "+t);for(var r=this._[t],i=0,a=r.length;i<a;++i)r[i].value.apply(e,n)}};var lt=at;function ht(){}var ft=function(t){return null==t?ht:function(){return this.querySelector(t)}};function dt(){return[]}var pt=function(t){return null==t?dt:function(){return this.querySelectorAll(t)}},gt=function(t){return function(){return this.matches(t)}},yt=function(t){return new Array(t.length)};function vt(t,e){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=e}vt.prototype={constructor:vt,appendChild:function(t){return this._parent.insertBefore(t,this._next)},insertBefore:function(t,e){return this._parent.insertBefore(t,e)},querySelector:function(t){return this._parent.querySelector(t)},querySelectorAll:function(t){return this._parent.querySelectorAll(t)}};function mt(t,e,n,r,i,a){for(var o,s=0,c=e.length,u=a.length;s<u;++s)(o=e[s])?(o.__data__=a[s],r[s]=o):n[s]=new vt(t,a[s]);for(;s<c;++s)(o=e[s])&&(i[s]=o)}function bt(t,e,n,r,i,a,o){var s,c,u,l={},h=e.length,f=a.length,d=new Array(h);for(s=0;s<h;++s)(c=e[s])&&(d[s]=u="$"+o.call(c,c.__data__,s,e),u in l?i[s]=c:l[u]=c);for(s=0;s<f;++s)(c=l[u="$"+o.call(t,a[s],s,a)])?(r[s]=c,c.__data__=a[s],l[u]=null):n[s]=new vt(t,a[s]);for(s=0;s<h;++s)(c=e[s])&&l[d[s]]===c&&(i[s]=c)}function xt(t,e){return t<e?-1:t>e?1:t>=e?0:NaN}var _t="http://www.w3.org/1999/xhtml",kt={svg:"http://www.w3.org/2000/svg",xhtml:_t,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"},wt=function(t){var e=t+="",n=e.indexOf(":");return n>=0&&"xmlns"!==(e=t.slice(0,n))&&(t=t.slice(n+1)),kt.hasOwnProperty(e)?{space:kt[e],local:t}:t};function Et(t){return function(){this.removeAttribute(t)}}function Tt(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Ct(t,e){return function(){this.setAttribute(t,e)}}function At(t,e){return function(){this.setAttributeNS(t.space,t.local,e)}}function St(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttribute(t):this.setAttribute(t,n)}}function Mt(t,e){return function(){var n=e.apply(this,arguments);null==n?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,n)}}var Ot=function(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView};function Dt(t){return function(){this.style.removeProperty(t)}}function Nt(t,e,n){return function(){this.style.setProperty(t,e,n)}}function Bt(t,e,n){return function(){var r=e.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,n)}}function Lt(t,e){return t.style.getPropertyValue(e)||Ot(t).getComputedStyle(t,null).getPropertyValue(e)}function Pt(t){return function(){delete this[t]}}function It(t,e){return function(){this[t]=e}}function Ft(t,e){return function(){var n=e.apply(this,arguments);null==n?delete this[t]:this[t]=n}}function jt(t){return t.trim().split(/^|\s+/)}function Rt(t){return t.classList||new Yt(t)}function Yt(t){this._node=t,this._names=jt(t.getAttribute("class")||"")}function zt(t,e){for(var n=Rt(t),r=-1,i=e.length;++r<i;)n.add(e[r])}function Ut(t,e){for(var n=Rt(t),r=-1,i=e.length;++r<i;)n.remove(e[r])}function $t(t){return function(){zt(this,t)}}function Wt(t){return function(){Ut(this,t)}}function Ht(t,e){return function(){(e.apply(this,arguments)?zt:Ut)(this,t)}}Yt.prototype={add:function(t){this._names.indexOf(t)<0&&(this._names.push(t),this._node.setAttribute("class",this._names.join(" ")))},remove:function(t){var e=this._names.indexOf(t);e>=0&&(this._names.splice(e,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};function Vt(){this.textContent=""}function Gt(t){return function(){this.textContent=t}}function qt(t){return function(){var e=t.apply(this,arguments);this.textContent=null==e?"":e}}function Xt(){this.innerHTML=""}function Zt(t){return function(){this.innerHTML=t}}function Jt(t){return function(){var e=t.apply(this,arguments);this.innerHTML=null==e?"":e}}function Kt(){this.nextSibling&&this.parentNode.appendChild(this)}function Qt(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function te(t){return function(){var e=this.ownerDocument,n=this.namespaceURI;return n===_t&&e.documentElement.namespaceURI===_t?e.createElement(t):e.createElementNS(n,t)}}function ee(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}var ne=function(t){var e=wt(t);return(e.local?ee:te)(e)};function re(){return null}function ie(){var t=this.parentNode;t&&t.removeChild(this)}function ae(){var t=this.cloneNode(!1),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}function oe(){var t=this.cloneNode(!0),e=this.parentNode;return e?e.insertBefore(t,this.nextSibling):t}var se={},ce=null;"undefined"!=typeof document&&("onmouseenter"in document.documentElement||(se={mouseenter:"mouseover",mouseleave:"mouseout"}));function ue(t,e,n){return t=le(t,e,n),function(e){var n=e.relatedTarget;n&&(n===this||8&n.compareDocumentPosition(this))||t.call(this,e)}}function le(t,e,n){return function(r){var i=ce;ce=r;try{t.call(this,this.__data__,e,n)}finally{ce=i}}}function he(t){return t.trim().split(/^|\s+/).map((function(t){var e="",n=t.indexOf(".");return n>=0&&(e=t.slice(n+1),t=t.slice(0,n)),{type:t,name:e}}))}function fe(t){return function(){var e=this.__on;if(e){for(var n,r=0,i=-1,a=e.length;r<a;++r)n=e[r],t.type&&n.type!==t.type||n.name!==t.name?e[++i]=n:this.removeEventListener(n.type,n.listener,n.capture);++i?e.length=i:delete this.__on}}}function de(t,e,n){var r=se.hasOwnProperty(t.type)?ue:le;return function(i,a,o){var s,c=this.__on,u=r(e,a,o);if(c)for(var l=0,h=c.length;l<h;++l)if((s=c[l]).type===t.type&&s.name===t.name)return this.removeEventListener(s.type,s.listener,s.capture),this.addEventListener(s.type,s.listener=u,s.capture=n),void(s.value=e);this.addEventListener(t.type,u,n),s={type:t.type,name:t.name,value:e,listener:u,capture:n},c?c.push(s):this.__on=[s]}}function pe(t,e,n,r){var i=ce;t.sourceEvent=ce,ce=t;try{return e.apply(n,r)}finally{ce=i}}function ge(t,e,n){var r=Ot(t),i=r.CustomEvent;"function"==typeof i?i=new i(e,n):(i=r.document.createEvent("Event"),n?(i.initEvent(e,n.bubbles,n.cancelable),i.detail=n.detail):i.initEvent(e,!1,!1)),t.dispatchEvent(i)}function ye(t,e){return function(){return ge(this,t,e)}}function ve(t,e){return function(){return ge(this,t,e.apply(this,arguments))}}var me=[null];function be(t,e){this._groups=t,this._parents=e}function xe(){return new be([[document.documentElement]],me)}be.prototype=xe.prototype={constructor:be,select:function(t){"function"!=typeof t&&(t=ft(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o,s=e[i],c=s.length,u=r[i]=new Array(c),l=0;l<c;++l)(a=s[l])&&(o=t.call(a,a.__data__,l,s))&&("__data__"in a&&(o.__data__=a.__data__),u[l]=o);return new be(r,this._parents)},selectAll:function(t){"function"!=typeof t&&(t=pt(t));for(var e=this._groups,n=e.length,r=[],i=[],a=0;a<n;++a)for(var o,s=e[a],c=s.length,u=0;u<c;++u)(o=s[u])&&(r.push(t.call(o,o.__data__,u,s)),i.push(o));return new be(r,i)},filter:function(t){"function"!=typeof t&&(t=gt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new be(r,this._parents)},data:function(t,e){if(!t)return p=new Array(this.size()),l=-1,this.each((function(t){p[++l]=t})),p;var n,r=e?bt:mt,i=this._parents,a=this._groups;"function"!=typeof t&&(n=t,t=function(){return n});for(var o=a.length,s=new Array(o),c=new Array(o),u=new Array(o),l=0;l<o;++l){var h=i[l],f=a[l],d=f.length,p=t.call(h,h&&h.__data__,l,i),g=p.length,y=c[l]=new Array(g),v=s[l]=new Array(g);r(h,f,y,v,u[l]=new Array(d),p,e);for(var m,b,x=0,_=0;x<g;++x)if(m=y[x]){for(x>=_&&(_=x+1);!(b=v[_])&&++_<g;);m._next=b||null}}return(s=new be(s,i))._enter=c,s._exit=u,s},enter:function(){return new be(this._enter||this._groups.map(yt),this._parents)},exit:function(){return new be(this._exit||this._groups.map(yt),this._parents)},join:function(t,e,n){var r=this.enter(),i=this,a=this.exit();return r="function"==typeof t?t(r):r.append(t+""),null!=e&&(i=e(i)),null==n?a.remove():n(a),r&&i?r.merge(i).order():i},merge:function(t){for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new be(o,this._parents)},order:function(){for(var t=this._groups,e=-1,n=t.length;++e<n;)for(var r,i=t[e],a=i.length-1,o=i[a];--a>=0;)(r=i[a])&&(o&&4^r.compareDocumentPosition(o)&&o.parentNode.insertBefore(r,o),o=r);return this},sort:function(t){function e(e,n){return e&&n?t(e.__data__,n.__data__):!e-!n}t||(t=xt);for(var n=this._groups,r=n.length,i=new Array(r),a=0;a<r;++a){for(var o,s=n[a],c=s.length,u=i[a]=new Array(c),l=0;l<c;++l)(o=s[l])&&(u[l]=o);u.sort(e)}return new be(i,this._parents).order()},call:function(){var t=arguments[0];return arguments[0]=this,t.apply(null,arguments),this},nodes:function(){var t=new Array(this.size()),e=-1;return this.each((function(){t[++e]=this})),t},node:function(){for(var t=this._groups,e=0,n=t.length;e<n;++e)for(var r=t[e],i=0,a=r.length;i<a;++i){var o=r[i];if(o)return o}return null},size:function(){var t=0;return this.each((function(){++t})),t},empty:function(){return!this.node()},each:function(t){for(var e=this._groups,n=0,r=e.length;n<r;++n)for(var i,a=e[n],o=0,s=a.length;o<s;++o)(i=a[o])&&t.call(i,i.__data__,o,a);return this},attr:function(t,e){var n=wt(t);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((null==e?n.local?Tt:Et:"function"==typeof e?n.local?Mt:St:n.local?At:Ct)(n,e))},style:function(t,e,n){return arguments.length>1?this.each((null==e?Dt:"function"==typeof e?Bt:Nt)(t,e,null==n?"":n)):Lt(this.node(),t)},property:function(t,e){return arguments.length>1?this.each((null==e?Pt:"function"==typeof e?Ft:It)(t,e)):this.node()[t]},classed:function(t,e){var n=jt(t+"");if(arguments.length<2){for(var r=Rt(this.node()),i=-1,a=n.length;++i<a;)if(!r.contains(n[i]))return!1;return!0}return this.each(("function"==typeof e?Ht:e?$t:Wt)(n,e))},text:function(t){return arguments.length?this.each(null==t?Vt:("function"==typeof t?qt:Gt)(t)):this.node().textContent},html:function(t){return arguments.length?this.each(null==t?Xt:("function"==typeof t?Jt:Zt)(t)):this.node().innerHTML},raise:function(){return this.each(Kt)},lower:function(){return this.each(Qt)},append:function(t){var e="function"==typeof t?t:ne(t);return this.select((function(){return this.appendChild(e.apply(this,arguments))}))},insert:function(t,e){var n="function"==typeof t?t:ne(t),r=null==e?re:"function"==typeof e?e:ft(e);return this.select((function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)}))},remove:function(){return this.each(ie)},clone:function(t){return this.select(t?oe:ae)},datum:function(t){return arguments.length?this.property("__data__",t):this.node().__data__},on:function(t,e,n){var r,i,a=he(t+""),o=a.length;if(!(arguments.length<2)){for(s=e?de:fe,null==n&&(n=!1),r=0;r<o;++r)this.each(s(a[r],e,n));return this}var s=this.node().__on;if(s)for(var c,u=0,l=s.length;u<l;++u)for(r=0,c=s[u];r<o;++r)if((i=a[r]).type===c.type&&i.name===c.name)return c.value},dispatch:function(t,e){return this.each(("function"==typeof e?ve:ye)(t,e))}};var _e=xe,ke=function(t){return"string"==typeof t?new be([[document.querySelector(t)]],[document.documentElement]):new be([[t]],me)};function we(){ce.stopImmediatePropagation()}var Ee=function(){ce.preventDefault(),ce.stopImmediatePropagation()},Te=function(t){var e=t.document.documentElement,n=ke(t).on("dragstart.drag",Ee,!0);"onselectstart"in e?n.on("selectstart.drag",Ee,!0):(e.__noselect=e.style.MozUserSelect,e.style.MozUserSelect="none")};function Ce(t,e){var n=t.document.documentElement,r=ke(t).on("dragstart.drag",null);e&&(r.on("click.drag",Ee,!0),setTimeout((function(){r.on("click.drag",null)}),0)),"onselectstart"in n?r.on("selectstart.drag",null):(n.style.MozUserSelect=n.__noselect,delete n.__noselect)}var Ae=function(t,e,n){t.prototype=e.prototype=n,n.constructor=t};function Se(t,e){var n=Object.create(t.prototype);for(var r in e)n[r]=e[r];return n}function Me(){}var Oe="\\s*([+-]?\\d+)\\s*",De="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",Ne="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",Be=/^#([0-9a-f]{3,8})$/,Le=new RegExp("^rgb\\("+[Oe,Oe,Oe]+"\\)$"),Pe=new RegExp("^rgb\\("+[Ne,Ne,Ne]+"\\)$"),Ie=new RegExp("^rgba\\("+[Oe,Oe,Oe,De]+"\\)$"),Fe=new RegExp("^rgba\\("+[Ne,Ne,Ne,De]+"\\)$"),je=new RegExp("^hsl\\("+[De,Ne,Ne]+"\\)$"),Re=new RegExp("^hsla\\("+[De,Ne,Ne,De]+"\\)$"),Ye={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function ze(){return this.rgb().formatHex()}function Ue(){return this.rgb().formatRgb()}function $e(t){var e,n;return t=(t+"").trim().toLowerCase(),(e=Be.exec(t))?(n=e[1].length,e=parseInt(e[1],16),6===n?We(e):3===n?new qe(e>>8&15|e>>4&240,e>>4&15|240&e,(15&e)<<4|15&e,1):8===n?new qe(e>>24&255,e>>16&255,e>>8&255,(255&e)/255):4===n?new qe(e>>12&15|e>>8&240,e>>8&15|e>>4&240,e>>4&15|240&e,((15&e)<<4|15&e)/255):null):(e=Le.exec(t))?new qe(e[1],e[2],e[3],1):(e=Pe.exec(t))?new qe(255*e[1]/100,255*e[2]/100,255*e[3]/100,1):(e=Ie.exec(t))?He(e[1],e[2],e[3],e[4]):(e=Fe.exec(t))?He(255*e[1]/100,255*e[2]/100,255*e[3]/100,e[4]):(e=je.exec(t))?Ke(e[1],e[2]/100,e[3]/100,1):(e=Re.exec(t))?Ke(e[1],e[2]/100,e[3]/100,e[4]):Ye.hasOwnProperty(t)?We(Ye[t]):"transparent"===t?new qe(NaN,NaN,NaN,0):null}function We(t){return new qe(t>>16&255,t>>8&255,255&t,1)}function He(t,e,n,r){return r<=0&&(t=e=n=NaN),new qe(t,e,n,r)}function Ve(t){return t instanceof Me||(t=$e(t)),t?new qe((t=t.rgb()).r,t.g,t.b,t.opacity):new qe}function Ge(t,e,n,r){return 1===arguments.length?Ve(t):new qe(t,e,n,null==r?1:r)}function qe(t,e,n,r){this.r=+t,this.g=+e,this.b=+n,this.opacity=+r}function Xe(){return"#"+Je(this.r)+Je(this.g)+Je(this.b)}function Ze(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function Je(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function Ke(t,e,n,r){return r<=0?t=e=n=NaN:n<=0||n>=1?t=e=NaN:e<=0&&(t=NaN),new en(t,e,n,r)}function Qe(t){if(t instanceof en)return new en(t.h,t.s,t.l,t.opacity);if(t instanceof Me||(t=$e(t)),!t)return new en;if(t instanceof en)return t;var e=(t=t.rgb()).r/255,n=t.g/255,r=t.b/255,i=Math.min(e,n,r),a=Math.max(e,n,r),o=NaN,s=a-i,c=(a+i)/2;return s?(o=e===a?(n-r)/s+6*(n<r):n===a?(r-e)/s+2:(e-n)/s+4,s/=c<.5?a+i:2-a-i,o*=60):s=c>0&&c<1?0:o,new en(o,s,c,t.opacity)}function tn(t,e,n,r){return 1===arguments.length?Qe(t):new en(t,e,n,null==r?1:r)}function en(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}function nn(t,e,n){return 255*(t<60?e+(n-e)*t/60:t<180?n:t<240?e+(n-e)*(240-t)/60:e)}function rn(t,e,n,r,i){var a=t*t,o=a*t;return((1-3*t+3*a-o)*e+(4-6*a+3*o)*n+(1+3*t+3*a-3*o)*r+o*i)/6}Ae(Me,$e,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:ze,formatHex:ze,formatHsl:function(){return Qe(this).formatHsl()},formatRgb:Ue,toString:Ue}),Ae(qe,Ge,Se(Me,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new qe(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Xe,formatHex:Xe,formatRgb:Ze,toString:Ze})),Ae(en,tn,Se(Me,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new en(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new en(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),e=isNaN(t)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*e,i=2*n-r;return new qe(nn(t>=240?t-240:t+120,i,r),nn(t,i,r),nn(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));var an=function(t){var e=t.length-1;return function(n){var r=n<=0?n=0:n>=1?(n=1,e-1):Math.floor(n*e),i=t[r],a=t[r+1],o=r>0?t[r-1]:2*i-a,s=r<e-1?t[r+2]:2*a-i;return rn((n-r/e)*e,o,i,a,s)}},on=function(t){var e=t.length;return function(n){var r=Math.floor(((n%=1)<0?++n:n)*e),i=t[(r+e-1)%e],a=t[r%e],o=t[(r+1)%e],s=t[(r+2)%e];return rn((n-r/e)*e,i,a,o,s)}},sn=function(t){return function(){return t}};function cn(t,e){return function(n){return t+n*e}}function un(t,e){var n=e-t;return n?cn(t,n>180||n<-180?n-360*Math.round(n/360):n):sn(isNaN(t)?e:t)}function ln(t){return 1==(t=+t)?hn:function(e,n){return n-e?function(t,e,n){return t=Math.pow(t,n),e=Math.pow(e,n)-t,n=1/n,function(r){return Math.pow(t+r*e,n)}}(e,n,t):sn(isNaN(e)?n:e)}}function hn(t,e){var n=e-t;return n?cn(t,n):sn(isNaN(t)?e:t)}var fn=function t(e){var n=ln(e);function r(t,e){var r=n((t=Ge(t)).r,(e=Ge(e)).r),i=n(t.g,e.g),a=n(t.b,e.b),o=hn(t.opacity,e.opacity);return function(e){return t.r=r(e),t.g=i(e),t.b=a(e),t.opacity=o(e),t+""}}return r.gamma=t,r}(1);function dn(t){return function(e){var n,r,i=e.length,a=new Array(i),o=new Array(i),s=new Array(i);for(n=0;n<i;++n)r=Ge(e[n]),a[n]=r.r||0,o[n]=r.g||0,s[n]=r.b||0;return a=t(a),o=t(o),s=t(s),r.opacity=1,function(t){return r.r=a(t),r.g=o(t),r.b=s(t),r+""}}}var pn=dn(an),gn=dn(on),yn=function(t,e){e||(e=[]);var n,r=t?Math.min(e.length,t.length):0,i=e.slice();return function(a){for(n=0;n<r;++n)i[n]=t[n]*(1-a)+e[n]*a;return i}};function vn(t){return ArrayBuffer.isView(t)&&!(t instanceof DataView)}var mn=function(t,e){return(vn(e)?yn:bn)(t,e)};function bn(t,e){var n,r=e?e.length:0,i=t?Math.min(r,t.length):0,a=new Array(i),o=new Array(r);for(n=0;n<i;++n)a[n]=Sn(t[n],e[n]);for(;n<r;++n)o[n]=e[n];return function(t){for(n=0;n<i;++n)o[n]=a[n](t);return o}}var xn=function(t,e){var n=new Date;return t=+t,e=+e,function(r){return n.setTime(t*(1-r)+e*r),n}},_n=function(t,e){return t=+t,e=+e,function(n){return t*(1-n)+e*n}},kn=function(t,e){var n,r={},i={};for(n in null!==t&&"object"==typeof t||(t={}),null!==e&&"object"==typeof e||(e={}),e)n in t?r[n]=Sn(t[n],e[n]):i[n]=e[n];return function(t){for(n in r)i[n]=r[n](t);return i}},wn=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,En=new RegExp(wn.source,"g");var Tn,Cn,An=function(t,e){var n,r,i,a=wn.lastIndex=En.lastIndex=0,o=-1,s=[],c=[];for(t+="",e+="";(n=wn.exec(t))&&(r=En.exec(e));)(i=r.index)>a&&(i=e.slice(a,i),s[o]?s[o]+=i:s[++o]=i),(n=n[0])===(r=r[0])?s[o]?s[o]+=r:s[++o]=r:(s[++o]=null,c.push({i:o,x:_n(n,r)})),a=En.lastIndex;return a<e.length&&(i=e.slice(a),s[o]?s[o]+=i:s[++o]=i),s.length<2?c[0]?function(t){return function(e){return t(e)+""}}(c[0].x):function(t){return function(){return t}}(e):(e=c.length,function(t){for(var n,r=0;r<e;++r)s[(n=c[r]).i]=n.x(t);return s.join("")})},Sn=function(t,e){var n,r=typeof e;return null==e||"boolean"===r?sn(e):("number"===r?_n:"string"===r?(n=$e(e))?(e=n,fn):An:e instanceof $e?fn:e instanceof Date?xn:vn(e)?yn:Array.isArray(e)?bn:"function"!=typeof e.valueOf&&"function"!=typeof e.toString||isNaN(e)?kn:_n)(t,e)},Mn=function(){for(var t,e=ce;t=e.sourceEvent;)e=t;return e},On=function(t,e){var n=t.ownerSVGElement||t;if(n.createSVGPoint){var r=n.createSVGPoint();return r.x=e.clientX,r.y=e.clientY,[(r=r.matrixTransform(t.getScreenCTM().inverse())).x,r.y]}var i=t.getBoundingClientRect();return[e.clientX-i.left-t.clientLeft,e.clientY-i.top-t.clientTop]},Dn=function(t,e,n){arguments.length<3&&(n=e,e=Mn().changedTouches);for(var r,i=0,a=e?e.length:0;i<a;++i)if((r=e[i]).identifier===n)return On(t,r);return null},Nn=function(t){var e=Mn();return e.changedTouches&&(e=e.changedTouches[0]),On(t,e)},Bn=0,Ln=0,Pn=0,In=0,Fn=0,jn=0,Rn="object"==typeof performance&&performance.now?performance:Date,Yn="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(t){setTimeout(t,17)};function zn(){return Fn||(Yn(Un),Fn=Rn.now()+jn)}function Un(){Fn=0}function $n(){this._call=this._time=this._next=null}function Wn(t,e,n){var r=new $n;return r.restart(t,e,n),r}function Hn(){zn(),++Bn;for(var t,e=Tn;e;)(t=Fn-e._time)>=0&&e._call.call(null,t),e=e._next;--Bn}function Vn(){Fn=(In=Rn.now())+jn,Bn=Ln=0;try{Hn()}finally{Bn=0,function(){var t,e,n=Tn,r=1/0;for(;n;)n._call?(r>n._time&&(r=n._time),t=n,n=n._next):(e=n._next,n._next=null,n=t?t._next=e:Tn=e);Cn=t,qn(r)}(),Fn=0}}function Gn(){var t=Rn.now(),e=t-In;e>1e3&&(jn-=e,In=t)}function qn(t){Bn||(Ln&&(Ln=clearTimeout(Ln)),t-Fn>24?(t<1/0&&(Ln=setTimeout(Vn,t-Rn.now()-jn)),Pn&&(Pn=clearInterval(Pn))):(Pn||(In=Rn.now(),Pn=setInterval(Gn,1e3)),Bn=1,Yn(Vn)))}$n.prototype=Wn.prototype={constructor:$n,restart:function(t,e,n){if("function"!=typeof t)throw new TypeError("callback is not a function");n=(null==n?zn():+n)+(null==e?0:+e),this._next||Cn===this||(Cn?Cn._next=this:Tn=this,Cn=this),this._call=t,this._time=n,qn()},stop:function(){this._call&&(this._call=null,this._time=1/0,qn())}};var Xn=function(t,e,n){var r=new $n;return e=null==e?0:+e,r.restart((function(n){r.stop(),t(n+e)}),e,n),r},Zn=lt("start","end","cancel","interrupt"),Jn=[],Kn=function(t,e,n,r,i,a){var o=t.__transition;if(o){if(n in o)return}else t.__transition={};!function(t,e,n){var r,i=t.__transition;function a(c){var u,l,h,f;if(1!==n.state)return s();for(u in i)if((f=i[u]).name===n.name){if(3===f.state)return Xn(a);4===f.state?(f.state=6,f.timer.stop(),f.on.call("interrupt",t,t.__data__,f.index,f.group),delete i[u]):+u<e&&(f.state=6,f.timer.stop(),f.on.call("cancel",t,t.__data__,f.index,f.group),delete i[u])}if(Xn((function(){3===n.state&&(n.state=4,n.timer.restart(o,n.delay,n.time),o(c))})),n.state=2,n.on.call("start",t,t.__data__,n.index,n.group),2===n.state){for(n.state=3,r=new Array(h=n.tween.length),u=0,l=-1;u<h;++u)(f=n.tween[u].value.call(t,t.__data__,n.index,n.group))&&(r[++l]=f);r.length=l+1}}function o(e){for(var i=e<n.duration?n.ease.call(null,e/n.duration):(n.timer.restart(s),n.state=5,1),a=-1,o=r.length;++a<o;)r[a].call(t,i);5===n.state&&(n.on.call("end",t,t.__data__,n.index,n.group),s())}function s(){for(var r in n.state=6,n.timer.stop(),delete i[e],i)return;delete t.__transition}i[e]=n,n.timer=Wn((function(t){n.state=1,n.timer.restart(a,n.delay,n.time),n.delay<=t&&a(t-n.delay)}),0,n.time)}(t,n,{name:e,index:r,group:i,on:Zn,tween:Jn,time:a.time,delay:a.delay,duration:a.duration,ease:a.ease,timer:null,state:0})};function Qn(t,e){var n=er(t,e);if(n.state>0)throw new Error("too late; already scheduled");return n}function tr(t,e){var n=er(t,e);if(n.state>3)throw new Error("too late; already running");return n}function er(t,e){var n=t.__transition;if(!n||!(n=n[e]))throw new Error("transition not found");return n}var nr,rr,ir,ar,or=function(t,e){var n,r,i,a=t.__transition,o=!0;if(a){for(i in e=null==e?null:e+"",a)(n=a[i]).name===e?(r=n.state>2&&n.state<5,n.state=6,n.timer.stop(),n.on.call(r?"interrupt":"cancel",t,t.__data__,n.index,n.group),delete a[i]):o=!1;o&&delete t.__transition}},sr=180/Math.PI,cr={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1},ur=function(t,e,n,r,i,a){var o,s,c;return(o=Math.sqrt(t*t+e*e))&&(t/=o,e/=o),(c=t*n+e*r)&&(n-=t*c,r-=e*c),(s=Math.sqrt(n*n+r*r))&&(n/=s,r/=s,c/=s),t*r<e*n&&(t=-t,e=-e,c=-c,o=-o),{translateX:i,translateY:a,rotate:Math.atan2(e,t)*sr,skewX:Math.atan(c)*sr,scaleX:o,scaleY:s}};function lr(t,e,n,r){function i(t){return t.length?t.pop()+" ":""}return function(a,o){var s=[],c=[];return a=t(a),o=t(o),function(t,r,i,a,o,s){if(t!==i||r!==a){var c=o.push("translate(",null,e,null,n);s.push({i:c-4,x:_n(t,i)},{i:c-2,x:_n(r,a)})}else(i||a)&&o.push("translate("+i+e+a+n)}(a.translateX,a.translateY,o.translateX,o.translateY,s,c),function(t,e,n,a){t!==e?(t-e>180?e+=360:e-t>180&&(t+=360),a.push({i:n.push(i(n)+"rotate(",null,r)-2,x:_n(t,e)})):e&&n.push(i(n)+"rotate("+e+r)}(a.rotate,o.rotate,s,c),function(t,e,n,a){t!==e?a.push({i:n.push(i(n)+"skewX(",null,r)-2,x:_n(t,e)}):e&&n.push(i(n)+"skewX("+e+r)}(a.skewX,o.skewX,s,c),function(t,e,n,r,a,o){if(t!==n||e!==r){var s=a.push(i(a)+"scale(",null,",",null,")");o.push({i:s-4,x:_n(t,n)},{i:s-2,x:_n(e,r)})}else 1===n&&1===r||a.push(i(a)+"scale("+n+","+r+")")}(a.scaleX,a.scaleY,o.scaleX,o.scaleY,s,c),a=o=null,function(t){for(var e,n=-1,r=c.length;++n<r;)s[(e=c[n]).i]=e.x(t);return s.join("")}}}var hr=lr((function(t){return"none"===t?cr:(nr||(nr=document.createElement("DIV"),rr=document.documentElement,ir=document.defaultView),nr.style.transform=t,t=ir.getComputedStyle(rr.appendChild(nr),null).getPropertyValue("transform"),rr.removeChild(nr),t=t.slice(7,-1).split(","),ur(+t[0],+t[1],+t[2],+t[3],+t[4],+t[5]))}),"px, ","px)","deg)"),fr=lr((function(t){return null==t?cr:(ar||(ar=document.createElementNS("http://www.w3.org/2000/svg","g")),ar.setAttribute("transform",t),(t=ar.transform.baseVal.consolidate())?(t=t.matrix,ur(t.a,t.b,t.c,t.d,t.e,t.f)):cr)}),", ",")",")");function dr(t,e){var n,r;return function(){var i=tr(this,t),a=i.tween;if(a!==n)for(var o=0,s=(r=n=a).length;o<s;++o)if(r[o].name===e){(r=r.slice()).splice(o,1);break}i.tween=r}}function pr(t,e,n){var r,i;if("function"!=typeof n)throw new Error;return function(){var a=tr(this,t),o=a.tween;if(o!==r){i=(r=o).slice();for(var s={name:e,value:n},c=0,u=i.length;c<u;++c)if(i[c].name===e){i[c]=s;break}c===u&&i.push(s)}a.tween=i}}function gr(t,e,n){var r=t._id;return t.each((function(){var t=tr(this,r);(t.value||(t.value={}))[e]=n.apply(this,arguments)})),function(t){return er(t,r).value[e]}}var yr=function(t,e){var n;return("number"==typeof e?_n:e instanceof $e?fn:(n=$e(e))?(e=n,fn):An)(t,e)};function vr(t){return function(){this.removeAttribute(t)}}function mr(t){return function(){this.removeAttributeNS(t.space,t.local)}}function br(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttribute(t);return o===a?null:o===r?i:i=e(r=o,n)}}function xr(t,e,n){var r,i,a=n+"";return function(){var o=this.getAttributeNS(t.space,t.local);return o===a?null:o===r?i:i=e(r=o,n)}}function _r(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttribute(t))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttribute(t)}}function kr(t,e,n){var r,i,a;return function(){var o,s,c=n(this);if(null!=c)return(o=this.getAttributeNS(t.space,t.local))===(s=c+"")?null:o===r&&s===i?a:(i=s,a=e(r=o,c));this.removeAttributeNS(t.space,t.local)}}function wr(t,e){return function(n){this.setAttribute(t,e.call(this,n))}}function Er(t,e){return function(n){this.setAttributeNS(t.space,t.local,e.call(this,n))}}function Tr(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&Er(t,i)),n}return i._value=e,i}function Cr(t,e){var n,r;function i(){var i=e.apply(this,arguments);return i!==r&&(n=(r=i)&&wr(t,i)),n}return i._value=e,i}function Ar(t,e){return function(){Qn(this,t).delay=+e.apply(this,arguments)}}function Sr(t,e){return e=+e,function(){Qn(this,t).delay=e}}function Mr(t,e){return function(){tr(this,t).duration=+e.apply(this,arguments)}}function Or(t,e){return e=+e,function(){tr(this,t).duration=e}}function Dr(t,e){if("function"!=typeof e)throw new Error;return function(){tr(this,t).ease=e}}function Nr(t,e,n){var r,i,a=function(t){return(t+"").trim().split(/^|\s+/).every((function(t){var e=t.indexOf(".");return e>=0&&(t=t.slice(0,e)),!t||"start"===t}))}(e)?Qn:tr;return function(){var o=a(this,t),s=o.on;s!==r&&(i=(r=s).copy()).on(e,n),o.on=i}}var Br=_e.prototype.constructor;function Lr(t){return function(){this.style.removeProperty(t)}}function Pr(t,e,n){return function(r){this.style.setProperty(t,e.call(this,r),n)}}function Ir(t,e,n){var r,i;function a(){var a=e.apply(this,arguments);return a!==i&&(r=(i=a)&&Pr(t,a,n)),r}return a._value=e,a}function Fr(t){return function(e){this.textContent=t.call(this,e)}}function jr(t){var e,n;function r(){var r=t.apply(this,arguments);return r!==n&&(e=(n=r)&&Fr(r)),e}return r._value=t,r}var Rr=0;function Yr(t,e,n,r){this._groups=t,this._parents=e,this._name=n,this._id=r}function zr(t){return _e().transition(t)}function Ur(){return++Rr}var $r=_e.prototype;function Wr(t){return t*t*t}function Hr(t){return--t*t*t+1}function Vr(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}Yr.prototype=zr.prototype={constructor:Yr,select:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=ft(t));for(var r=this._groups,i=r.length,a=new Array(i),o=0;o<i;++o)for(var s,c,u=r[o],l=u.length,h=a[o]=new Array(l),f=0;f<l;++f)(s=u[f])&&(c=t.call(s,s.__data__,f,u))&&("__data__"in s&&(c.__data__=s.__data__),h[f]=c,Kn(h[f],e,n,f,h,er(s,n)));return new Yr(a,this._parents,e,n)},selectAll:function(t){var e=this._name,n=this._id;"function"!=typeof t&&(t=pt(t));for(var r=this._groups,i=r.length,a=[],o=[],s=0;s<i;++s)for(var c,u=r[s],l=u.length,h=0;h<l;++h)if(c=u[h]){for(var f,d=t.call(c,c.__data__,h,u),p=er(c,n),g=0,y=d.length;g<y;++g)(f=d[g])&&Kn(f,e,n,g,d,p);a.push(d),o.push(c)}return new Yr(a,o,e,n)},filter:function(t){"function"!=typeof t&&(t=gt(t));for(var e=this._groups,n=e.length,r=new Array(n),i=0;i<n;++i)for(var a,o=e[i],s=o.length,c=r[i]=[],u=0;u<s;++u)(a=o[u])&&t.call(a,a.__data__,u,o)&&c.push(a);return new Yr(r,this._parents,this._name,this._id)},merge:function(t){if(t._id!==this._id)throw new Error;for(var e=this._groups,n=t._groups,r=e.length,i=n.length,a=Math.min(r,i),o=new Array(r),s=0;s<a;++s)for(var c,u=e[s],l=n[s],h=u.length,f=o[s]=new Array(h),d=0;d<h;++d)(c=u[d]||l[d])&&(f[d]=c);for(;s<r;++s)o[s]=e[s];return new Yr(o,this._parents,this._name,this._id)},selection:function(){return new Br(this._groups,this._parents)},transition:function(){for(var t=this._name,e=this._id,n=Ur(),r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)if(o=s[u]){var l=er(o,e);Kn(o,t,n,u,s,{time:l.time+l.delay+l.duration,delay:0,duration:l.duration,ease:l.ease})}return new Yr(r,this._parents,t,n)},call:$r.call,nodes:$r.nodes,node:$r.node,size:$r.size,empty:$r.empty,each:$r.each,on:function(t,e){var n=this._id;return arguments.length<2?er(this.node(),n).on.on(t):this.each(Nr(n,t,e))},attr:function(t,e){var n=wt(t),r="transform"===n?fr:yr;return this.attrTween(t,"function"==typeof e?(n.local?kr:_r)(n,r,gr(this,"attr."+t,e)):null==e?(n.local?mr:vr)(n):(n.local?xr:br)(n,r,e))},attrTween:function(t,e){var n="attr."+t;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(null==e)return this.tween(n,null);if("function"!=typeof e)throw new Error;var r=wt(t);return this.tween(n,(r.local?Tr:Cr)(r,e))},style:function(t,e,n){var r="transform"==(t+="")?hr:yr;return null==e?this.styleTween(t,function(t,e){var n,r,i;return function(){var a=Lt(this,t),o=(this.style.removeProperty(t),Lt(this,t));return a===o?null:a===n&&o===r?i:i=e(n=a,r=o)}}(t,r)).on("end.style."+t,Lr(t)):"function"==typeof e?this.styleTween(t,function(t,e,n){var r,i,a;return function(){var o=Lt(this,t),s=n(this),c=s+"";return null==s&&(this.style.removeProperty(t),c=s=Lt(this,t)),o===c?null:o===r&&c===i?a:(i=c,a=e(r=o,s))}}(t,r,gr(this,"style."+t,e))).each(function(t,e){var n,r,i,a,o="style."+e,s="end."+o;return function(){var c=tr(this,t),u=c.on,l=null==c.value[o]?a||(a=Lr(e)):void 0;u===n&&i===l||(r=(n=u).copy()).on(s,i=l),c.on=r}}(this._id,t)):this.styleTween(t,function(t,e,n){var r,i,a=n+"";return function(){var o=Lt(this,t);return o===a?null:o===r?i:i=e(r=o,n)}}(t,r,e),n).on("end.style."+t,null)},styleTween:function(t,e,n){var r="style."+(t+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(null==e)return this.tween(r,null);if("function"!=typeof e)throw new Error;return this.tween(r,Ir(t,e,null==n?"":n))},text:function(t){return this.tween("text","function"==typeof t?function(t){return function(){var e=t(this);this.textContent=null==e?"":e}}(gr(this,"text",t)):function(t){return function(){this.textContent=t}}(null==t?"":t+""))},textTween:function(t){var e="text";if(arguments.length<1)return(e=this.tween(e))&&e._value;if(null==t)return this.tween(e,null);if("function"!=typeof t)throw new Error;return this.tween(e,jr(t))},remove:function(){return this.on("end.remove",(t=this._id,function(){var e=this.parentNode;for(var n in this.__transition)if(+n!==t)return;e&&e.removeChild(this)}));var t},tween:function(t,e){var n=this._id;if(t+="",arguments.length<2){for(var r,i=er(this.node(),n).tween,a=0,o=i.length;a<o;++a)if((r=i[a]).name===t)return r.value;return null}return this.each((null==e?dr:pr)(n,t,e))},delay:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Ar:Sr)(e,t)):er(this.node(),e).delay},duration:function(t){var e=this._id;return arguments.length?this.each(("function"==typeof t?Mr:Or)(e,t)):er(this.node(),e).duration},ease:function(t){var e=this._id;return arguments.length?this.each(Dr(e,t)):er(this.node(),e).ease},end:function(){var t,e,n=this,r=n._id,i=n.size();return new Promise((function(a,o){var s={value:o},c={value:function(){0==--i&&a()}};n.each((function(){var n=tr(this,r),i=n.on;i!==t&&((e=(t=i).copy())._.cancel.push(s),e._.interrupt.push(s),e._.end.push(c)),n.on=e}))}))}};var Gr={time:null,delay:0,duration:250,ease:Vr};function qr(t,e){for(var n;!(n=t.__transition)||!(n=n[e]);)if(!(t=t.parentNode))return Gr.time=zn(),Gr;return n}_e.prototype.interrupt=function(t){return this.each((function(){or(this,t)}))},_e.prototype.transition=function(t){var e,n;t instanceof Yr?(e=t._id,t=t._name):(e=Ur(),(n=Gr).time=zn(),t=null==t?null:t+"");for(var r=this._groups,i=r.length,a=0;a<i;++a)for(var o,s=r[a],c=s.length,u=0;u<c;++u)(o=s[u])&&Kn(o,t,e,u,s,n||qr(o,e));return new Yr(r,this._parents,t,e)};var Xr=[null],Zr=function(t,e){var n,r,i=t.__transition;if(i)for(r in e=null==e?null:e+"",i)if((n=i[r]).state>1&&n.name===e)return new Yr([[t]],Xr,e,+r);return null},Jr=function(t){return function(){return t}},Kr=function(t,e,n){this.target=t,this.type=e,this.selection=n};function Qr(){ce.stopImmediatePropagation()}var ti=function(){ce.preventDefault(),ce.stopImmediatePropagation()},ei={name:"drag"},ni={name:"space"},ri={name:"handle"},ii={name:"center"};function ai(t){return[+t[0],+t[1]]}function oi(t){return[ai(t[0]),ai(t[1])]}function si(t){return function(e){return Dn(e,ce.touches,t)}}var ci={name:"x",handles:["w","e"].map(yi),input:function(t,e){return null==t?null:[[+t[0],e[0][1]],[+t[1],e[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},ui={name:"y",handles:["n","s"].map(yi),input:function(t,e){return null==t?null:[[e[0][0],+t[0]],[e[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},li={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(yi),input:function(t){return null==t?null:oi(t)},output:function(t){return t}},hi={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},fi={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},di={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},pi={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},gi={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function yi(t){return{type:t}}function vi(){return!ce.ctrlKey&&!ce.button}function mi(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function bi(){return navigator.maxTouchPoints||"ontouchstart"in this}function xi(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function _i(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function ki(t){var e=t.__brush;return e?e.dim.output(e.selection):null}function wi(){return Ci(ci)}function Ei(){return Ci(ui)}var Ti=function(){return Ci(li)};function Ci(t){var e,n=mi,r=vi,i=bi,a=!0,o=lt("start","brush","end"),s=6;function c(e){var n=e.property("__brush",g).selectAll(".overlay").data([yi("overlay")]);n.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",hi.overlay).merge(n).each((function(){var t=xi(this).extent;ke(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),e.selectAll(".selection").data([yi("selection")]).enter().append("rect").attr("class","selection").attr("cursor",hi.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=e.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return hi[t.type]})),e.each(u).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",f).filter(i).on("touchstart.brush",f).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function u(){var t=ke(this),e=xi(this).selection;e?(t.selectAll(".selection").style("display",null).attr("x",e[0][0]).attr("y",e[0][1]).attr("width",e[1][0]-e[0][0]).attr("height",e[1][1]-e[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?e[1][0]-s/2:e[0][0]-s/2})).attr("y",(function(t){return"s"===t.type[0]?e[1][1]-s/2:e[0][1]-s/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?e[1][0]-e[0][0]+s:s})).attr("height",(function(t){return"e"===t.type||"w"===t.type?e[1][1]-e[0][1]+s:s}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function l(t,e,n){return!n&&t.__brush.emitter||new h(t,e)}function h(t,e){this.that=t,this.args=e,this.state=t.__brush,this.active=0}function f(){if((!e||ce.touches)&&r.apply(this,arguments)){var n,i,o,s,c,h,f,d,p,g,y,v=this,m=ce.target.__data__.type,b="selection"===(a&&ce.metaKey?m="overlay":m)?ei:a&&ce.altKey?ii:ri,x=t===ui?null:pi[m],_=t===ci?null:gi[m],k=xi(v),w=k.extent,E=k.selection,T=w[0][0],C=w[0][1],A=w[1][0],S=w[1][1],M=0,O=0,D=x&&_&&a&&ce.shiftKey,N=ce.touches?si(ce.changedTouches[0].identifier):Nn,B=N(v),L=B,P=l(v,arguments,!0).beforestart();"overlay"===m?(E&&(p=!0),k.selection=E=[[n=t===ui?T:B[0],o=t===ci?C:B[1]],[c=t===ui?A:n,f=t===ci?S:o]]):(n=E[0][0],o=E[0][1],c=E[1][0],f=E[1][1]),i=n,s=o,h=c,d=f;var I=ke(v).attr("pointer-events","none"),F=I.selectAll(".overlay").attr("cursor",hi[m]);if(ce.touches)P.moved=R,P.ended=z;else{var j=ke(ce.view).on("mousemove.brush",R,!0).on("mouseup.brush",z,!0);a&&j.on("keydown.brush",U,!0).on("keyup.brush",$,!0),Te(ce.view)}Qr(),or(v),u.call(v),P.start()}function R(){var t=N(v);!D||g||y||(Math.abs(t[0]-L[0])>Math.abs(t[1]-L[1])?y=!0:g=!0),L=t,p=!0,ti(),Y()}function Y(){var t;switch(M=L[0]-B[0],O=L[1]-B[1],b){case ni:case ei:x&&(M=Math.max(T-n,Math.min(A-c,M)),i=n+M,h=c+M),_&&(O=Math.max(C-o,Math.min(S-f,O)),s=o+O,d=f+O);break;case ri:x<0?(M=Math.max(T-n,Math.min(A-n,M)),i=n+M,h=c):x>0&&(M=Math.max(T-c,Math.min(A-c,M)),i=n,h=c+M),_<0?(O=Math.max(C-o,Math.min(S-o,O)),s=o+O,d=f):_>0&&(O=Math.max(C-f,Math.min(S-f,O)),s=o,d=f+O);break;case ii:x&&(i=Math.max(T,Math.min(A,n-M*x)),h=Math.max(T,Math.min(A,c+M*x))),_&&(s=Math.max(C,Math.min(S,o-O*_)),d=Math.max(C,Math.min(S,f+O*_)))}h<i&&(x*=-1,t=n,n=c,c=t,t=i,i=h,h=t,m in fi&&F.attr("cursor",hi[m=fi[m]])),d<s&&(_*=-1,t=o,o=f,f=t,t=s,s=d,d=t,m in di&&F.attr("cursor",hi[m=di[m]])),k.selection&&(E=k.selection),g&&(i=E[0][0],h=E[1][0]),y&&(s=E[0][1],d=E[1][1]),E[0][0]===i&&E[0][1]===s&&E[1][0]===h&&E[1][1]===d||(k.selection=[[i,s],[h,d]],u.call(v),P.brush())}function z(){if(Qr(),ce.touches){if(ce.touches.length)return;e&&clearTimeout(e),e=setTimeout((function(){e=null}),500)}else Ce(ce.view,p),j.on("keydown.brush keyup.brush mousemove.brush mouseup.brush",null);I.attr("pointer-events","all"),F.attr("cursor",hi.overlay),k.selection&&(E=k.selection),_i(E)&&(k.selection=null,u.call(v)),P.end()}function U(){switch(ce.keyCode){case 16:D=x&&_;break;case 18:b===ri&&(x&&(c=h-M*x,n=i+M*x),_&&(f=d-O*_,o=s+O*_),b=ii,Y());break;case 32:b!==ri&&b!==ii||(x<0?c=h-M:x>0&&(n=i-M),_<0?f=d-O:_>0&&(o=s-O),b=ni,F.attr("cursor",hi.selection),Y());break;default:return}ti()}function $(){switch(ce.keyCode){case 16:D&&(g=y=D=!1,Y());break;case 18:b===ii&&(x<0?c=h:x>0&&(n=i),_<0?f=d:_>0&&(o=s),b=ri,Y());break;case 32:b===ni&&(ce.altKey?(x&&(c=h-M*x,n=i+M*x),_&&(f=d-O*_,o=s+O*_),b=ii):(x<0?c=h:x>0&&(n=i),_<0?f=d:_>0&&(o=s),b=ri),F.attr("cursor",hi[m]),Y());break;default:return}ti()}}function d(){l(this,arguments).moved()}function p(){l(this,arguments).ended()}function g(){var e=this.__brush||{selection:null};return e.extent=oi(n.apply(this,arguments)),e.dim=t,e}return c.move=function(e,n){e.selection?e.on("start.brush",(function(){l(this,arguments).beforestart().start()})).on("interrupt.brush end.brush",(function(){l(this,arguments).end()})).tween("brush",(function(){var e=this,r=e.__brush,i=l(e,arguments),a=r.selection,o=t.input("function"==typeof n?n.apply(this,arguments):n,r.extent),s=Sn(a,o);function c(t){r.selection=1===t&&null===o?null:s(t),u.call(e),i.brush()}return null!==a&&null!==o?c:c(1)})):e.each((function(){var e=this,r=arguments,i=e.__brush,a=t.input("function"==typeof n?n.apply(e,r):n,i.extent),o=l(e,r).beforestart();or(e),i.selection=null===a?null:a,u.call(e),o.start().brush().end()}))},c.clear=function(t){c.move(t,null)},h.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(){return this.starting?(this.starting=!1,this.emit("start")):this.emit("brush"),this},brush:function(){return this.emit("brush"),this},end:function(){return 0==--this.active&&(delete this.state.emitter,this.emit("end")),this},emit:function(e){pe(new Kr(c,e,t.output(this.state.selection)),o.apply,o,[e,this.that,this.args])}},c.extent=function(t){return arguments.length?(n="function"==typeof t?t:Jr(oi(t)),c):n},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:Jr(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:Jr(!!t),c):i},c.handleSize=function(t){return arguments.length?(s=+t,c):s},c.keyModifiers=function(t){return arguments.length?(a=!!t,c):a},c.on=function(){var t=o.on.apply(o,arguments);return t===o?c:t},c}var Ai=Math.cos,Si=Math.sin,Mi=Math.PI,Oi=Mi/2,Di=2*Mi,Ni=Math.max;function Bi(t){return function(e,n){return t(e.source.value+e.target.value,n.source.value+n.target.value)}}var Li=function(){var t=0,e=null,n=null,r=null;function i(i){var a,o,s,c,u,l,h=i.length,f=[],d=k(h),p=[],g=[],y=g.groups=new Array(h),v=new Array(h*h);for(a=0,u=-1;++u<h;){for(o=0,l=-1;++l<h;)o+=i[u][l];f.push(o),p.push(k(h)),a+=o}for(e&&d.sort((function(t,n){return e(f[t],f[n])})),n&&p.forEach((function(t,e){t.sort((function(t,r){return n(i[e][t],i[e][r])}))})),c=(a=Ni(0,Di-t*h)/a)?t:Di/h,o=0,u=-1;++u<h;){for(s=o,l=-1;++l<h;){var m=d[u],b=p[m][l],x=i[m][b],_=o,w=o+=x*a;v[b*h+m]={index:m,subindex:b,startAngle:_,endAngle:w,value:x}}y[m]={index:m,startAngle:s,endAngle:o,value:f[m]},o+=c}for(u=-1;++u<h;)for(l=u-1;++l<h;){var E=v[l*h+u],T=v[u*h+l];(E.value||T.value)&&g.push(E.value<T.value?{source:T,target:E}:{source:E,target:T})}return r?g.sort(r):g}return i.padAngle=function(e){return arguments.length?(t=Ni(0,e),i):t},i.sortGroups=function(t){return arguments.length?(e=t,i):e},i.sortSubgroups=function(t){return arguments.length?(n=t,i):n},i.sortChords=function(t){return arguments.length?(null==t?r=null:(r=Bi(t))._=t,i):r&&r._},i},Pi=Array.prototype.slice,Ii=function(t){return function(){return t}},Fi=Math.PI,ji=2*Fi,Ri=ji-1e-6;function Yi(){this._x0=this._y0=this._x1=this._y1=null,this._=""}function zi(){return new Yi}Yi.prototype=zi.prototype={constructor:Yi,moveTo:function(t,e){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)},closePath:function(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")},lineTo:function(t,e){this._+="L"+(this._x1=+t)+","+(this._y1=+e)},quadraticCurveTo:function(t,e,n,r){this._+="Q"+ +t+","+ +e+","+(this._x1=+n)+","+(this._y1=+r)},bezierCurveTo:function(t,e,n,r,i,a){this._+="C"+ +t+","+ +e+","+ +n+","+ +r+","+(this._x1=+i)+","+(this._y1=+a)},arcTo:function(t,e,n,r,i){t=+t,e=+e,n=+n,r=+r,i=+i;var a=this._x1,o=this._y1,s=n-t,c=r-e,u=a-t,l=o-e,h=u*u+l*l;if(i<0)throw new Error("negative radius: "+i);if(null===this._x1)this._+="M"+(this._x1=t)+","+(this._y1=e);else if(h>1e-6)if(Math.abs(l*s-c*u)>1e-6&&i){var f=n-a,d=r-o,p=s*s+c*c,g=f*f+d*d,y=Math.sqrt(p),v=Math.sqrt(h),m=i*Math.tan((Fi-Math.acos((p+h-g)/(2*y*v)))/2),b=m/v,x=m/y;Math.abs(b-1)>1e-6&&(this._+="L"+(t+b*u)+","+(e+b*l)),this._+="A"+i+","+i+",0,0,"+ +(l*f>u*d)+","+(this._x1=t+x*s)+","+(this._y1=e+x*c)}else this._+="L"+(this._x1=t)+","+(this._y1=e);else;},arc:function(t,e,n,r,i,a){t=+t,e=+e,a=!!a;var o=(n=+n)*Math.cos(r),s=n*Math.sin(r),c=t+o,u=e+s,l=1^a,h=a?r-i:i-r;if(n<0)throw new Error("negative radius: "+n);null===this._x1?this._+="M"+c+","+u:(Math.abs(this._x1-c)>1e-6||Math.abs(this._y1-u)>1e-6)&&(this._+="L"+c+","+u),n&&(h<0&&(h=h%ji+ji),h>Ri?this._+="A"+n+","+n+",0,1,"+l+","+(t-o)+","+(e-s)+"A"+n+","+n+",0,1,"+l+","+(this._x1=c)+","+(this._y1=u):h>1e-6&&(this._+="A"+n+","+n+",0,"+ +(h>=Fi)+","+l+","+(this._x1=t+n*Math.cos(i))+","+(this._y1=e+n*Math.sin(i))))},rect:function(t,e,n,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+e)+"h"+ +n+"v"+ +r+"h"+-n+"Z"},toString:function(){return this._}};var Ui=zi;function $i(t){return t.source}function Wi(t){return t.target}function Hi(t){return t.radius}function Vi(t){return t.startAngle}function Gi(t){return t.endAngle}var qi=function(){var t=$i,e=Wi,n=Hi,r=Vi,i=Gi,a=null;function o(){var o,s=Pi.call(arguments),c=t.apply(this,s),u=e.apply(this,s),l=+n.apply(this,(s[0]=c,s)),h=r.apply(this,s)-Oi,f=i.apply(this,s)-Oi,d=l*Ai(h),p=l*Si(h),g=+n.apply(this,(s[0]=u,s)),y=r.apply(this,s)-Oi,v=i.apply(this,s)-Oi;if(a||(a=o=Ui()),a.moveTo(d,p),a.arc(0,0,l,h,f),h===y&&f===v||(a.quadraticCurveTo(0,0,g*Ai(y),g*Si(y)),a.arc(0,0,g,y,v)),a.quadraticCurveTo(0,0,d,p),a.closePath(),o)return a=null,o+""||null}return o.radius=function(t){return arguments.length?(n="function"==typeof t?t:Ii(+t),o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:Ii(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:Ii(+t),o):i},o.source=function(e){return arguments.length?(t=e,o):t},o.target=function(t){return arguments.length?(e=t,o):e},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o};function Xi(){}function Zi(t,e){var n=new Xi;if(t instanceof Xi)t.each((function(t,e){n.set(e,t)}));else if(Array.isArray(t)){var r,i=-1,a=t.length;if(null==e)for(;++i<a;)n.set(i,t[i]);else for(;++i<a;)n.set(e(r=t[i],i,t),r)}else if(t)for(var o in t)n.set(o,t[o]);return n}Xi.prototype=Zi.prototype={constructor:Xi,has:function(t){return"$"+t in this},get:function(t){return this["$"+t]},set:function(t,e){return this["$"+t]=e,this},remove:function(t){var e="$"+t;return e in this&&delete this[e]},clear:function(){for(var t in this)"$"===t[0]&&delete this[t]},keys:function(){var t=[];for(var e in this)"$"===e[0]&&t.push(e.slice(1));return t},values:function(){var t=[];for(var e in this)"$"===e[0]&&t.push(this[e]);return t},entries:function(){var t=[];for(var e in this)"$"===e[0]&&t.push({key:e.slice(1),value:this[e]});return t},size:function(){var t=0;for(var e in this)"$"===e[0]&&++t;return t},empty:function(){for(var t in this)if("$"===t[0])return!1;return!0},each:function(t){for(var e in this)"$"===e[0]&&t(this[e],e.slice(1),this)}};var Ji=Zi,Ki=function(){var t,e,n,r=[],i=[];function a(n,i,o,s){if(i>=r.length)return null!=t&&n.sort(t),null!=e?e(n):n;for(var c,u,l,h=-1,f=n.length,d=r[i++],p=Ji(),g=o();++h<f;)(l=p.get(c=d(u=n[h])+""))?l.push(u):p.set(c,[u]);return p.each((function(t,e){s(g,e,a(t,i,o,s))})),g}return n={object:function(t){return a(t,0,Qi,ta)},map:function(t){return a(t,0,ea,na)},entries:function(t){return function t(n,a){if(++a>r.length)return n;var o,s=i[a-1];return null!=e&&a>=r.length?o=n.entries():(o=[],n.each((function(e,n){o.push({key:n,values:t(e,a)})}))),null!=s?o.sort((function(t,e){return s(t.key,e.key)})):o}(a(t,0,ea,na),0)},key:function(t){return r.push(t),n},sortKeys:function(t){return i[r.length-1]=t,n},sortValues:function(e){return t=e,n},rollup:function(t){return e=t,n}}};function Qi(){return{}}function ta(t,e,n){t[e]=n}function ea(){return Ji()}function na(t,e,n){t.set(e,n)}function ra(){}var ia=Ji.prototype;function aa(t,e){var n=new ra;if(t instanceof ra)t.each((function(t){n.add(t)}));else if(t){var r=-1,i=t.length;if(null==e)for(;++r<i;)n.add(t[r]);else for(;++r<i;)n.add(e(t[r],r,t))}return n}ra.prototype=aa.prototype={constructor:ra,has:ia.has,add:function(t){return this["$"+(t+="")]=t,this},remove:ia.remove,clear:ia.clear,values:ia.keys,size:ia.size,empty:ia.empty,each:ia.each};var oa=aa,sa=function(t){var e=[];for(var n in t)e.push(n);return e},ca=function(t){var e=[];for(var n in t)e.push(t[n]);return e},ua=function(t){var e=[];for(var n in t)e.push({key:n,value:t[n]});return e},la=Math.PI/180,ha=180/Math.PI;function fa(t){if(t instanceof ga)return new ga(t.l,t.a,t.b,t.opacity);if(t instanceof wa)return Ea(t);t instanceof qe||(t=Ve(t));var e,n,r=ba(t.r),i=ba(t.g),a=ba(t.b),o=ya((.2225045*r+.7168786*i+.0606169*a)/1);return r===i&&i===a?e=n=o:(e=ya((.4360747*r+.3850649*i+.1430804*a)/.96422),n=ya((.0139322*r+.0971045*i+.7141733*a)/.82521)),new ga(116*o-16,500*(e-o),200*(o-n),t.opacity)}function da(t,e){return new ga(t,0,0,null==e?1:e)}function pa(t,e,n,r){return 1===arguments.length?fa(t):new ga(t,e,n,null==r?1:r)}function ga(t,e,n,r){this.l=+t,this.a=+e,this.b=+n,this.opacity=+r}function ya(t){return t>6/29*(6/29)*(6/29)?Math.pow(t,1/3):t/(6/29*3*(6/29))+4/29}function va(t){return t>6/29?t*t*t:6/29*3*(6/29)*(t-4/29)}function ma(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function ba(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function xa(t){if(t instanceof wa)return new wa(t.h,t.c,t.l,t.opacity);if(t instanceof ga||(t=fa(t)),0===t.a&&0===t.b)return new wa(NaN,0<t.l&&t.l<100?0:NaN,t.l,t.opacity);var e=Math.atan2(t.b,t.a)*ha;return new wa(e<0?e+360:e,Math.sqrt(t.a*t.a+t.b*t.b),t.l,t.opacity)}function _a(t,e,n,r){return 1===arguments.length?xa(t):new wa(n,e,t,null==r?1:r)}function ka(t,e,n,r){return 1===arguments.length?xa(t):new wa(t,e,n,null==r?1:r)}function wa(t,e,n,r){this.h=+t,this.c=+e,this.l=+n,this.opacity=+r}function Ea(t){if(isNaN(t.h))return new ga(t.l,0,0,t.opacity);var e=t.h*la;return new ga(t.l,Math.cos(e)*t.c,Math.sin(e)*t.c,t.opacity)}Ae(ga,pa,Se(Me,{brighter:function(t){return new ga(this.l+18*(null==t?1:t),this.a,this.b,this.opacity)},darker:function(t){return new ga(this.l-18*(null==t?1:t),this.a,this.b,this.opacity)},rgb:function(){var t=(this.l+16)/116,e=isNaN(this.a)?t:t+this.a/500,n=isNaN(this.b)?t:t-this.b/200;return new qe(ma(3.1338561*(e=.96422*va(e))-1.6168667*(t=1*va(t))-.4906146*(n=.82521*va(n))),ma(-.9787684*e+1.9161415*t+.033454*n),ma(.0719453*e-.2289914*t+1.4052427*n),this.opacity)}})),Ae(wa,ka,Se(Me,{brighter:function(t){return new wa(this.h,this.c,this.l+18*(null==t?1:t),this.opacity)},darker:function(t){return new wa(this.h,this.c,this.l-18*(null==t?1:t),this.opacity)},rgb:function(){return Ea(this).rgb()}}));var Ta=-.29227,Ca=-1.7884503806,Aa=3.5172982438,Sa=-.6557636667999999;function Ma(t){if(t instanceof Da)return new Da(t.h,t.s,t.l,t.opacity);t instanceof qe||(t=Ve(t));var e=t.r/255,n=t.g/255,r=t.b/255,i=(Sa*r+Ca*e-Aa*n)/(Sa+Ca-Aa),a=r-i,o=(1.97294*(n-i)-Ta*a)/-.90649,s=Math.sqrt(o*o+a*a)/(1.97294*i*(1-i)),c=s?Math.atan2(o,a)*ha-120:NaN;return new Da(c<0?c+360:c,s,i,t.opacity)}function Oa(t,e,n,r){return 1===arguments.length?Ma(t):new Da(t,e,n,null==r?1:r)}function Da(t,e,n,r){this.h=+t,this.s=+e,this.l=+n,this.opacity=+r}Ae(Da,Oa,Se(Me,{brighter:function(t){return t=null==t?1/.7:Math.pow(1/.7,t),new Da(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?.7:Math.pow(.7,t),new Da(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=isNaN(this.h)?0:(this.h+120)*la,e=+this.l,n=isNaN(this.s)?0:this.s*e*(1-e),r=Math.cos(t),i=Math.sin(t);return new qe(255*(e+n*(-.14861*r+1.78277*i)),255*(e+n*(Ta*r+-.90649*i)),255*(e+n*(1.97294*r)),this.opacity)}}));var Na=Array.prototype.slice,Ba=function(t,e){return t-e},La=function(t){return function(){return t}},Pa=function(t,e){for(var n,r=-1,i=e.length;++r<i;)if(n=Ia(t,e[r]))return n;return 0};function Ia(t,e){for(var n=e[0],r=e[1],i=-1,a=0,o=t.length,s=o-1;a<o;s=a++){var c=t[a],u=c[0],l=c[1],h=t[s],f=h[0],d=h[1];if(Fa(c,h,e))return 0;l>r!=d>r&&n<(f-u)*(r-l)/(d-l)+u&&(i=-i)}return i}function Fa(t,e,n){var r,i,a,o;return function(t,e,n){return(e[0]-t[0])*(n[1]-t[1])==(n[0]-t[0])*(e[1]-t[1])}(t,e,n)&&(i=t[r=+(t[0]===e[0])],a=n[r],o=e[r],i<=a&&a<=o||o<=a&&a<=i)}var ja=function(){},Ra=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]],Ya=function(){var t=1,e=1,n=M,r=s;function i(t){var e=n(t);if(Array.isArray(e))e=e.slice().sort(Ba);else{var r=y(t),i=r[0],o=r[1];e=S(i,o,e),e=k(Math.floor(i/e)*e,Math.floor(o/e)*e,e)}return e.map((function(e){return a(t,e)}))}function a(n,i){var a=[],s=[];return function(n,r,i){var a,s,c,u,l,h,f=new Array,d=new Array;a=s=-1,u=n[0]>=r,Ra[u<<1].forEach(p);for(;++a<t-1;)c=u,u=n[a+1]>=r,Ra[c|u<<1].forEach(p);Ra[u<<0].forEach(p);for(;++s<e-1;){for(a=-1,u=n[s*t+t]>=r,l=n[s*t]>=r,Ra[u<<1|l<<2].forEach(p);++a<t-1;)c=u,u=n[s*t+t+a+1]>=r,h=l,l=n[s*t+a+1]>=r,Ra[c|u<<1|l<<2|h<<3].forEach(p);Ra[u|l<<3].forEach(p)}a=-1,l=n[s*t]>=r,Ra[l<<2].forEach(p);for(;++a<t-1;)h=l,l=n[s*t+a+1]>=r,Ra[l<<2|h<<3].forEach(p);function p(t){var e,n,r=[t[0][0]+a,t[0][1]+s],c=[t[1][0]+a,t[1][1]+s],u=o(r),l=o(c);(e=d[u])?(n=f[l])?(delete d[e.end],delete f[n.start],e===n?(e.ring.push(c),i(e.ring)):f[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete d[e.end],e.ring.push(c),d[e.end=l]=e):(e=f[l])?(n=d[u])?(delete f[e.start],delete d[n.end],e===n?(e.ring.push(c),i(e.ring)):f[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete f[e.start],e.ring.unshift(r),f[e.start=u]=e):f[u]=d[l]={start:u,end:l,ring:[r,c]}}Ra[l<<3].forEach(p)}(n,i,(function(t){r(t,n,i),function(t){for(var e=0,n=t.length,r=t[n-1][1]*t[0][0]-t[n-1][0]*t[0][1];++e<n;)r+=t[e-1][1]*t[e][0]-t[e-1][0]*t[e][1];return r}(t)>0?a.push([t]):s.push(t)})),s.forEach((function(t){for(var e,n=0,r=a.length;n<r;++n)if(-1!==Pa((e=a[n])[0],t))return void e.push(t)})),{type:"MultiPolygon",value:i,coordinates:a}}function o(e){return 2*e[0]+e[1]*(t+1)*4}function s(n,r,i){n.forEach((function(n){var a,o=n[0],s=n[1],c=0|o,u=0|s,l=r[u*t+c];o>0&&o<t&&c===o&&(a=r[u*t+c-1],n[0]=o+(i-a)/(l-a)-.5),s>0&&s<e&&u===s&&(a=r[(u-1)*t+c],n[1]=s+(i-a)/(l-a)-.5)}))}return i.contour=a,i.size=function(n){if(!arguments.length)return[t,e];var r=Math.ceil(n[0]),a=Math.ceil(n[1]);if(!(r>0&&a>0))throw new Error("invalid size");return t=r,e=a,i},i.thresholds=function(t){return arguments.length?(n="function"==typeof t?t:Array.isArray(t)?La(Na.call(t)):La(t),i):n},i.smooth=function(t){return arguments.length?(r=t?s:ja,i):r===s},i};function za(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<i;++o)for(var s=0,c=0;s<r+n;++s)s<r&&(c+=t.data[s+o*r]),s>=n&&(s>=a&&(c-=t.data[s-a+o*r]),e.data[s-n+o*r]=c/Math.min(s+1,r-1+a-s,a))}function Ua(t,e,n){for(var r=t.width,i=t.height,a=1+(n<<1),o=0;o<r;++o)for(var s=0,c=0;s<i+n;++s)s<i&&(c+=t.data[o+s*r]),s>=n&&(s>=a&&(c-=t.data[o+(s-a)*r]),e.data[o+(s-n)*r]=c/Math.min(s+1,i-1+a-s,a))}function $a(t){return t[0]}function Wa(t){return t[1]}function Ha(){return 1}var Va=function(){var t=$a,e=Wa,n=Ha,r=960,i=500,a=20,o=2,s=3*a,c=r+2*s>>o,u=i+2*s>>o,l=La(20);function h(r){var i=new Float32Array(c*u),h=new Float32Array(c*u);r.forEach((function(r,a,l){var h=+t(r,a,l)+s>>o,f=+e(r,a,l)+s>>o,d=+n(r,a,l);h>=0&&h<c&&f>=0&&f<u&&(i[h+f*c]+=d)})),za({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),Ua({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),za({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),Ua({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o),za({width:c,height:u,data:i},{width:c,height:u,data:h},a>>o),Ua({width:c,height:u,data:h},{width:c,height:u,data:i},a>>o);var d=l(i);if(!Array.isArray(d)){var p=L(i);d=S(0,p,d),(d=k(0,Math.floor(p/d)*d,d)).shift()}return Ya().thresholds(d).size([c,u])(i).map(f)}function f(t){return t.value*=Math.pow(2,-2*o),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(g)}function g(t){t[0]=t[0]*Math.pow(2,o)-s,t[1]=t[1]*Math.pow(2,o)-s}function y(){return c=r+2*(s=3*a)>>o,u=i+2*s>>o,h}return h.x=function(e){return arguments.length?(t="function"==typeof e?e:La(+e),h):t},h.y=function(t){return arguments.length?(e="function"==typeof t?t:La(+t),h):e},h.weight=function(t){return arguments.length?(n="function"==typeof t?t:La(+t),h):n},h.size=function(t){if(!arguments.length)return[r,i];var e=Math.ceil(t[0]),n=Math.ceil(t[1]);if(!(e>=0||e>=0))throw new Error("invalid size");return r=e,i=n,y()},h.cellSize=function(t){if(!arguments.length)return 1<<o;if(!((t=+t)>=1))throw new Error("invalid cell size");return o=Math.floor(Math.log(t)/Math.LN2),y()},h.thresholds=function(t){return arguments.length?(l="function"==typeof t?t:Array.isArray(t)?La(Na.call(t)):La(t),h):l},h.bandwidth=function(t){if(!arguments.length)return Math.sqrt(a*(a+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return a=Math.round((Math.sqrt(4*t*t+1)-1)/2),y()},h},Ga=function(t){return function(){return t}};function qa(t,e,n,r,i,a,o,s,c,u){this.target=t,this.type=e,this.subject=n,this.identifier=r,this.active=i,this.x=a,this.y=o,this.dx=s,this.dy=c,this._=u}function Xa(){return!ce.ctrlKey&&!ce.button}function Za(){return this.parentNode}function Ja(t){return null==t?{x:ce.x,y:ce.y}:t}function Ka(){return navigator.maxTouchPoints||"ontouchstart"in this}qa.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var Qa=function(){var t,e,n,r,i=Xa,a=Za,o=Ja,s=Ka,c={},u=lt("start","drag","end"),l=0,h=0;function f(t){t.on("mousedown.drag",d).filter(s).on("touchstart.drag",y).on("touchmove.drag",v).on("touchend.drag touchcancel.drag",m).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(){if(!r&&i.apply(this,arguments)){var o=b("mouse",a.apply(this,arguments),Nn,this,arguments);o&&(ke(ce.view).on("mousemove.drag",p,!0).on("mouseup.drag",g,!0),Te(ce.view),we(),n=!1,t=ce.clientX,e=ce.clientY,o("start"))}}function p(){if(Ee(),!n){var r=ce.clientX-t,i=ce.clientY-e;n=r*r+i*i>h}c.mouse("drag")}function g(){ke(ce.view).on("mousemove.drag mouseup.drag",null),Ce(ce.view,n),Ee(),c.mouse("end")}function y(){if(i.apply(this,arguments)){var t,e,n=ce.changedTouches,r=a.apply(this,arguments),o=n.length;for(t=0;t<o;++t)(e=b(n[t].identifier,r,Dn,this,arguments))&&(we(),e("start"))}}function v(){var t,e,n=ce.changedTouches,r=n.length;for(t=0;t<r;++t)(e=c[n[t].identifier])&&(Ee(),e("drag"))}function m(){var t,e,n=ce.changedTouches,i=n.length;for(r&&clearTimeout(r),r=setTimeout((function(){r=null}),500),t=0;t<i;++t)(e=c[n[t].identifier])&&(we(),e("end"))}function b(t,e,n,r,i){var a,s,h,d=n(e,t),p=u.copy();if(pe(new qa(f,"beforestart",a,t,l,d[0],d[1],0,0,p),(function(){return null!=(ce.subject=a=o.apply(r,i))&&(s=a.x-d[0]||0,h=a.y-d[1]||0,!0)})))return function o(u){var g,y=d;switch(u){case"start":c[t]=o,g=l++;break;case"end":delete c[t],--l;case"drag":d=n(e,t),g=l}pe(new qa(f,u,a,t,g,d[0]+s,d[1]+h,d[0]-y[0],d[1]-y[1],p),p.apply,p,[u,r,i])}}return f.filter=function(t){return arguments.length?(i="function"==typeof t?t:Ga(!!t),f):i},f.container=function(t){return arguments.length?(a="function"==typeof t?t:Ga(t),f):a},f.subject=function(t){return arguments.length?(o="function"==typeof t?t:Ga(t),f):o},f.touchable=function(t){return arguments.length?(s="function"==typeof t?t:Ga(!!t),f):s},f.on=function(){var t=u.on.apply(u,arguments);return t===u?f:t},f.clickDistance=function(t){return arguments.length?(h=(t=+t)*t,f):Math.sqrt(h)},f},to={},eo={};function no(t){return new Function("d","return {"+t.map((function(t,e){return JSON.stringify(t)+": d["+e+'] || ""'})).join(",")+"}")}function ro(t){var e=Object.create(null),n=[];return t.forEach((function(t){for(var r in t)r in e||n.push(e[r]=r)})),n}function io(t,e){var n=t+"",r=n.length;return r<e?new Array(e-r+1).join(0)+n:n}function ao(t){var e,n=t.getUTCHours(),r=t.getUTCMinutes(),i=t.getUTCSeconds(),a=t.getUTCMilliseconds();return isNaN(t)?"Invalid Date":((e=t.getUTCFullYear())<0?"-"+io(-e,6):e>9999?"+"+io(e,6):io(e,4))+"-"+io(t.getUTCMonth()+1,2)+"-"+io(t.getUTCDate(),2)+(a?"T"+io(n,2)+":"+io(r,2)+":"+io(i,2)+"."+io(a,3)+"Z":i?"T"+io(n,2)+":"+io(r,2)+":"+io(i,2)+"Z":r||n?"T"+io(n,2)+":"+io(r,2)+"Z":"")}var oo=function(t){var e=new RegExp('["'+t+"\n\r]"),n=t.charCodeAt(0);function r(t,e){var r,i=[],a=t.length,o=0,s=0,c=a<=0,u=!1;function l(){if(c)return eo;if(u)return u=!1,to;var e,r,i=o;if(34===t.charCodeAt(i)){for(;o++<a&&34!==t.charCodeAt(o)||34===t.charCodeAt(++o););return(e=o)>=a?c=!0:10===(r=t.charCodeAt(o++))?u=!0:13===r&&(u=!0,10===t.charCodeAt(o)&&++o),t.slice(i+1,e-1).replace(/""/g,'"')}for(;o<a;){if(10===(r=t.charCodeAt(e=o++)))u=!0;else if(13===r)u=!0,10===t.charCodeAt(o)&&++o;else if(r!==n)continue;return t.slice(i,e)}return c=!0,t.slice(i,a)}for(10===t.charCodeAt(a-1)&&--a,13===t.charCodeAt(a-1)&&--a;(r=l())!==eo;){for(var h=[];r!==to&&r!==eo;)h.push(r),r=l();e&&null==(h=e(h,s++))||i.push(h)}return i}function i(e,n){return e.map((function(e){return n.map((function(t){return o(e[t])})).join(t)}))}function a(e){return e.map(o).join(t)}function o(t){return null==t?"":t instanceof Date?ao(t):e.test(t+="")?'"'+t.replace(/"/g,'""')+'"':t}return{parse:function(t,e){var n,i,a=r(t,(function(t,r){if(n)return n(t,r-1);i=t,n=e?function(t,e){var n=no(t);return function(r,i){return e(n(r),i,t)}}(t,e):no(t)}));return a.columns=i||[],a},parseRows:r,format:function(e,n){return null==n&&(n=ro(e)),[n.map(o).join(t)].concat(i(e,n)).join("\n")},formatBody:function(t,e){return null==e&&(e=ro(t)),i(t,e).join("\n")},formatRows:function(t){return t.map(a).join("\n")},formatRow:a,formatValue:o}},so=oo(","),co=so.parse,uo=so.parseRows,lo=so.format,ho=so.formatBody,fo=so.formatRows,po=so.formatRow,go=so.formatValue,yo=oo("\t"),vo=yo.parse,mo=yo.parseRows,bo=yo.format,xo=yo.formatBody,_o=yo.formatRows,ko=yo.formatRow,wo=yo.formatValue;function Eo(t){for(var e in t){var n,r,i=t[e].trim();if(i)if("true"===i)i=!0;else if("false"===i)i=!1;else if("NaN"===i)i=NaN;else if(isNaN(n=+i)){if(!(r=i.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/)))continue;To&&r[4]&&!r[7]&&(i=i.replace(/-/g,"/").replace(/T/," ")),i=new Date(i)}else i=n;else i=null;t[e]=i}return t}var To=new Date("2019-01-01T00:00").getHours()||new Date("2019-07-01T00:00").getHours();function Co(t){return+t}function Ao(t){return t*t}function So(t){return t*(2-t)}function Mo(t){return((t*=2)<=1?t*t:--t*(2-t)+1)/2}var Oo=function t(e){function n(t){return Math.pow(t,e)}return e=+e,n.exponent=t,n}(3),Do=function t(e){function n(t){return 1-Math.pow(1-t,e)}return e=+e,n.exponent=t,n}(3),No=function t(e){function n(t){return((t*=2)<=1?Math.pow(t,e):2-Math.pow(2-t,e))/2}return e=+e,n.exponent=t,n}(3),Bo=Math.PI,Lo=Bo/2;function Po(t){return 1-Math.cos(t*Lo)}function Io(t){return Math.sin(t*Lo)}function Fo(t){return(1-Math.cos(Bo*t))/2}function jo(t){return Math.pow(2,10*t-10)}function Ro(t){return 1-Math.pow(2,-10*t)}function Yo(t){return((t*=2)<=1?Math.pow(2,10*t-10):2-Math.pow(2,10-10*t))/2}function zo(t){return 1-Math.sqrt(1-t*t)}function Uo(t){return Math.sqrt(1- --t*t)}function $o(t){return((t*=2)<=1?1-Math.sqrt(1-t*t):Math.sqrt(1-(t-=2)*t)+1)/2}function Wo(t){return 1-Ho(1-t)}function Ho(t){return(t=+t)<4/11?7.5625*t*t:t<8/11?7.5625*(t-=6/11)*t+.75:t<10/11?7.5625*(t-=9/11)*t+.9375:7.5625*(t-=21/22)*t+63/64}function Vo(t){return((t*=2)<=1?1-Ho(1-t):Ho(t-1)+1)/2}var Go=function t(e){function n(t){return t*t*((e+1)*t-e)}return e=+e,n.overshoot=t,n}(1.70158),qo=function t(e){function n(t){return--t*t*((e+1)*t+e)+1}return e=+e,n.overshoot=t,n}(1.70158),Xo=function t(e){function n(t){return((t*=2)<1?t*t*((e+1)*t-e):(t-=2)*t*((e+1)*t+e)+2)/2}return e=+e,n.overshoot=t,n}(1.70158),Zo=2*Math.PI,Jo=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=Zo);function i(t){return e*Math.pow(2,10*--t)*Math.sin((r-t)/n)}return i.amplitude=function(e){return t(e,n*Zo)},i.period=function(n){return t(e,n)},i}(1,.3),Ko=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=Zo);function i(t){return 1-e*Math.pow(2,-10*(t=+t))*Math.sin((t+r)/n)}return i.amplitude=function(e){return t(e,n*Zo)},i.period=function(n){return t(e,n)},i}(1,.3),Qo=function t(e,n){var r=Math.asin(1/(e=Math.max(1,e)))*(n/=Zo);function i(t){return((t=2*t-1)<0?e*Math.pow(2,10*t)*Math.sin((r-t)/n):2-e*Math.pow(2,-10*t)*Math.sin((r+t)/n))/2}return i.amplitude=function(e){return t(e,n*Zo)},i.period=function(n){return t(e,n)},i}(1,.3);function ts(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.blob()}var es=function(t,e){return fetch(t,e).then(ts)};function ns(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.arrayBuffer()}var rs=function(t,e){return fetch(t,e).then(ns)};function is(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.text()}var as=function(t,e){return fetch(t,e).then(is)};function os(t){return function(e,n,r){return 2===arguments.length&&"function"==typeof n&&(r=n,n=void 0),as(e,n).then((function(e){return t(e,r)}))}}function ss(t,e,n,r){3===arguments.length&&"function"==typeof n&&(r=n,n=void 0);var i=oo(t);return as(e,n).then((function(t){return i.parse(t,r)}))}var cs=os(co),us=os(vo),ls=function(t,e){return new Promise((function(n,r){var i=new Image;for(var a in e)i[a]=e[a];i.onerror=r,i.onload=function(){n(i)},i.src=t}))};function hs(t){if(!t.ok)throw new Error(t.status+" "+t.statusText);return t.json()}var fs=function(t,e){return fetch(t,e).then(hs)};function ds(t){return function(e,n){return as(e,n).then((function(e){return(new DOMParser).parseFromString(e,t)}))}}var ps=ds("application/xml"),gs=ds("text/html"),ys=ds("image/svg+xml"),vs=function(t,e){var n;function r(){var r,i,a=n.length,o=0,s=0;for(r=0;r<a;++r)o+=(i=n[r]).x,s+=i.y;for(o=o/a-t,s=s/a-e,r=0;r<a;++r)(i=n[r]).x-=o,i.y-=s}return null==t&&(t=0),null==e&&(e=0),r.initialize=function(t){n=t},r.x=function(e){return arguments.length?(t=+e,r):t},r.y=function(t){return arguments.length?(e=+t,r):e},r},ms=function(t){return function(){return t}},bs=function(){return 1e-6*(Math.random()-.5)};function xs(t,e,n,r){if(isNaN(e)||isNaN(n))return t;var i,a,o,s,c,u,l,h,f,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,m=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((u=e>=(a=(g+v)/2))?g=a:v=a,(l=n>=(o=(y+m)/2))?y=o:m=o,i=d,!(d=d[h=l<<1|u]))return i[h]=p,t;if(s=+t._x.call(null,d.data),c=+t._y.call(null,d.data),e===s&&n===c)return p.next=d,i?i[h]=p:t._root=p,t;do{i=i?i[h]=new Array(4):t._root=new Array(4),(u=e>=(a=(g+v)/2))?g=a:v=a,(l=n>=(o=(y+m)/2))?y=o:m=o}while((h=l<<1|u)==(f=(c>=o)<<1|s>=a));return i[f]=d,i[h]=p,t}var _s=function(t,e,n,r,i){this.node=t,this.x0=e,this.y0=n,this.x1=r,this.y1=i};function ks(t){return t[0]}function ws(t){return t[1]}function Es(t,e,n){var r=new Ts(null==e?ks:e,null==n?ws:n,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function Ts(t,e,n,r,i,a){this._x=t,this._y=e,this._x0=n,this._y0=r,this._x1=i,this._y1=a,this._root=void 0}function Cs(t){for(var e={data:t.data},n=e;t=t.next;)n=n.next={data:t.data};return e}var As=Es.prototype=Ts.prototype;function Ss(t){return t.x+t.vx}function Ms(t){return t.y+t.vy}As.copy=function(){var t,e,n=new Ts(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return n;if(!r.length)return n._root=Cs(r),n;for(t=[{source:r,target:n._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(e=r.source[i])&&(e.length?t.push({source:e,target:r.target[i]=new Array(4)}):r.target[i]=Cs(e));return n},As.add=function(t){var e=+this._x.call(null,t),n=+this._y.call(null,t);return xs(this.cover(e,n),e,n,t)},As.addAll=function(t){var e,n,r,i,a=t.length,o=new Array(a),s=new Array(a),c=1/0,u=1/0,l=-1/0,h=-1/0;for(n=0;n<a;++n)isNaN(r=+this._x.call(null,e=t[n]))||isNaN(i=+this._y.call(null,e))||(o[n]=r,s[n]=i,r<c&&(c=r),r>l&&(l=r),i<u&&(u=i),i>h&&(h=i));if(c>l||u>h)return this;for(this.cover(c,u).cover(l,h),n=0;n<a;++n)xs(this,o[n],s[n],t[n]);return this},As.cover=function(t,e){if(isNaN(t=+t)||isNaN(e=+e))return this;var n=this._x0,r=this._y0,i=this._x1,a=this._y1;if(isNaN(n))i=(n=Math.floor(t))+1,a=(r=Math.floor(e))+1;else{for(var o,s,c=i-n,u=this._root;n>t||t>=i||r>e||e>=a;)switch(s=(e<r)<<1|t<n,(o=new Array(4))[s]=u,u=o,c*=2,s){case 0:i=n+c,a=r+c;break;case 1:n=i-c,a=r+c;break;case 2:i=n+c,r=a-c;break;case 3:n=i-c,r=a-c}this._root&&this._root.length&&(this._root=u)}return this._x0=n,this._y0=r,this._x1=i,this._y1=a,this},As.data=function(){var t=[];return this.visit((function(e){if(!e.length)do{t.push(e.data)}while(e=e.next)})),t},As.extent=function(t){return arguments.length?this.cover(+t[0][0],+t[0][1]).cover(+t[1][0],+t[1][1]):isNaN(this._x0)?void 0:[[this._x0,this._y0],[this._x1,this._y1]]},As.find=function(t,e,n){var r,i,a,o,s,c,u,l=this._x0,h=this._y0,f=this._x1,d=this._y1,p=[],g=this._root;for(g&&p.push(new _s(g,l,h,f,d)),null==n?n=1/0:(l=t-n,h=e-n,f=t+n,d=e+n,n*=n);c=p.pop();)if(!(!(g=c.node)||(i=c.x0)>f||(a=c.y0)>d||(o=c.x1)<l||(s=c.y1)<h))if(g.length){var y=(i+o)/2,v=(a+s)/2;p.push(new _s(g[3],y,v,o,s),new _s(g[2],i,v,y,s),new _s(g[1],y,a,o,v),new _s(g[0],i,a,y,v)),(u=(e>=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-u],p[p.length-1-u]=c)}else{var m=t-+this._x.call(null,g.data),b=e-+this._y.call(null,g.data),x=m*m+b*b;if(x<n){var _=Math.sqrt(n=x);l=t-_,h=e-_,f=t+_,d=e+_,r=g.data}}return r},As.remove=function(t){if(isNaN(a=+this._x.call(null,t))||isNaN(o=+this._y.call(null,t)))return this;var e,n,r,i,a,o,s,c,u,l,h,f,d=this._root,p=this._x0,g=this._y0,y=this._x1,v=this._y1;if(!d)return this;if(d.length)for(;;){if((u=a>=(s=(p+y)/2))?p=s:y=s,(l=o>=(c=(g+v)/2))?g=c:v=c,e=d,!(d=d[h=l<<1|u]))return this;if(!d.length)break;(e[h+1&3]||e[h+2&3]||e[h+3&3])&&(n=e,f=h)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):e?(i?e[h]=i:delete e[h],(d=e[0]||e[1]||e[2]||e[3])&&d===(e[3]||e[2]||e[1]||e[0])&&!d.length&&(n?n[f]=d:this._root=d),this):(this._root=i,this)},As.removeAll=function(t){for(var e=0,n=t.length;e<n;++e)this.remove(t[e]);return this},As.root=function(){return this._root},As.size=function(){var t=0;return this.visit((function(e){if(!e.length)do{++t}while(e=e.next)})),t},As.visit=function(t){var e,n,r,i,a,o,s=[],c=this._root;for(c&&s.push(new _s(c,this._x0,this._y0,this._x1,this._y1));e=s.pop();)if(!t(c=e.node,r=e.x0,i=e.y0,a=e.x1,o=e.y1)&&c.length){var u=(r+a)/2,l=(i+o)/2;(n=c[3])&&s.push(new _s(n,u,l,a,o)),(n=c[2])&&s.push(new _s(n,r,l,u,o)),(n=c[1])&&s.push(new _s(n,u,i,a,l)),(n=c[0])&&s.push(new _s(n,r,i,u,l))}return this},As.visitAfter=function(t){var e,n=[],r=[];for(this._root&&n.push(new _s(this._root,this._x0,this._y0,this._x1,this._y1));e=n.pop();){var i=e.node;if(i.length){var a,o=e.x0,s=e.y0,c=e.x1,u=e.y1,l=(o+c)/2,h=(s+u)/2;(a=i[0])&&n.push(new _s(a,o,s,l,h)),(a=i[1])&&n.push(new _s(a,l,s,c,h)),(a=i[2])&&n.push(new _s(a,o,h,l,u)),(a=i[3])&&n.push(new _s(a,l,h,c,u))}r.push(e)}for(;e=r.pop();)t(e.node,e.x0,e.y0,e.x1,e.y1);return this},As.x=function(t){return arguments.length?(this._x=t,this):this._x},As.y=function(t){return arguments.length?(this._y=t,this):this._y};var Os=function(t){var e,n,r=1,i=1;function a(){for(var t,a,s,c,u,l,h,f=e.length,d=0;d<i;++d)for(a=Es(e,Ss,Ms).visitAfter(o),t=0;t<f;++t)s=e[t],l=n[s.index],h=l*l,c=s.x+s.vx,u=s.y+s.vy,a.visit(p);function p(t,e,n,i,a){var o=t.data,f=t.r,d=l+f;if(!o)return e>c+d||i<c-d||n>u+d||a<u-d;if(o.index>s.index){var p=c-o.x-o.vx,g=u-o.y-o.vy,y=p*p+g*g;y<d*d&&(0===p&&(y+=(p=bs())*p),0===g&&(y+=(g=bs())*g),y=(d-(y=Math.sqrt(y)))/y*r,s.vx+=(p*=y)*(d=(f*=f)/(h+f)),s.vy+=(g*=y)*d,o.vx-=p*(d=1-d),o.vy-=g*d)}}}function o(t){if(t.data)return t.r=n[t.data.index];for(var e=t.r=0;e<4;++e)t[e]&&t[e].r>t.r&&(t.r=t[e].r)}function s(){if(e){var r,i,a=e.length;for(n=new Array(a),r=0;r<a;++r)i=e[r],n[i.index]=+t(i,r,e)}}return"function"!=typeof t&&(t=ms(null==t?1:+t)),a.initialize=function(t){e=t,s()},a.iterations=function(t){return arguments.length?(i=+t,a):i},a.strength=function(t){return arguments.length?(r=+t,a):r},a.radius=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),s(),a):t},a};function Ds(t){return t.index}function Ns(t,e){var n=t.get(e);if(!n)throw new Error("missing: "+e);return n}var Bs=function(t){var e,n,r,i,a,o=Ds,s=function(t){return 1/Math.min(i[t.source.index],i[t.target.index])},c=ms(30),u=1;function l(r){for(var i=0,o=t.length;i<u;++i)for(var s,c,l,h,f,d,p,g=0;g<o;++g)c=(s=t[g]).source,h=(l=s.target).x+l.vx-c.x-c.vx||bs(),f=l.y+l.vy-c.y-c.vy||bs(),h*=d=((d=Math.sqrt(h*h+f*f))-n[g])/d*r*e[g],f*=d,l.vx-=h*(p=a[g]),l.vy-=f*p,c.vx+=h*(p=1-p),c.vy+=f*p}function h(){if(r){var s,c,u=r.length,l=t.length,h=Ji(r,o);for(s=0,i=new Array(u);s<l;++s)(c=t[s]).index=s,"object"!=typeof c.source&&(c.source=Ns(h,c.source)),"object"!=typeof c.target&&(c.target=Ns(h,c.target)),i[c.source.index]=(i[c.source.index]||0)+1,i[c.target.index]=(i[c.target.index]||0)+1;for(s=0,a=new Array(l);s<l;++s)c=t[s],a[s]=i[c.source.index]/(i[c.source.index]+i[c.target.index]);e=new Array(l),f(),n=new Array(l),d()}}function f(){if(r)for(var n=0,i=t.length;n<i;++n)e[n]=+s(t[n],n,t)}function d(){if(r)for(var e=0,i=t.length;e<i;++e)n[e]=+c(t[e],e,t)}return null==t&&(t=[]),l.initialize=function(t){r=t,h()},l.links=function(e){return arguments.length?(t=e,h(),l):t},l.id=function(t){return arguments.length?(o=t,l):o},l.iterations=function(t){return arguments.length?(u=+t,l):u},l.strength=function(t){return arguments.length?(s="function"==typeof t?t:ms(+t),f(),l):s},l.distance=function(t){return arguments.length?(c="function"==typeof t?t:ms(+t),d(),l):c},l};function Ls(t){return t.x}function Ps(t){return t.y}var Is=Math.PI*(3-Math.sqrt(5)),Fs=function(t){var e,n=1,r=.001,i=1-Math.pow(r,1/300),a=0,o=.6,s=Ji(),c=Wn(l),u=lt("tick","end");function l(){h(),u.call("tick",e),n<r&&(c.stop(),u.call("end",e))}function h(r){var c,u,l=t.length;void 0===r&&(r=1);for(var h=0;h<r;++h)for(n+=(a-n)*i,s.each((function(t){t(n)})),c=0;c<l;++c)null==(u=t[c]).fx?u.x+=u.vx*=o:(u.x=u.fx,u.vx=0),null==u.fy?u.y+=u.vy*=o:(u.y=u.fy,u.vy=0);return e}function f(){for(var e,n=0,r=t.length;n<r;++n){if((e=t[n]).index=n,null!=e.fx&&(e.x=e.fx),null!=e.fy&&(e.y=e.fy),isNaN(e.x)||isNaN(e.y)){var i=10*Math.sqrt(n),a=n*Is;e.x=i*Math.cos(a),e.y=i*Math.sin(a)}(isNaN(e.vx)||isNaN(e.vy))&&(e.vx=e.vy=0)}}function d(e){return e.initialize&&e.initialize(t),e}return null==t&&(t=[]),f(),e={tick:h,restart:function(){return c.restart(l),e},stop:function(){return c.stop(),e},nodes:function(n){return arguments.length?(t=n,f(),s.each(d),e):t},alpha:function(t){return arguments.length?(n=+t,e):n},alphaMin:function(t){return arguments.length?(r=+t,e):r},alphaDecay:function(t){return arguments.length?(i=+t,e):+i},alphaTarget:function(t){return arguments.length?(a=+t,e):a},velocityDecay:function(t){return arguments.length?(o=1-t,e):1-o},force:function(t,n){return arguments.length>1?(null==n?s.remove(t):s.set(t,d(n)),e):s.get(t)},find:function(e,n,r){var i,a,o,s,c,u=0,l=t.length;for(null==r?r=1/0:r*=r,u=0;u<l;++u)(o=(i=e-(s=t[u]).x)*i+(a=n-s.y)*a)<r&&(c=s,r=o);return c},on:function(t,n){return arguments.length>1?(u.on(t,n),e):u.on(t)}}},js=function(){var t,e,n,r,i=ms(-30),a=1,o=1/0,s=.81;function c(r){var i,a=t.length,o=Es(t,Ls,Ps).visitAfter(l);for(n=r,i=0;i<a;++i)e=t[i],o.visit(h)}function u(){if(t){var e,n,a=t.length;for(r=new Array(a),e=0;e<a;++e)n=t[e],r[n.index]=+i(n,e,t)}}function l(t){var e,n,i,a,o,s=0,c=0;if(t.length){for(i=a=o=0;o<4;++o)(e=t[o])&&(n=Math.abs(e.value))&&(s+=e.value,c+=n,i+=n*e.x,a+=n*e.y);t.x=i/c,t.y=a/c}else{(e=t).x=e.data.x,e.y=e.data.y;do{s+=r[e.data.index]}while(e=e.next)}t.value=s}function h(t,i,c,u){if(!t.value)return!0;var l=t.x-e.x,h=t.y-e.y,f=u-i,d=l*l+h*h;if(f*f/s<d)return d<o&&(0===l&&(d+=(l=bs())*l),0===h&&(d+=(h=bs())*h),d<a&&(d=Math.sqrt(a*d)),e.vx+=l*t.value*n/d,e.vy+=h*t.value*n/d),!0;if(!(t.length||d>=o)){(t.data!==e||t.next)&&(0===l&&(d+=(l=bs())*l),0===h&&(d+=(h=bs())*h),d<a&&(d=Math.sqrt(a*d)));do{t.data!==e&&(f=r[t.data.index]*n/d,e.vx+=l*f,e.vy+=h*f)}while(t=t.next)}}return c.initialize=function(e){t=e,u()},c.strength=function(t){return arguments.length?(i="function"==typeof t?t:ms(+t),u(),c):i},c.distanceMin=function(t){return arguments.length?(a=t*t,c):Math.sqrt(a)},c.distanceMax=function(t){return arguments.length?(o=t*t,c):Math.sqrt(o)},c.theta=function(t){return arguments.length?(s=t*t,c):Math.sqrt(s)},c},Rs=function(t,e,n){var r,i,a,o=ms(.1);function s(t){for(var o=0,s=r.length;o<s;++o){var c=r[o],u=c.x-e||1e-6,l=c.y-n||1e-6,h=Math.sqrt(u*u+l*l),f=(a[o]-h)*i[o]*t/h;c.vx+=u*f,c.vy+=l*f}}function c(){if(r){var e,n=r.length;for(i=new Array(n),a=new Array(n),e=0;e<n;++e)a[e]=+t(r[e],e,r),i[e]=isNaN(a[e])?0:+o(r[e],e,r)}}return"function"!=typeof t&&(t=ms(+t)),null==e&&(e=0),null==n&&(n=0),s.initialize=function(t){r=t,c()},s.strength=function(t){return arguments.length?(o="function"==typeof t?t:ms(+t),c(),s):o},s.radius=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),c(),s):t},s.x=function(t){return arguments.length?(e=+t,s):e},s.y=function(t){return arguments.length?(n=+t,s):n},s},Ys=function(t){var e,n,r,i=ms(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vx+=(r[a]-i.x)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=ms(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:ms(+t),o(),a):i},a.x=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),o(),a):t},a},zs=function(t){var e,n,r,i=ms(.1);function a(t){for(var i,a=0,o=e.length;a<o;++a)(i=e[a]).vy+=(r[a]-i.y)*n[a]*t}function o(){if(e){var a,o=e.length;for(n=new Array(o),r=new Array(o),a=0;a<o;++a)n[a]=isNaN(r[a]=+t(e[a],a,e))?0:+i(e[a],a,e)}}return"function"!=typeof t&&(t=ms(null==t?0:+t)),a.initialize=function(t){e=t,o()},a.strength=function(t){return arguments.length?(i="function"==typeof t?t:ms(+t),o(),a):i},a.y=function(e){return arguments.length?(t="function"==typeof e?e:ms(+e),o(),a):t},a},Us=function(t,e){if((n=(t=e?t.toExponential(e-1):t.toExponential()).indexOf("e"))<0)return null;var n,r=t.slice(0,n);return[r.length>1?r[0]+r.slice(2):r,+t.slice(n+1)]},$s=function(t){return(t=Us(Math.abs(t)))?t[1]:NaN},Ws=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function Hs(t){if(!(e=Ws.exec(t)))throw new Error("invalid format: "+t);var e;return new Vs({fill:e[1],align:e[2],sign:e[3],symbol:e[4],zero:e[5],width:e[6],comma:e[7],precision:e[8]&&e[8].slice(1),trim:e[9],type:e[10]})}function Vs(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}Hs.prototype=Vs.prototype,Vs.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var Gs,qs,Xs,Zs,Js=function(t,e){var n=Us(t,e);if(!n)return t+"";var r=n[0],i=n[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")},Ks={"%":function(t,e){return(100*t).toFixed(e)},b:function(t){return Math.round(t).toString(2)},c:function(t){return t+""},d:function(t){return Math.round(t).toString(10)},e:function(t,e){return t.toExponential(e)},f:function(t,e){return t.toFixed(e)},g:function(t,e){return t.toPrecision(e)},o:function(t){return Math.round(t).toString(8)},p:function(t,e){return Js(100*t,e)},r:Js,s:function(t,e){var n=Us(t,e);if(!n)return t+"";var r=n[0],i=n[1],a=i-(Gs=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,o=r.length;return a===o?r:a>o?r+new Array(a-o+1).join("0"):a>0?r.slice(0,a)+"."+r.slice(a):"0."+new Array(1-a).join("0")+Us(t,Math.max(0,e+a-1))[0]},X:function(t){return Math.round(t).toString(16).toUpperCase()},x:function(t){return Math.round(t).toString(16)}},Qs=function(t){return t},tc=Array.prototype.map,ec=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"],nc=function(t){var e,n,r=void 0===t.grouping||void 0===t.thousands?Qs:(e=tc.call(t.grouping,Number),n=t.thousands+"",function(t,r){for(var i=t.length,a=[],o=0,s=e[0],c=0;i>0&&s>0&&(c+s+1>r&&(s=Math.max(1,r-c)),a.push(t.substring(i-=s,i+s)),!((c+=s+1)>r));)s=e[o=(o+1)%e.length];return a.reverse().join(n)}),i=void 0===t.currency?"":t.currency[0]+"",a=void 0===t.currency?"":t.currency[1]+"",o=void 0===t.decimal?".":t.decimal+"",s=void 0===t.numerals?Qs:function(t){return function(e){return e.replace(/[0-9]/g,(function(e){return t[+e]}))}}(tc.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",u=void 0===t.minus?"-":t.minus+"",l=void 0===t.nan?"NaN":t.nan+"";function h(t){var e=(t=Hs(t)).fill,n=t.align,h=t.sign,f=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,m=t.type;"n"===m?(g=!0,m="g"):Ks[m]||(void 0===y&&(y=12),v=!0,m="g"),(d||"0"===e&&"="===n)&&(d=!0,e="0",n="=");var b="$"===f?i:"#"===f&&/[boxX]/.test(m)?"0"+m.toLowerCase():"",x="$"===f?a:/[%p]/.test(m)?c:"",_=Ks[m],k=/[defgprs%]/.test(m);function w(t){var i,a,c,f=b,w=x;if("c"===m)w=_(t)+w,t="";else{var E=(t=+t)<0;if(t=isNaN(t)?l:_(Math.abs(t),y),v&&(t=function(t){t:for(var e,n=t.length,r=1,i=-1;r<n;++r)switch(t[r]){case".":i=e=r;break;case"0":0===i&&(i=r),e=r;break;default:if(!+t[r])break t;i>0&&(i=0)}return i>0?t.slice(0,i)+t.slice(e+1):t}(t)),E&&0==+t&&(E=!1),f=(E?"("===h?h:u:"-"===h||"("===h?"":h)+f,w=("s"===m?ec[8+Gs/3]:"")+w+(E&&"("===h?")":""),k)for(i=-1,a=t.length;++i<a;)if(48>(c=t.charCodeAt(i))||c>57){w=(46===c?o+t.slice(i+1):t.slice(i))+w,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var T=f.length+t.length+w.length,C=T<p?new Array(p-T+1).join(e):"";switch(g&&d&&(t=r(C+t,C.length?p-w.length:1/0),C=""),n){case"<":t=f+t+w+C;break;case"=":t=f+C+t+w;break;case"^":t=C.slice(0,T=C.length>>1)+f+t+w+C.slice(T);break;default:t=C+f+t+w}return s(t)}return y=void 0===y?6:/[gprs]/.test(m)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),w.toString=function(){return t+""},w}return{format:h,formatPrefix:function(t,e){var n=h(((t=Hs(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor($s(e)/3))),i=Math.pow(10,-r),a=ec[8+r/3];return function(t){return n(i*t)+a}}}};function rc(t){return qs=nc(t),Xs=qs.format,Zs=qs.formatPrefix,qs}rc({decimal:".",thousands:",",grouping:[3],currency:["$",""],minus:"-"});var ic=function(t){return Math.max(0,-$s(Math.abs(t)))},ac=function(t,e){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor($s(e)/3)))-$s(Math.abs(t)))},oc=function(t,e){return t=Math.abs(t),e=Math.abs(e)-t,Math.max(0,$s(e)-$s(t))+1},sc=function(){return new cc};function cc(){this.reset()}cc.prototype={constructor:cc,reset:function(){this.s=this.t=0},add:function(t){lc(uc,t,this.t),lc(this,uc.s,this.s),this.s?this.t+=uc.t:this.s=uc.t},valueOf:function(){return this.s}};var uc=new cc;function lc(t,e,n){var r=t.s=e+n,i=r-e,a=r-i;t.t=e-a+(n-i)}var hc=Math.PI,fc=hc/2,dc=hc/4,pc=2*hc,gc=180/hc,yc=hc/180,vc=Math.abs,mc=Math.atan,bc=Math.atan2,xc=Math.cos,_c=Math.ceil,kc=Math.exp,wc=(Math.floor,Math.log),Ec=Math.pow,Tc=Math.sin,Cc=Math.sign||function(t){return t>0?1:t<0?-1:0},Ac=Math.sqrt,Sc=Math.tan;function Mc(t){return t>1?0:t<-1?hc:Math.acos(t)}function Oc(t){return t>1?fc:t<-1?-fc:Math.asin(t)}function Dc(t){return(t=Tc(t/2))*t}function Nc(){}function Bc(t,e){t&&Pc.hasOwnProperty(t.type)&&Pc[t.type](t,e)}var Lc={Feature:function(t,e){Bc(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)Bc(n[r].geometry,e)}},Pc={Sphere:function(t,e){e.sphere()},Point:function(t,e){t=t.coordinates,e.point(t[0],t[1],t[2])},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)t=n[r],e.point(t[0],t[1],t[2])},LineString:function(t,e){Ic(t.coordinates,e,0)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)Ic(n[r],e,0)},Polygon:function(t,e){Fc(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)Fc(n[r],e)},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)Bc(n[r],e)}};function Ic(t,e,n){var r,i=-1,a=t.length-n;for(e.lineStart();++i<a;)r=t[i],e.point(r[0],r[1],r[2]);e.lineEnd()}function Fc(t,e){var n=-1,r=t.length;for(e.polygonStart();++n<r;)Ic(t[n],e,1);e.polygonEnd()}var jc,Rc,Yc,zc,Uc,$c=function(t,e){t&&Lc.hasOwnProperty(t.type)?Lc[t.type](t,e):Bc(t,e)},Wc=sc(),Hc=sc(),Vc={point:Nc,lineStart:Nc,lineEnd:Nc,polygonStart:function(){Wc.reset(),Vc.lineStart=Gc,Vc.lineEnd=qc},polygonEnd:function(){var t=+Wc;Hc.add(t<0?pc+t:t),this.lineStart=this.lineEnd=this.point=Nc},sphere:function(){Hc.add(pc)}};function Gc(){Vc.point=Xc}function qc(){Zc(jc,Rc)}function Xc(t,e){Vc.point=Zc,jc=t,Rc=e,Yc=t*=yc,zc=xc(e=(e*=yc)/2+dc),Uc=Tc(e)}function Zc(t,e){var n=(t*=yc)-Yc,r=n>=0?1:-1,i=r*n,a=xc(e=(e*=yc)/2+dc),o=Tc(e),s=Uc*o,c=zc*a+s*xc(i),u=s*r*Tc(i);Wc.add(bc(u,c)),Yc=t,zc=a,Uc=o}var Jc=function(t){return Hc.reset(),$c(t,Vc),2*Hc};function Kc(t){return[bc(t[1],t[0]),Oc(t[2])]}function Qc(t){var e=t[0],n=t[1],r=xc(n);return[r*xc(e),r*Tc(e),Tc(n)]}function tu(t,e){return t[0]*e[0]+t[1]*e[1]+t[2]*e[2]}function eu(t,e){return[t[1]*e[2]-t[2]*e[1],t[2]*e[0]-t[0]*e[2],t[0]*e[1]-t[1]*e[0]]}function nu(t,e){t[0]+=e[0],t[1]+=e[1],t[2]+=e[2]}function ru(t,e){return[t[0]*e,t[1]*e,t[2]*e]}function iu(t){var e=Ac(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=e,t[1]/=e,t[2]/=e}var au,ou,su,cu,uu,lu,hu,fu,du,pu,gu=sc(),yu={point:vu,lineStart:bu,lineEnd:xu,polygonStart:function(){yu.point=_u,yu.lineStart=ku,yu.lineEnd=wu,gu.reset(),Vc.polygonStart()},polygonEnd:function(){Vc.polygonEnd(),yu.point=vu,yu.lineStart=bu,yu.lineEnd=xu,Wc<0?(au=-(su=180),ou=-(cu=90)):gu>1e-6?cu=90:gu<-1e-6&&(ou=-90),pu[0]=au,pu[1]=su},sphere:function(){au=-(su=180),ou=-(cu=90)}};function vu(t,e){du.push(pu=[au=t,su=t]),e<ou&&(ou=e),e>cu&&(cu=e)}function mu(t,e){var n=Qc([t*yc,e*yc]);if(fu){var r=eu(fu,n),i=eu([r[1],-r[0],0],r);iu(i),i=Kc(i);var a,o=t-uu,s=o>0?1:-1,c=i[0]*gc*s,u=vc(o)>180;u^(s*uu<c&&c<s*t)?(a=i[1]*gc)>cu&&(cu=a):u^(s*uu<(c=(c+360)%360-180)&&c<s*t)?(a=-i[1]*gc)<ou&&(ou=a):(e<ou&&(ou=e),e>cu&&(cu=e)),u?t<uu?Eu(au,t)>Eu(au,su)&&(su=t):Eu(t,su)>Eu(au,su)&&(au=t):su>=au?(t<au&&(au=t),t>su&&(su=t)):t>uu?Eu(au,t)>Eu(au,su)&&(su=t):Eu(t,su)>Eu(au,su)&&(au=t)}else du.push(pu=[au=t,su=t]);e<ou&&(ou=e),e>cu&&(cu=e),fu=n,uu=t}function bu(){yu.point=mu}function xu(){pu[0]=au,pu[1]=su,yu.point=vu,fu=null}function _u(t,e){if(fu){var n=t-uu;gu.add(vc(n)>180?n+(n>0?360:-360):n)}else lu=t,hu=e;Vc.point(t,e),mu(t,e)}function ku(){Vc.lineStart()}function wu(){_u(lu,hu),Vc.lineEnd(),vc(gu)>1e-6&&(au=-(su=180)),pu[0]=au,pu[1]=su,fu=null}function Eu(t,e){return(e-=t)<0?e+360:e}function Tu(t,e){return t[0]-e[0]}function Cu(t,e){return t[0]<=t[1]?t[0]<=e&&e<=t[1]:e<t[0]||t[1]<e}var Au,Su,Mu,Ou,Du,Nu,Bu,Lu,Pu,Iu,Fu,ju,Ru,Yu,zu,Uu,$u=function(t){var e,n,r,i,a,o,s;if(cu=su=-(au=ou=1/0),du=[],$c(t,yu),n=du.length){for(du.sort(Tu),e=1,a=[r=du[0]];e<n;++e)Cu(r,(i=du[e])[0])||Cu(r,i[1])?(Eu(r[0],i[1])>Eu(r[0],r[1])&&(r[1]=i[1]),Eu(i[0],r[1])>Eu(r[0],r[1])&&(r[0]=i[0])):a.push(r=i);for(o=-1/0,e=0,r=a[n=a.length-1];e<=n;r=i,++e)i=a[e],(s=Eu(r[1],i[0]))>o&&(o=s,au=i[0],su=r[1])}return du=pu=null,au===1/0||ou===1/0?[[NaN,NaN],[NaN,NaN]]:[[au,ou],[su,cu]]},Wu={sphere:Nc,point:Hu,lineStart:Gu,lineEnd:Zu,polygonStart:function(){Wu.lineStart=Ju,Wu.lineEnd=Ku},polygonEnd:function(){Wu.lineStart=Gu,Wu.lineEnd=Zu}};function Hu(t,e){t*=yc;var n=xc(e*=yc);Vu(n*xc(t),n*Tc(t),Tc(e))}function Vu(t,e,n){++Au,Mu+=(t-Mu)/Au,Ou+=(e-Ou)/Au,Du+=(n-Du)/Au}function Gu(){Wu.point=qu}function qu(t,e){t*=yc;var n=xc(e*=yc);Yu=n*xc(t),zu=n*Tc(t),Uu=Tc(e),Wu.point=Xu,Vu(Yu,zu,Uu)}function Xu(t,e){t*=yc;var n=xc(e*=yc),r=n*xc(t),i=n*Tc(t),a=Tc(e),o=bc(Ac((o=zu*a-Uu*i)*o+(o=Uu*r-Yu*a)*o+(o=Yu*i-zu*r)*o),Yu*r+zu*i+Uu*a);Su+=o,Nu+=o*(Yu+(Yu=r)),Bu+=o*(zu+(zu=i)),Lu+=o*(Uu+(Uu=a)),Vu(Yu,zu,Uu)}function Zu(){Wu.point=Hu}function Ju(){Wu.point=Qu}function Ku(){tl(ju,Ru),Wu.point=Hu}function Qu(t,e){ju=t,Ru=e,t*=yc,e*=yc,Wu.point=tl;var n=xc(e);Yu=n*xc(t),zu=n*Tc(t),Uu=Tc(e),Vu(Yu,zu,Uu)}function tl(t,e){t*=yc;var n=xc(e*=yc),r=n*xc(t),i=n*Tc(t),a=Tc(e),o=zu*a-Uu*i,s=Uu*r-Yu*a,c=Yu*i-zu*r,u=Ac(o*o+s*s+c*c),l=Oc(u),h=u&&-l/u;Pu+=h*o,Iu+=h*s,Fu+=h*c,Su+=l,Nu+=l*(Yu+(Yu=r)),Bu+=l*(zu+(zu=i)),Lu+=l*(Uu+(Uu=a)),Vu(Yu,zu,Uu)}var el=function(t){Au=Su=Mu=Ou=Du=Nu=Bu=Lu=Pu=Iu=Fu=0,$c(t,Wu);var e=Pu,n=Iu,r=Fu,i=e*e+n*n+r*r;return i<1e-12&&(e=Nu,n=Bu,r=Lu,Su<1e-6&&(e=Mu,n=Ou,r=Du),(i=e*e+n*n+r*r)<1e-12)?[NaN,NaN]:[bc(n,e)*gc,Oc(r/Ac(i))*gc]},nl=function(t){return function(){return t}},rl=function(t,e){function n(n,r){return n=t(n,r),e(n[0],n[1])}return t.invert&&e.invert&&(n.invert=function(n,r){return(n=e.invert(n,r))&&t.invert(n[0],n[1])}),n};function il(t,e){return[vc(t)>hc?t+Math.round(-t/pc)*pc:t,e]}function al(t,e,n){return(t%=pc)?e||n?rl(sl(t),cl(e,n)):sl(t):e||n?cl(e,n):il}function ol(t){return function(e,n){return[(e+=t)>hc?e-pc:e<-hc?e+pc:e,n]}}function sl(t){var e=ol(t);return e.invert=ol(-t),e}function cl(t,e){var n=xc(t),r=Tc(t),i=xc(e),a=Tc(e);function o(t,e){var o=xc(e),s=xc(t)*o,c=Tc(t)*o,u=Tc(e),l=u*n+s*r;return[bc(c*i-l*a,s*n-u*r),Oc(l*i+c*a)]}return o.invert=function(t,e){var o=xc(e),s=xc(t)*o,c=Tc(t)*o,u=Tc(e),l=u*i-c*a;return[bc(c*i+u*a,s*n+l*r),Oc(l*n-s*r)]},o}il.invert=il;var ul=function(t){function e(e){return(e=t(e[0]*yc,e[1]*yc))[0]*=gc,e[1]*=gc,e}return t=al(t[0]*yc,t[1]*yc,t.length>2?t[2]*yc:0),e.invert=function(e){return(e=t.invert(e[0]*yc,e[1]*yc))[0]*=gc,e[1]*=gc,e},e};function ll(t,e,n,r,i,a){if(n){var o=xc(e),s=Tc(e),c=r*n;null==i?(i=e+r*pc,a=e-c/2):(i=hl(o,i),a=hl(o,a),(r>0?i<a:i>a)&&(i+=r*pc));for(var u,l=i;r>0?l>a:l<a;l-=c)u=Kc([o,-s*xc(l),-s*Tc(l)]),t.point(u[0],u[1])}}function hl(t,e){(e=Qc(e))[0]-=t,iu(e);var n=Mc(-e[1]);return((-e[2]<0?-n:n)+pc-1e-6)%pc}var fl=function(){var t,e,n=nl([0,0]),r=nl(90),i=nl(6),a={point:function(n,r){t.push(n=e(n,r)),n[0]*=gc,n[1]*=gc}};function o(){var o=n.apply(this,arguments),s=r.apply(this,arguments)*yc,c=i.apply(this,arguments)*yc;return t=[],e=al(-o[0]*yc,-o[1]*yc,0).invert,ll(a,s,c,1),o={type:"Polygon",coordinates:[t]},t=e=null,o}return o.center=function(t){return arguments.length?(n="function"==typeof t?t:nl([+t[0],+t[1]]),o):n},o.radius=function(t){return arguments.length?(r="function"==typeof t?t:nl(+t),o):r},o.precision=function(t){return arguments.length?(i="function"==typeof t?t:nl(+t),o):i},o},dl=function(){var t,e=[];return{point:function(e,n){t.push([e,n])},lineStart:function(){e.push(t=[])},lineEnd:Nc,rejoin:function(){e.length>1&&e.push(e.pop().concat(e.shift()))},result:function(){var n=e;return e=[],t=null,n}}},pl=function(t,e){return vc(t[0]-e[0])<1e-6&&vc(t[1]-e[1])<1e-6};function gl(t,e,n,r){this.x=t,this.z=e,this.o=n,this.e=r,this.v=!1,this.n=this.p=null}var yl=function(t,e,n,r,i){var a,o,s=[],c=[];if(t.forEach((function(t){if(!((e=t.length-1)<=0)){var e,n,r=t[0],o=t[e];if(pl(r,o)){for(i.lineStart(),a=0;a<e;++a)i.point((r=t[a])[0],r[1]);i.lineEnd()}else s.push(n=new gl(r,t,null,!0)),c.push(n.o=new gl(r,null,n,!1)),s.push(n=new gl(o,t,null,!1)),c.push(n.o=new gl(o,null,n,!0))}})),s.length){for(c.sort(e),vl(s),vl(c),a=0,o=c.length;a<o;++a)c[a].e=n=!n;for(var u,l,h=s[0];;){for(var f=h,d=!0;f.v;)if((f=f.n)===h)return;u=f.z,i.lineStart();do{if(f.v=f.o.v=!0,f.e){if(d)for(a=0,o=u.length;a<o;++a)i.point((l=u[a])[0],l[1]);else r(f.x,f.n.x,1,i);f=f.n}else{if(d)for(u=f.p.z,a=u.length-1;a>=0;--a)i.point((l=u[a])[0],l[1]);else r(f.x,f.p.x,-1,i);f=f.p}u=(f=f.o).z,d=!d}while(!f.v);i.lineEnd()}}};function vl(t){if(e=t.length){for(var e,n,r=0,i=t[0];++r<e;)i.n=n=t[r],n.p=i,i=n;i.n=n=t[0],n.p=i}}var ml=sc();function bl(t){return vc(t[0])<=hc?t[0]:Cc(t[0])*((vc(t[0])+hc)%pc-hc)}var xl=function(t,e){var n=bl(e),r=e[1],i=Tc(r),a=[Tc(n),-xc(n),0],o=0,s=0;ml.reset(),1===i?r=fc+1e-6:-1===i&&(r=-fc-1e-6);for(var c=0,u=t.length;c<u;++c)if(h=(l=t[c]).length)for(var l,h,f=l[h-1],d=bl(f),p=f[1]/2+dc,g=Tc(p),y=xc(p),v=0;v<h;++v,d=b,g=_,y=k,f=m){var m=l[v],b=bl(m),x=m[1]/2+dc,_=Tc(x),k=xc(x),w=b-d,E=w>=0?1:-1,T=E*w,C=T>hc,A=g*_;if(ml.add(bc(A*E*Tc(T),y*k+A*xc(T))),o+=C?w+E*pc:w,C^d>=n^b>=n){var S=eu(Qc(f),Qc(m));iu(S);var M=eu(a,S);iu(M);var O=(C^w>=0?-1:1)*Oc(M[2]);(r>O||r===O&&(S[0]||S[1]))&&(s+=C^w>=0?1:-1)}}return(o<-1e-6||o<1e-6&&ml<-1e-6)^1&s},_l=function(t,e,n,r){return function(i){var a,o,s,c=e(i),u=dl(),l=e(u),h=!1,f={point:d,lineStart:g,lineEnd:y,polygonStart:function(){f.point=v,f.lineStart=m,f.lineEnd=b,o=[],a=[]},polygonEnd:function(){f.point=d,f.lineStart=g,f.lineEnd=y,o=F(o);var t=xl(a,r);o.length?(h||(i.polygonStart(),h=!0),yl(o,wl,t,n,i)):t&&(h||(i.polygonStart(),h=!0),i.lineStart(),n(null,null,1,i),i.lineEnd()),h&&(i.polygonEnd(),h=!1),o=a=null},sphere:function(){i.polygonStart(),i.lineStart(),n(null,null,1,i),i.lineEnd(),i.polygonEnd()}};function d(e,n){t(e,n)&&i.point(e,n)}function p(t,e){c.point(t,e)}function g(){f.point=p,c.lineStart()}function y(){f.point=d,c.lineEnd()}function v(t,e){s.push([t,e]),l.point(t,e)}function m(){l.lineStart(),s=[]}function b(){v(s[0][0],s[0][1]),l.lineEnd();var t,e,n,r,c=l.clean(),f=u.result(),d=f.length;if(s.pop(),a.push(s),s=null,d)if(1&c){if((e=(n=f[0]).length-1)>0){for(h||(i.polygonStart(),h=!0),i.lineStart(),t=0;t<e;++t)i.point((r=n[t])[0],r[1]);i.lineEnd()}}else d>1&&2&c&&f.push(f.pop().concat(f.shift())),o.push(f.filter(kl))}return f}};function kl(t){return t.length>1}function wl(t,e){return((t=t.x)[0]<0?t[1]-fc-1e-6:fc-t[1])-((e=e.x)[0]<0?e[1]-fc-1e-6:fc-e[1])}var El=_l((function(){return!0}),(function(t){var e,n=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),e=1},point:function(a,o){var s=a>0?hc:-hc,c=vc(a-n);vc(c-hc)<1e-6?(t.point(n,r=(r+o)/2>0?fc:-fc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),t.point(a,r),e=0):i!==s&&c>=hc&&(vc(n-i)<1e-6&&(n-=1e-6*i),vc(a-s)<1e-6&&(a-=1e-6*s),r=function(t,e,n,r){var i,a,o=Tc(t-n);return vc(o)>1e-6?mc((Tc(e)*(a=xc(r))*Tc(n)-Tc(r)*(i=xc(e))*Tc(t))/(i*a*o)):(e+r)/2}(n,r,a,o),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(s,r),e=0),t.point(n=a,r=o),i=s},lineEnd:function(){t.lineEnd(),n=r=NaN},clean:function(){return 2-e}}}),(function(t,e,n,r){var i;if(null==t)i=n*fc,r.point(-hc,i),r.point(0,i),r.point(hc,i),r.point(hc,0),r.point(hc,-i),r.point(0,-i),r.point(-hc,-i),r.point(-hc,0),r.point(-hc,i);else if(vc(t[0]-e[0])>1e-6){var a=t[0]<e[0]?hc:-hc;i=n*a/2,r.point(-a,i),r.point(0,i),r.point(a,i)}else r.point(e[0],e[1])}),[-hc,-fc]);var Tl=function(t){var e=xc(t),n=6*yc,r=e>0,i=vc(e)>1e-6;function a(t,n){return xc(t)*xc(n)>e}function o(t,n,r){var i=[1,0,0],a=eu(Qc(t),Qc(n)),o=tu(a,a),s=a[0],c=o-s*s;if(!c)return!r&&t;var u=e*o/c,l=-e*s/c,h=eu(i,a),f=ru(i,u);nu(f,ru(a,l));var d=h,p=tu(f,d),g=tu(d,d),y=p*p-g*(tu(f,f)-1);if(!(y<0)){var v=Ac(y),m=ru(d,(-p-v)/g);if(nu(m,f),m=Kc(m),!r)return m;var b,x=t[0],_=n[0],k=t[1],w=n[1];_<x&&(b=x,x=_,_=b);var E=_-x,T=vc(E-hc)<1e-6;if(!T&&w<k&&(b=k,k=w,w=b),T||E<1e-6?T?k+w>0^m[1]<(vc(m[0]-x)<1e-6?k:w):k<=m[1]&&m[1]<=w:E>hc^(x<=m[0]&&m[0]<=_)){var C=ru(d,(-p+v)/g);return nu(C,f),[m,Kc(C)]}}}function s(e,n){var i=r?t:hc-t,a=0;return e<-i?a|=1:e>i&&(a|=2),n<-i?a|=4:n>i&&(a|=8),a}return _l(a,(function(t){var e,n,c,u,l;return{lineStart:function(){u=c=!1,l=1},point:function(h,f){var d,p=[h,f],g=a(h,f),y=r?g?0:s(h,f):g?s(h+(h<0?hc:-hc),f):0;if(!e&&(u=c=g)&&t.lineStart(),g!==c&&(!(d=o(e,p))||pl(e,d)||pl(p,d))&&(p[0]+=1e-6,p[1]+=1e-6,g=a(p[0],p[1])),g!==c)l=0,g?(t.lineStart(),d=o(p,e),t.point(d[0],d[1])):(d=o(e,p),t.point(d[0],d[1]),t.lineEnd()),e=d;else if(i&&e&&r^g){var v;y&n||!(v=o(p,e,!0))||(l=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1])))}!g||e&&pl(e,p)||t.point(p[0],p[1]),e=p,c=g,n=y},lineEnd:function(){c&&t.lineEnd(),e=null},clean:function(){return l|(u&&c)<<1}}}),(function(e,r,i,a){ll(a,t,n,i,e,r)}),r?[0,-t]:[-hc,t-hc])};function Cl(t,e,n,r){function i(i,a){return t<=i&&i<=n&&e<=a&&a<=r}function a(i,a,s,u){var l=0,h=0;if(null==i||(l=o(i,s))!==(h=o(a,s))||c(i,a)<0^s>0)do{u.point(0===l||3===l?t:n,l>1?r:e)}while((l=(l+s+4)%4)!==h);else u.point(a[0],a[1])}function o(r,i){return vc(r[0]-t)<1e-6?i>0?0:3:vc(r[0]-n)<1e-6?i>0?2:1:vc(r[1]-e)<1e-6?i>0?1:0:i>0?3:2}function s(t,e){return c(t.x,e.x)}function c(t,e){var n=o(t,1),r=o(e,1);return n!==r?n-r:0===n?e[1]-t[1]:1===n?t[0]-e[0]:2===n?t[1]-e[1]:e[0]-t[0]}return function(o){var c,u,l,h,f,d,p,g,y,v,m,b=o,x=dl(),_={point:k,lineStart:function(){_.point=w,u&&u.push(l=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(w(h,f),d&&y&&x.rejoin(),c.push(x.result()));_.point=k,y&&b.lineEnd()},polygonStart:function(){b=x,c=[],u=[],m=!0},polygonEnd:function(){var e=function(){for(var e=0,n=0,i=u.length;n<i;++n)for(var a,o,s=u[n],c=1,l=s.length,h=s[0],f=h[0],d=h[1];c<l;++c)a=f,o=d,h=s[c],f=h[0],d=h[1],o<=r?d>r&&(f-a)*(r-o)>(d-o)*(t-a)&&++e:d<=r&&(f-a)*(r-o)<(d-o)*(t-a)&&--e;return e}(),n=m&&e,i=(c=F(c)).length;(n||i)&&(o.polygonStart(),n&&(o.lineStart(),a(null,null,1,o),o.lineEnd()),i&&yl(c,s,e,a,o),o.polygonEnd());b=o,c=u=l=null}};function k(t,e){i(t,e)&&b.point(t,e)}function w(a,o){var s=i(a,o);if(u&&l.push([a,o]),v)h=a,f=o,d=s,v=!1,s&&(b.lineStart(),b.point(a,o));else if(s&&y)b.point(a,o);else{var c=[p=Math.max(-1e9,Math.min(1e9,p)),g=Math.max(-1e9,Math.min(1e9,g))],x=[a=Math.max(-1e9,Math.min(1e9,a)),o=Math.max(-1e9,Math.min(1e9,o))];!function(t,e,n,r,i,a){var o,s=t[0],c=t[1],u=0,l=1,h=e[0]-s,f=e[1]-c;if(o=n-s,h||!(o>0)){if(o/=h,h<0){if(o<u)return;o<l&&(l=o)}else if(h>0){if(o>l)return;o>u&&(u=o)}if(o=i-s,h||!(o<0)){if(o/=h,h<0){if(o>l)return;o>u&&(u=o)}else if(h>0){if(o<u)return;o<l&&(l=o)}if(o=r-c,f||!(o>0)){if(o/=f,f<0){if(o<u)return;o<l&&(l=o)}else if(f>0){if(o>l)return;o>u&&(u=o)}if(o=a-c,f||!(o<0)){if(o/=f,f<0){if(o>l)return;o>u&&(u=o)}else if(f>0){if(o<u)return;o<l&&(l=o)}return u>0&&(t[0]=s+u*h,t[1]=c+u*f),l<1&&(e[0]=s+l*h,e[1]=c+l*f),!0}}}}}(c,x,t,e,n,r)?s&&(b.lineStart(),b.point(a,o),m=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(x[0],x[1]),s||b.lineEnd(),m=!1)}p=a,g=o,y=s}return _}}var Al,Sl,Ml,Ol=function(){var t,e,n,r=0,i=0,a=960,o=500;return n={stream:function(n){return t&&e===n?t:t=Cl(r,i,a,o)(e=n)},extent:function(s){return arguments.length?(r=+s[0][0],i=+s[0][1],a=+s[1][0],o=+s[1][1],t=e=null,n):[[r,i],[a,o]]}}},Dl=sc(),Nl={sphere:Nc,point:Nc,lineStart:function(){Nl.point=Ll,Nl.lineEnd=Bl},lineEnd:Nc,polygonStart:Nc,polygonEnd:Nc};function Bl(){Nl.point=Nl.lineEnd=Nc}function Ll(t,e){Al=t*=yc,Sl=Tc(e*=yc),Ml=xc(e),Nl.point=Pl}function Pl(t,e){t*=yc;var n=Tc(e*=yc),r=xc(e),i=vc(t-Al),a=xc(i),o=r*Tc(i),s=Ml*n-Sl*r*a,c=Sl*n+Ml*r*a;Dl.add(bc(Ac(o*o+s*s),c)),Al=t,Sl=n,Ml=r}var Il=function(t){return Dl.reset(),$c(t,Nl),+Dl},Fl=[null,null],jl={type:"LineString",coordinates:Fl},Rl=function(t,e){return Fl[0]=t,Fl[1]=e,Il(jl)},Yl={Feature:function(t,e){return Ul(t.geometry,e)},FeatureCollection:function(t,e){for(var n=t.features,r=-1,i=n.length;++r<i;)if(Ul(n[r].geometry,e))return!0;return!1}},zl={Sphere:function(){return!0},Point:function(t,e){return $l(t.coordinates,e)},MultiPoint:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if($l(n[r],e))return!0;return!1},LineString:function(t,e){return Wl(t.coordinates,e)},MultiLineString:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(Wl(n[r],e))return!0;return!1},Polygon:function(t,e){return Hl(t.coordinates,e)},MultiPolygon:function(t,e){for(var n=t.coordinates,r=-1,i=n.length;++r<i;)if(Hl(n[r],e))return!0;return!1},GeometryCollection:function(t,e){for(var n=t.geometries,r=-1,i=n.length;++r<i;)if(Ul(n[r],e))return!0;return!1}};function Ul(t,e){return!(!t||!zl.hasOwnProperty(t.type))&&zl[t.type](t,e)}function $l(t,e){return 0===Rl(t,e)}function Wl(t,e){for(var n,r,i,a=0,o=t.length;a<o;a++){if(0===(r=Rl(t[a],e)))return!0;if(a>0&&(i=Rl(t[a],t[a-1]))>0&&n<=i&&r<=i&&(n+r-i)*(1-Math.pow((n-r)/i,2))<1e-12*i)return!0;n=r}return!1}function Hl(t,e){return!!xl(t.map(Vl),Gl(e))}function Vl(t){return(t=t.map(Gl)).pop(),t}function Gl(t){return[t[0]*yc,t[1]*yc]}var ql=function(t,e){return(t&&Yl.hasOwnProperty(t.type)?Yl[t.type]:Ul)(t,e)};function Xl(t,e,n){var r=k(t,e-1e-6,n).concat(e);return function(t){return r.map((function(e){return[t,e]}))}}function Zl(t,e,n){var r=k(t,e-1e-6,n).concat(e);return function(t){return r.map((function(e){return[e,t]}))}}function Jl(){var t,e,n,r,i,a,o,s,c,u,l,h,f=10,d=f,p=90,g=360,y=2.5;function v(){return{type:"MultiLineString",coordinates:m()}}function m(){return k(_c(r/p)*p,n,p).map(l).concat(k(_c(s/g)*g,o,g).map(h)).concat(k(_c(e/f)*f,t,f).filter((function(t){return vc(t%p)>1e-6})).map(c)).concat(k(_c(a/d)*d,i,d).filter((function(t){return vc(t%g)>1e-6})).map(u))}return v.lines=function(){return m().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[l(r).concat(h(o).slice(1),l(n).reverse().slice(1),h(s).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],n=+t[1][0],s=+t[0][1],o=+t[1][1],r>n&&(t=r,r=n,n=t),s>o&&(t=s,s=o,o=t),v.precision(y)):[[r,s],[n,o]]},v.extentMinor=function(n){return arguments.length?(e=+n[0][0],t=+n[1][0],a=+n[0][1],i=+n[1][1],e>t&&(n=e,e=t,t=n),a>i&&(n=a,a=i,i=n),v.precision(y)):[[e,a],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(f=+t[0],d=+t[1],v):[f,d]},v.precision=function(f){return arguments.length?(y=+f,c=Xl(a,i,90),u=Zl(e,t,y),l=Xl(s,o,90),h=Zl(r,n,y),v):y},v.extentMajor([[-180,1e-6-90],[180,90-1e-6]]).extentMinor([[-180,-80-1e-6],[180,80+1e-6]])}function Kl(){return Jl()()}var Ql,th,eh,nh,rh=function(t,e){var n=t[0]*yc,r=t[1]*yc,i=e[0]*yc,a=e[1]*yc,o=xc(r),s=Tc(r),c=xc(a),u=Tc(a),l=o*xc(n),h=o*Tc(n),f=c*xc(i),d=c*Tc(i),p=2*Oc(Ac(Dc(a-r)+o*c*Dc(i-n))),g=Tc(p),y=p?function(t){var e=Tc(t*=p)/g,n=Tc(p-t)/g,r=n*l+e*f,i=n*h+e*d,a=n*s+e*u;return[bc(i,r)*gc,bc(a,Ac(r*r+i*i))*gc]}:function(){return[n*gc,r*gc]};return y.distance=p,y},ih=function(t){return t},ah=sc(),oh=sc(),sh={point:Nc,lineStart:Nc,lineEnd:Nc,polygonStart:function(){sh.lineStart=ch,sh.lineEnd=hh},polygonEnd:function(){sh.lineStart=sh.lineEnd=sh.point=Nc,ah.add(vc(oh)),oh.reset()},result:function(){var t=ah/2;return ah.reset(),t}};function ch(){sh.point=uh}function uh(t,e){sh.point=lh,Ql=eh=t,th=nh=e}function lh(t,e){oh.add(nh*t-eh*e),eh=t,nh=e}function hh(){lh(Ql,th)}var fh=sh,dh=1/0,ph=dh,gh=-dh,yh=gh;var vh,mh,bh,xh,_h={point:function(t,e){t<dh&&(dh=t);t>gh&&(gh=t);e<ph&&(ph=e);e>yh&&(yh=e)},lineStart:Nc,lineEnd:Nc,polygonStart:Nc,polygonEnd:Nc,result:function(){var t=[[dh,ph],[gh,yh]];return gh=yh=-(ph=dh=1/0),t}},kh=0,wh=0,Eh=0,Th=0,Ch=0,Ah=0,Sh=0,Mh=0,Oh=0,Dh={point:Nh,lineStart:Bh,lineEnd:Ih,polygonStart:function(){Dh.lineStart=Fh,Dh.lineEnd=jh},polygonEnd:function(){Dh.point=Nh,Dh.lineStart=Bh,Dh.lineEnd=Ih},result:function(){var t=Oh?[Sh/Oh,Mh/Oh]:Ah?[Th/Ah,Ch/Ah]:Eh?[kh/Eh,wh/Eh]:[NaN,NaN];return kh=wh=Eh=Th=Ch=Ah=Sh=Mh=Oh=0,t}};function Nh(t,e){kh+=t,wh+=e,++Eh}function Bh(){Dh.point=Lh}function Lh(t,e){Dh.point=Ph,Nh(bh=t,xh=e)}function Ph(t,e){var n=t-bh,r=e-xh,i=Ac(n*n+r*r);Th+=i*(bh+t)/2,Ch+=i*(xh+e)/2,Ah+=i,Nh(bh=t,xh=e)}function Ih(){Dh.point=Nh}function Fh(){Dh.point=Rh}function jh(){Yh(vh,mh)}function Rh(t,e){Dh.point=Yh,Nh(vh=bh=t,mh=xh=e)}function Yh(t,e){var n=t-bh,r=e-xh,i=Ac(n*n+r*r);Th+=i*(bh+t)/2,Ch+=i*(xh+e)/2,Ah+=i,Sh+=(i=xh*t-bh*e)*(bh+t),Mh+=i*(xh+e),Oh+=3*i,Nh(bh=t,xh=e)}var zh=Dh;function Uh(t){this._context=t}Uh.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._context.moveTo(t,e),this._point=1;break;case 1:this._context.lineTo(t,e);break;default:this._context.moveTo(t+this._radius,e),this._context.arc(t,e,this._radius,0,pc)}},result:Nc};var $h,Wh,Hh,Vh,Gh,qh=sc(),Xh={point:Nc,lineStart:function(){Xh.point=Zh},lineEnd:function(){$h&&Jh(Wh,Hh),Xh.point=Nc},polygonStart:function(){$h=!0},polygonEnd:function(){$h=null},result:function(){var t=+qh;return qh.reset(),t}};function Zh(t,e){Xh.point=Jh,Wh=Vh=t,Hh=Gh=e}function Jh(t,e){Vh-=t,Gh-=e,qh.add(Ac(Vh*Vh+Gh*Gh)),Vh=t,Gh=e}var Kh=Xh;function Qh(){this._string=[]}function tf(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}Qh.prototype={_radius:4.5,_circle:tf(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,e){switch(this._point){case 0:this._string.push("M",t,",",e),this._point=1;break;case 1:this._string.push("L",t,",",e);break;default:null==this._circle&&(this._circle=tf(this._radius)),this._string.push("M",t,",",e,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}};var ef=function(t,e){var n,r,i=4.5;function a(t){return t&&("function"==typeof i&&r.pointRadius(+i.apply(this,arguments)),$c(t,n(r))),r.result()}return a.area=function(t){return $c(t,n(fh)),fh.result()},a.measure=function(t){return $c(t,n(Kh)),Kh.result()},a.bounds=function(t){return $c(t,n(_h)),_h.result()},a.centroid=function(t){return $c(t,n(zh)),zh.result()},a.projection=function(e){return arguments.length?(n=null==e?(t=null,ih):(t=e).stream,a):t},a.context=function(t){return arguments.length?(r=null==t?(e=null,new Qh):new Uh(e=t),"function"!=typeof i&&r.pointRadius(i),a):e},a.pointRadius=function(t){return arguments.length?(i="function"==typeof t?t:(r.pointRadius(+t),+t),a):i},a.projection(t).context(e)},nf=function(t){return{stream:rf(t)}};function rf(t){return function(e){var n=new af;for(var r in t)n[r]=t[r];return n.stream=e,n}}function af(){}function of(t,e,n){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),$c(n,t.stream(_h)),e(_h.result()),null!=r&&t.clipExtent(r),t}function sf(t,e,n){return of(t,(function(n){var r=e[1][0]-e[0][0],i=e[1][1]-e[0][1],a=Math.min(r/(n[1][0]-n[0][0]),i/(n[1][1]-n[0][1])),o=+e[0][0]+(r-a*(n[1][0]+n[0][0]))/2,s=+e[0][1]+(i-a*(n[1][1]+n[0][1]))/2;t.scale(150*a).translate([o,s])}),n)}function cf(t,e,n){return sf(t,[[0,0],e],n)}function uf(t,e,n){return of(t,(function(n){var r=+e,i=r/(n[1][0]-n[0][0]),a=(r-i*(n[1][0]+n[0][0]))/2,o=-i*n[0][1];t.scale(150*i).translate([a,o])}),n)}function lf(t,e,n){return of(t,(function(n){var r=+e,i=r/(n[1][1]-n[0][1]),a=-i*n[0][0],o=(r-i*(n[1][1]+n[0][1]))/2;t.scale(150*i).translate([a,o])}),n)}af.prototype={constructor:af,point:function(t,e){this.stream.point(t,e)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var hf=xc(30*yc),ff=function(t,e){return+e?function(t,e){function n(r,i,a,o,s,c,u,l,h,f,d,p,g,y){var v=u-r,m=l-i,b=v*v+m*m;if(b>4*e&&g--){var x=o+f,_=s+d,k=c+p,w=Ac(x*x+_*_+k*k),E=Oc(k/=w),T=vc(vc(k)-1)<1e-6||vc(a-h)<1e-6?(a+h)/2:bc(_,x),C=t(T,E),A=C[0],S=C[1],M=A-r,O=S-i,D=m*M-v*O;(D*D/b>e||vc((v*M+m*O)/b-.5)>.3||o*f+s*d+c*p<hf)&&(n(r,i,a,o,s,c,A,S,T,x/=w,_/=w,k,g,y),y.point(A,S),n(A,S,T,x,_,k,u,l,h,f,d,p,g,y))}}return function(e){var r,i,a,o,s,c,u,l,h,f,d,p,g={point:y,lineStart:v,lineEnd:b,polygonStart:function(){e.polygonStart(),g.lineStart=x},polygonEnd:function(){e.polygonEnd(),g.lineStart=v}};function y(n,r){n=t(n,r),e.point(n[0],n[1])}function v(){l=NaN,g.point=m,e.lineStart()}function m(r,i){var a=Qc([r,i]),o=t(r,i);n(l,h,u,f,d,p,l=o[0],h=o[1],u=r,f=a[0],d=a[1],p=a[2],16,e),e.point(l,h)}function b(){g.point=y,e.lineEnd()}function x(){v(),g.point=_,g.lineEnd=k}function _(t,e){m(r=t,e),i=l,a=h,o=f,s=d,c=p,g.point=m}function k(){n(l,h,u,f,d,p,i,a,r,o,s,c,16,e),g.lineEnd=b,b()}return g}}(t,e):function(t){return rf({point:function(e,n){e=t(e,n),this.stream.point(e[0],e[1])}})}(t)};var df=rf({point:function(t,e){this.stream.point(t*yc,e*yc)}});function pf(t,e,n){function r(r,i){return[e+t*r,n-t*i]}return r.invert=function(r,i){return[(r-e)/t,(n-i)/t]},r}function gf(t,e,n,r){var i=xc(r),a=Tc(r),o=i*t,s=a*t,c=i/t,u=a/t,l=(a*n-i*e)/t,h=(a*e+i*n)/t;function f(t,r){return[o*t-s*r+e,n-s*t-o*r]}return f.invert=function(t,e){return[c*t-u*e+l,h-u*t-c*e]},f}function yf(t){return vf((function(){return t}))()}function vf(t){var e,n,r,i,a,o,s,c,u,l,h=150,f=480,d=250,p=0,g=0,y=0,v=0,m=0,b=0,x=null,_=El,k=null,w=ih,E=.5;function T(t){return c(t[0]*yc,t[1]*yc)}function C(t){return(t=c.invert(t[0],t[1]))&&[t[0]*gc,t[1]*gc]}function A(){var t=gf(h,0,0,b).apply(null,e(p,g)),r=(b?gf:pf)(h,f-t[0],d-t[1],b);return n=al(y,v,m),s=rl(e,r),c=rl(n,s),o=ff(s,E),S()}function S(){return u=l=null,T}return T.stream=function(t){return u&&l===t?u:u=df(function(t){return rf({point:function(e,n){var r=t(e,n);return this.stream.point(r[0],r[1])}})}(n)(_(o(w(l=t)))))},T.preclip=function(t){return arguments.length?(_=t,x=void 0,S()):_},T.postclip=function(t){return arguments.length?(w=t,k=r=i=a=null,S()):w},T.clipAngle=function(t){return arguments.length?(_=+t?Tl(x=t*yc):(x=null,El),S()):x*gc},T.clipExtent=function(t){return arguments.length?(w=null==t?(k=r=i=a=null,ih):Cl(k=+t[0][0],r=+t[0][1],i=+t[1][0],a=+t[1][1]),S()):null==k?null:[[k,r],[i,a]]},T.scale=function(t){return arguments.length?(h=+t,A()):h},T.translate=function(t){return arguments.length?(f=+t[0],d=+t[1],A()):[f,d]},T.center=function(t){return arguments.length?(p=t[0]%360*yc,g=t[1]%360*yc,A()):[p*gc,g*gc]},T.rotate=function(t){return arguments.length?(y=t[0]%360*yc,v=t[1]%360*yc,m=t.length>2?t[2]%360*yc:0,A()):[y*gc,v*gc,m*gc]},T.angle=function(t){return arguments.length?(b=t%360*yc,A()):b*gc},T.precision=function(t){return arguments.length?(o=ff(s,E=t*t),S()):Ac(E)},T.fitExtent=function(t,e){return sf(T,t,e)},T.fitSize=function(t,e){return cf(T,t,e)},T.fitWidth=function(t,e){return uf(T,t,e)},T.fitHeight=function(t,e){return lf(T,t,e)},function(){return e=t.apply(this,arguments),T.invert=e.invert&&C,A()}}function mf(t){var e=0,n=hc/3,r=vf(t),i=r(e,n);return i.parallels=function(t){return arguments.length?r(e=t[0]*yc,n=t[1]*yc):[e*gc,n*gc]},i}function bf(t,e){var n=Tc(t),r=(n+Tc(e))/2;if(vc(r)<1e-6)return function(t){var e=xc(t);function n(t,n){return[t*e,Tc(n)/e]}return n.invert=function(t,n){return[t/e,Oc(n*e)]},n}(t);var i=1+n*(2*r-n),a=Ac(i)/r;function o(t,e){var n=Ac(i-2*r*Tc(e))/r;return[n*Tc(t*=r),a-n*xc(t)]}return o.invert=function(t,e){var n=a-e;return[bc(t,vc(n))/r*Cc(n),Oc((i-(t*t+n*n)*r*r)/(2*r))]},o}var xf=function(){return mf(bf).scale(155.424).center([0,33.6442])},_f=function(){return xf().parallels([29.5,45.5]).scale(1070).translate([480,250]).rotate([96,0]).center([-.6,38.7])};var kf=function(){var t,e,n,r,i,a,o=_f(),s=xf().rotate([154,0]).center([-2,58.5]).parallels([55,65]),c=xf().rotate([157,0]).center([-3,19.9]).parallels([8,18]),u={point:function(t,e){a=[t,e]}};function l(t){var e=t[0],o=t[1];return a=null,n.point(e,o),a||(r.point(e,o),a)||(i.point(e,o),a)}function h(){return t=e=null,l}return l.invert=function(t){var e=o.scale(),n=o.translate(),r=(t[0]-n[0])/e,i=(t[1]-n[1])/e;return(i>=.12&&i<.234&&r>=-.425&&r<-.214?s:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:o).invert(t)},l.stream=function(n){return t&&e===n?t:(r=[o.stream(e=n),s.stream(n),c.stream(n)],i=r.length,t={point:function(t,e){for(var n=-1;++n<i;)r[n].point(t,e)},sphere:function(){for(var t=-1;++t<i;)r[t].sphere()},lineStart:function(){for(var t=-1;++t<i;)r[t].lineStart()},lineEnd:function(){for(var t=-1;++t<i;)r[t].lineEnd()},polygonStart:function(){for(var t=-1;++t<i;)r[t].polygonStart()},polygonEnd:function(){for(var t=-1;++t<i;)r[t].polygonEnd()}});var r,i},l.precision=function(t){return arguments.length?(o.precision(t),s.precision(t),c.precision(t),h()):o.precision()},l.scale=function(t){return arguments.length?(o.scale(t),s.scale(.35*t),c.scale(t),l.translate(o.translate())):o.scale()},l.translate=function(t){if(!arguments.length)return o.translate();var e=o.scale(),a=+t[0],l=+t[1];return n=o.translate(t).clipExtent([[a-.455*e,l-.238*e],[a+.455*e,l+.238*e]]).stream(u),r=s.translate([a-.307*e,l+.201*e]).clipExtent([[a-.425*e+1e-6,l+.12*e+1e-6],[a-.214*e-1e-6,l+.234*e-1e-6]]).stream(u),i=c.translate([a-.205*e,l+.212*e]).clipExtent([[a-.214*e+1e-6,l+.166*e+1e-6],[a-.115*e-1e-6,l+.234*e-1e-6]]).stream(u),h()},l.fitExtent=function(t,e){return sf(l,t,e)},l.fitSize=function(t,e){return cf(l,t,e)},l.fitWidth=function(t,e){return uf(l,t,e)},l.fitHeight=function(t,e){return lf(l,t,e)},l.scale(1070)};function wf(t){return function(e,n){var r=xc(e),i=xc(n),a=t(r*i);return[a*i*Tc(e),a*Tc(n)]}}function Ef(t){return function(e,n){var r=Ac(e*e+n*n),i=t(r),a=Tc(i),o=xc(i);return[bc(e*a,r*o),Oc(r&&n*a/r)]}}var Tf=wf((function(t){return Ac(2/(1+t))}));Tf.invert=Ef((function(t){return 2*Oc(t/2)}));var Cf=function(){return yf(Tf).scale(124.75).clipAngle(179.999)},Af=wf((function(t){return(t=Mc(t))&&t/Tc(t)}));Af.invert=Ef((function(t){return t}));var Sf=function(){return yf(Af).scale(79.4188).clipAngle(179.999)};function Mf(t,e){return[t,wc(Sc((fc+e)/2))]}Mf.invert=function(t,e){return[t,2*mc(kc(e))-fc]};var Of=function(){return Df(Mf).scale(961/pc)};function Df(t){var e,n,r,i=yf(t),a=i.center,o=i.scale,s=i.translate,c=i.clipExtent,u=null;function l(){var a=hc*o(),s=i(ul(i.rotate()).invert([0,0]));return c(null==u?[[s[0]-a,s[1]-a],[s[0]+a,s[1]+a]]:t===Mf?[[Math.max(s[0]-a,u),e],[Math.min(s[0]+a,n),r]]:[[u,Math.max(s[1]-a,e)],[n,Math.min(s[1]+a,r)]])}return i.scale=function(t){return arguments.length?(o(t),l()):o()},i.translate=function(t){return arguments.length?(s(t),l()):s()},i.center=function(t){return arguments.length?(a(t),l()):a()},i.clipExtent=function(t){return arguments.length?(null==t?u=e=n=r=null:(u=+t[0][0],e=+t[0][1],n=+t[1][0],r=+t[1][1]),l()):null==u?null:[[u,e],[n,r]]},l()}function Nf(t){return Sc((fc+t)/2)}function Bf(t,e){var n=xc(t),r=t===e?Tc(t):wc(n/xc(e))/wc(Nf(e)/Nf(t)),i=n*Ec(Nf(t),r)/r;if(!r)return Mf;function a(t,e){i>0?e<1e-6-fc&&(e=1e-6-fc):e>fc-1e-6&&(e=fc-1e-6);var n=i/Ec(Nf(e),r);return[n*Tc(r*t),i-n*xc(r*t)]}return a.invert=function(t,e){var n=i-e,a=Cc(r)*Ac(t*t+n*n);return[bc(t,vc(n))/r*Cc(n),2*mc(Ec(i/a,1/r))-fc]},a}var Lf=function(){return mf(Bf).scale(109.5).parallels([30,30])};function Pf(t,e){return[t,e]}Pf.invert=Pf;var If=function(){return yf(Pf).scale(152.63)};function Ff(t,e){var n=xc(t),r=t===e?Tc(t):(n-xc(e))/(e-t),i=n/r+t;if(vc(r)<1e-6)return Pf;function a(t,e){var n=i-e,a=r*t;return[n*Tc(a),i-n*xc(a)]}return a.invert=function(t,e){var n=i-e;return[bc(t,vc(n))/r*Cc(n),i-Cc(r)*Ac(t*t+n*n)]},a}var jf=function(){return mf(Ff).scale(131.154).center([0,13.9389])},Rf=1.340264,Yf=-.081106,zf=893e-6,Uf=.003796,$f=Ac(3)/2;function Wf(t,e){var n=Oc($f*Tc(e)),r=n*n,i=r*r*r;return[t*xc(n)/($f*(Rf+3*Yf*r+i*(7*zf+9*Uf*r))),n*(Rf+Yf*r+i*(zf+Uf*r))]}Wf.invert=function(t,e){for(var n,r=e,i=r*r,a=i*i*i,o=0;o<12&&(a=(i=(r-=n=(r*(Rf+Yf*i+a*(zf+Uf*i))-e)/(Rf+3*Yf*i+a*(7*zf+9*Uf*i)))*r)*i*i,!(vc(n)<1e-12));++o);return[$f*t*(Rf+3*Yf*i+a*(7*zf+9*Uf*i))/xc(r),Oc(Tc(r)/$f)]};var Hf=function(){return yf(Wf).scale(177.158)};function Vf(t,e){var n=xc(e),r=xc(t)*n;return[n*Tc(t)/r,Tc(e)/r]}Vf.invert=Ef(mc);var Gf=function(){return yf(Vf).scale(144.049).clipAngle(60)};function qf(t,e,n,r){return 1===t&&1===e&&0===n&&0===r?ih:rf({point:function(i,a){this.stream.point(i*t+n,a*e+r)}})}var Xf=function(){var t,e,n,r,i,a,o=1,s=0,c=0,u=1,l=1,h=ih,f=null,d=ih;function p(){return r=i=null,a}return a={stream:function(t){return r&&i===t?r:r=h(d(i=t))},postclip:function(r){return arguments.length?(d=r,f=t=e=n=null,p()):d},clipExtent:function(r){return arguments.length?(d=null==r?(f=t=e=n=null,ih):Cl(f=+r[0][0],t=+r[0][1],e=+r[1][0],n=+r[1][1]),p()):null==f?null:[[f,t],[e,n]]},scale:function(t){return arguments.length?(h=qf((o=+t)*u,o*l,s,c),p()):o},translate:function(t){return arguments.length?(h=qf(o*u,o*l,s=+t[0],c=+t[1]),p()):[s,c]},reflectX:function(t){return arguments.length?(h=qf(o*(u=t?-1:1),o*l,s,c),p()):u<0},reflectY:function(t){return arguments.length?(h=qf(o*u,o*(l=t?-1:1),s,c),p()):l<0},fitExtent:function(t,e){return sf(a,t,e)},fitSize:function(t,e){return cf(a,t,e)},fitWidth:function(t,e){return uf(a,t,e)},fitHeight:function(t,e){return lf(a,t,e)}}};function Zf(t,e){var n=e*e,r=n*n;return[t*(.8707-.131979*n+r*(r*(.003971*n-.001529*r)-.013791)),e*(1.007226+n*(.015085+r*(.028874*n-.044475-.005916*r)))]}Zf.invert=function(t,e){var n,r=e,i=25;do{var a=r*r,o=a*a;r-=n=(r*(1.007226+a*(.015085+o*(.028874*a-.044475-.005916*o)))-e)/(1.007226+a*(.045255+o*(.259866*a-.311325-.005916*11*o)))}while(vc(n)>1e-6&&--i>0);return[t/(.8707+(a=r*r)*(a*(a*a*a*(.003971-.001529*a)-.013791)-.131979)),r]};var Jf=function(){return yf(Zf).scale(175.295)};function Kf(t,e){return[xc(e)*Tc(t),Tc(e)]}Kf.invert=Ef(Oc);var Qf=function(){return yf(Kf).scale(249.5).clipAngle(90+1e-6)};function td(t,e){var n=xc(e),r=1+xc(t)*n;return[n*Tc(t)/r,Tc(e)/r]}td.invert=Ef((function(t){return 2*mc(t)}));var ed=function(){return yf(td).scale(250).clipAngle(142)};function nd(t,e){return[wc(Sc((fc+e)/2)),-t]}nd.invert=function(t,e){return[-e,2*mc(kc(t))-fc]};var rd=function(){var t=Df(nd),e=t.center,n=t.rotate;return t.center=function(t){return arguments.length?e([-t[1],t[0]]):[(t=e())[1],-t[0]]},t.rotate=function(t){return arguments.length?n([t[0],t[1],t.length>2?t[2]+90:90]):[(t=n())[0],t[1],t[2]-90]},n([0,0,90]).scale(159.155)};function id(t,e){return t.parent===e.parent?1:2}function ad(t,e){return t+e.x}function od(t,e){return Math.max(t,e.y)}var sd=function(){var t=id,e=1,n=1,r=!1;function i(i){var a,o=0;i.eachAfter((function(e){var n=e.children;n?(e.x=function(t){return t.reduce(ad,0)/t.length}(n),e.y=function(t){return 1+t.reduce(od,0)}(n)):(e.x=a?o+=t(e,a):0,e.y=0,a=e)}));var s=function(t){for(var e;e=t.children;)t=e[0];return t}(i),c=function(t){for(var e;e=t.children;)t=e[e.length-1];return t}(i),u=s.x-t(s,c)/2,l=c.x+t(c,s)/2;return i.eachAfter(r?function(t){t.x=(t.x-i.x)*e,t.y=(i.y-t.y)*n}:function(t){t.x=(t.x-u)/(l-u)*e,t.y=(1-(i.y?t.y/i.y:1))*n})}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i};function cd(t){var e=0,n=t.children,r=n&&n.length;if(r)for(;--r>=0;)e+=n[r].value;else e=1;t.value=e}function ud(t,e){var n,r,i,a,o,s=new dd(t),c=+t.value&&(s.value=t.value),u=[s];for(null==e&&(e=ld);n=u.pop();)if(c&&(n.value=+n.data.value),(i=e(n.data))&&(o=i.length))for(n.children=new Array(o),a=o-1;a>=0;--a)u.push(r=n.children[a]=new dd(i[a])),r.parent=n,r.depth=n.depth+1;return s.eachBefore(fd)}function ld(t){return t.children}function hd(t){t.data=t.data.data}function fd(t){var e=0;do{t.height=e}while((t=t.parent)&&t.height<++e)}function dd(t){this.data=t,this.depth=this.height=0,this.parent=null}dd.prototype=ud.prototype={constructor:dd,count:function(){return this.eachAfter(cd)},each:function(t){var e,n,r,i,a=this,o=[a];do{for(e=o.reverse(),o=[];a=e.pop();)if(t(a),n=a.children)for(r=0,i=n.length;r<i;++r)o.push(n[r])}while(o.length);return this},eachAfter:function(t){for(var e,n,r,i=this,a=[i],o=[];i=a.pop();)if(o.push(i),e=i.children)for(n=0,r=e.length;n<r;++n)a.push(e[n]);for(;i=o.pop();)t(i);return this},eachBefore:function(t){for(var e,n,r=this,i=[r];r=i.pop();)if(t(r),e=r.children)for(n=e.length-1;n>=0;--n)i.push(e[n]);return this},sum:function(t){return this.eachAfter((function(e){for(var n=+t(e.data)||0,r=e.children,i=r&&r.length;--i>=0;)n+=r[i].value;e.value=n}))},sort:function(t){return this.eachBefore((function(e){e.children&&e.children.sort(t)}))},path:function(t){for(var e=this,n=function(t,e){if(t===e)return t;var n=t.ancestors(),r=e.ancestors(),i=null;t=n.pop(),e=r.pop();for(;t===e;)i=t,t=n.pop(),e=r.pop();return i}(e,t),r=[e];e!==n;)e=e.parent,r.push(e);for(var i=r.length;t!==n;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,e=[t];t=t.parent;)e.push(t);return e},descendants:function(){var t=[];return this.each((function(e){t.push(e)})),t},leaves:function(){var t=[];return this.eachBefore((function(e){e.children||t.push(e)})),t},links:function(){var t=this,e=[];return t.each((function(n){n!==t&&e.push({source:n.parent,target:n})})),e},copy:function(){return ud(this).eachBefore(hd)}};var pd=Array.prototype.slice;var gd=function(t){for(var e,n,r=0,i=(t=function(t){for(var e,n,r=t.length;r;)n=Math.random()*r--|0,e=t[r],t[r]=t[n],t[n]=e;return t}(pd.call(t))).length,a=[];r<i;)e=t[r],n&&md(n,e)?++r:(n=xd(a=yd(a,e)),r=0);return n};function yd(t,e){var n,r;if(bd(e,t))return[e];for(n=0;n<t.length;++n)if(vd(e,t[n])&&bd(_d(t[n],e),t))return[t[n],e];for(n=0;n<t.length-1;++n)for(r=n+1;r<t.length;++r)if(vd(_d(t[n],t[r]),e)&&vd(_d(t[n],e),t[r])&&vd(_d(t[r],e),t[n])&&bd(kd(t[n],t[r],e),t))return[t[n],t[r],e];throw new Error}function vd(t,e){var n=t.r-e.r,r=e.x-t.x,i=e.y-t.y;return n<0||n*n<r*r+i*i}function md(t,e){var n=t.r-e.r+1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function bd(t,e){for(var n=0;n<e.length;++n)if(!md(t,e[n]))return!1;return!0}function xd(t){switch(t.length){case 1:return{x:(e=t[0]).x,y:e.y,r:e.r};case 2:return _d(t[0],t[1]);case 3:return kd(t[0],t[1],t[2])}var e}function _d(t,e){var n=t.x,r=t.y,i=t.r,a=e.x,o=e.y,s=e.r,c=a-n,u=o-r,l=s-i,h=Math.sqrt(c*c+u*u);return{x:(n+a+c/h*l)/2,y:(r+o+u/h*l)/2,r:(h+i+s)/2}}function kd(t,e,n){var r=t.x,i=t.y,a=t.r,o=e.x,s=e.y,c=e.r,u=n.x,l=n.y,h=n.r,f=r-o,d=r-u,p=i-s,g=i-l,y=c-a,v=h-a,m=r*r+i*i-a*a,b=m-o*o-s*s+c*c,x=m-u*u-l*l+h*h,_=d*p-f*g,k=(p*x-g*b)/(2*_)-r,w=(g*y-p*v)/_,E=(d*b-f*x)/(2*_)-i,T=(f*v-d*y)/_,C=w*w+T*T-1,A=2*(a+k*w+E*T),S=k*k+E*E-a*a,M=-(C?(A+Math.sqrt(A*A-4*C*S))/(2*C):S/A);return{x:r+k+w*M,y:i+E+T*M,r:M}}function wd(t,e,n){var r,i,a,o,s=t.x-e.x,c=t.y-e.y,u=s*s+c*c;u?(i=e.r+n.r,i*=i,o=t.r+n.r,i>(o*=o)?(r=(u+o-i)/(2*u),a=Math.sqrt(Math.max(0,o/u-r*r)),n.x=t.x-r*s-a*c,n.y=t.y-r*c+a*s):(r=(u+i-o)/(2*u),a=Math.sqrt(Math.max(0,i/u-r*r)),n.x=e.x+r*s-a*c,n.y=e.y+r*c+a*s)):(n.x=e.x+n.r,n.y=e.y)}function Ed(t,e){var n=t.r+e.r-1e-6,r=e.x-t.x,i=e.y-t.y;return n>0&&n*n>r*r+i*i}function Td(t){var e=t._,n=t.next._,r=e.r+n.r,i=(e.x*n.r+n.x*e.r)/r,a=(e.y*n.r+n.y*e.r)/r;return i*i+a*a}function Cd(t){this._=t,this.next=null,this.previous=null}function Ad(t){if(!(i=t.length))return 0;var e,n,r,i,a,o,s,c,u,l,h;if((e=t[0]).x=0,e.y=0,!(i>1))return e.r;if(n=t[1],e.x=-n.r,n.x=e.r,n.y=0,!(i>2))return e.r+n.r;wd(n,e,r=t[2]),e=new Cd(e),n=new Cd(n),r=new Cd(r),e.next=r.previous=n,n.next=e.previous=r,r.next=n.previous=e;t:for(s=3;s<i;++s){wd(e._,n._,r=t[s]),r=new Cd(r),c=n.next,u=e.previous,l=n._.r,h=e._.r;do{if(l<=h){if(Ed(c._,r._)){n=c,e.next=n,n.previous=e,--s;continue t}l+=c._.r,c=c.next}else{if(Ed(u._,r._)){(e=u).next=n,n.previous=e,--s;continue t}h+=u._.r,u=u.previous}}while(c!==u.next);for(r.previous=e,r.next=n,e.next=n.previous=n=r,a=Td(e);(r=r.next)!==n;)(o=Td(r))<a&&(e=r,a=o);n=e.next}for(e=[n._],r=n;(r=r.next)!==n;)e.push(r._);for(r=gd(e),s=0;s<i;++s)(e=t[s]).x-=r.x,e.y-=r.y;return r.r}var Sd=function(t){return Ad(t),t};function Md(t){return null==t?null:Od(t)}function Od(t){if("function"!=typeof t)throw new Error;return t}function Dd(){return 0}var Nd=function(t){return function(){return t}};function Bd(t){return Math.sqrt(t.value)}var Ld=function(){var t=null,e=1,n=1,r=Dd;function i(i){return i.x=e/2,i.y=n/2,t?i.eachBefore(Pd(t)).eachAfter(Id(r,.5)).eachBefore(Fd(1)):i.eachBefore(Pd(Bd)).eachAfter(Id(Dd,1)).eachAfter(Id(r,i.r/Math.min(e,n))).eachBefore(Fd(Math.min(e,n)/(2*i.r))),i}return i.radius=function(e){return arguments.length?(t=Md(e),i):t},i.size=function(t){return arguments.length?(e=+t[0],n=+t[1],i):[e,n]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:Nd(+t),i):r},i};function Pd(t){return function(e){e.children||(e.r=Math.max(0,+t(e)||0))}}function Id(t,e){return function(n){if(r=n.children){var r,i,a,o=r.length,s=t(n)*e||0;if(s)for(i=0;i<o;++i)r[i].r+=s;if(a=Ad(r),s)for(i=0;i<o;++i)r[i].r-=s;n.r=a+s}}}function Fd(t){return function(e){var n=e.parent;e.r*=t,n&&(e.x=n.x+t*e.x,e.y=n.y+t*e.y)}}var jd=function(t){t.x0=Math.round(t.x0),t.y0=Math.round(t.y0),t.x1=Math.round(t.x1),t.y1=Math.round(t.y1)},Rd=function(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(r-e)/t.value;++s<c;)(a=o[s]).y0=n,a.y1=i,a.x0=e,a.x1=e+=a.value*u},Yd=function(){var t=1,e=1,n=0,r=!1;function i(i){var a=i.height+1;return i.x0=i.y0=n,i.x1=t,i.y1=e/a,i.eachBefore(function(t,e){return function(r){r.children&&Rd(r,r.x0,t*(r.depth+1)/e,r.x1,t*(r.depth+2)/e);var i=r.x0,a=r.y0,o=r.x1-n,s=r.y1-n;o<i&&(i=o=(i+o)/2),s<a&&(a=s=(a+s)/2),r.x0=i,r.y0=a,r.x1=o,r.y1=s}}(e,a)),r&&i.eachBefore(jd),i}return i.round=function(t){return arguments.length?(r=!!t,i):r},i.size=function(n){return arguments.length?(t=+n[0],e=+n[1],i):[t,e]},i.padding=function(t){return arguments.length?(n=+t,i):n},i},zd={depth:-1},Ud={};function $d(t){return t.id}function Wd(t){return t.parentId}var Hd=function(){var t=$d,e=Wd;function n(n){var r,i,a,o,s,c,u,l=n.length,h=new Array(l),f={};for(i=0;i<l;++i)r=n[i],s=h[i]=new dd(r),null!=(c=t(r,i,n))&&(c+="")&&(f[u="$"+(s.id=c)]=u in f?Ud:s);for(i=0;i<l;++i)if(s=h[i],null!=(c=e(n[i],i,n))&&(c+="")){if(!(o=f["$"+c]))throw new Error("missing: "+c);if(o===Ud)throw new Error("ambiguous: "+c);o.children?o.children.push(s):o.children=[s],s.parent=o}else{if(a)throw new Error("multiple roots");a=s}if(!a)throw new Error("no root");if(a.parent=zd,a.eachBefore((function(t){t.depth=t.parent.depth+1,--l})).eachBefore(fd),a.parent=null,l>0)throw new Error("cycle");return a}return n.id=function(e){return arguments.length?(t=Od(e),n):t},n.parentId=function(t){return arguments.length?(e=Od(t),n):e},n};function Vd(t,e){return t.parent===e.parent?1:2}function Gd(t){var e=t.children;return e?e[0]:t.t}function qd(t){var e=t.children;return e?e[e.length-1]:t.t}function Xd(t,e,n){var r=n/(e.i-t.i);e.c-=r,e.s+=n,t.c+=r,e.z+=n,e.m+=n}function Zd(t,e,n){return t.a.parent===e.parent?t.a:n}function Jd(t,e){this._=t,this.parent=null,this.children=null,this.A=null,this.a=this,this.z=0,this.m=0,this.c=0,this.s=0,this.t=null,this.i=e}Jd.prototype=Object.create(dd.prototype);var Kd=function(){var t=Vd,e=1,n=1,r=null;function i(i){var c=function(t){for(var e,n,r,i,a,o=new Jd(t,0),s=[o];e=s.pop();)if(r=e._.children)for(e.children=new Array(a=r.length),i=a-1;i>=0;--i)s.push(n=e.children[i]=new Jd(r[i],i)),n.parent=e;return(o.parent=new Jd(null,0)).children=[o],o}(i);if(c.eachAfter(a),c.parent.m=-c.z,c.eachBefore(o),r)i.eachBefore(s);else{var u=i,l=i,h=i;i.eachBefore((function(t){t.x<u.x&&(u=t),t.x>l.x&&(l=t),t.depth>h.depth&&(h=t)}));var f=u===l?1:t(u,l)/2,d=f-u.x,p=e/(l.x+f+d),g=n/(h.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function a(e){var n=e.children,r=e.parent.children,i=e.i?r[e.i-1]:null;if(n){!function(t){for(var e,n=0,r=0,i=t.children,a=i.length;--a>=0;)(e=i[a]).z+=n,e.m+=n,n+=e.s+(r+=e.c)}(e);var a=(n[0].z+n[n.length-1].z)/2;i?(e.z=i.z+t(e._,i._),e.m=e.z-a):e.z=a}else i&&(e.z=i.z+t(e._,i._));e.parent.A=function(e,n,r){if(n){for(var i,a=e,o=e,s=n,c=a.parent.children[0],u=a.m,l=o.m,h=s.m,f=c.m;s=qd(s),a=Gd(a),s&&a;)c=Gd(c),(o=qd(o)).a=e,(i=s.z+h-a.z-u+t(s._,a._))>0&&(Xd(Zd(s,e,r),e,i),u+=i,l+=i),h+=s.m,u+=a.m,f+=c.m,l+=o.m;s&&!qd(o)&&(o.t=s,o.m+=h-l),a&&!Gd(c)&&(c.t=a,c.m+=u-f,r=e)}return r}(e,i,e.parent.A||r[0])}function o(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function s(t){t.x*=e,t.y=t.depth*n}return i.separation=function(e){return arguments.length?(t=e,i):t},i.size=function(t){return arguments.length?(r=!1,e=+t[0],n=+t[1],i):r?null:[e,n]},i.nodeSize=function(t){return arguments.length?(r=!0,e=+t[0],n=+t[1],i):r?[e,n]:null},i},Qd=function(t,e,n,r,i){for(var a,o=t.children,s=-1,c=o.length,u=t.value&&(i-n)/t.value;++s<c;)(a=o[s]).x0=e,a.x1=r,a.y0=n,a.y1=n+=a.value*u},tp=(1+Math.sqrt(5))/2;function ep(t,e,n,r,i,a){for(var o,s,c,u,l,h,f,d,p,g,y,v=[],m=e.children,b=0,x=0,_=m.length,k=e.value;b<_;){c=i-n,u=a-r;do{l=m[x++].value}while(!l&&x<_);for(h=f=l,y=l*l*(g=Math.max(u/c,c/u)/(k*t)),p=Math.max(f/y,y/h);x<_;++x){if(l+=s=m[x].value,s<h&&(h=s),s>f&&(f=s),y=l*l*g,(d=Math.max(f/y,y/h))>p){l-=s;break}p=d}v.push(o={value:l,dice:c<u,children:m.slice(b,x)}),o.dice?Rd(o,n,r,i,k?r+=u*l/k:a):Qd(o,n,r,k?n+=c*l/k:i,a),k-=l,b=x}return v}var np=function t(e){function n(t,n,r,i,a){ep(e,t,n,r,i,a)}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(tp),rp=function(){var t=np,e=!1,n=1,r=1,i=[0],a=Dd,o=Dd,s=Dd,c=Dd,u=Dd;function l(t){return t.x0=t.y0=0,t.x1=n,t.y1=r,t.eachBefore(h),i=[0],e&&t.eachBefore(jd),t}function h(e){var n=i[e.depth],r=e.x0+n,l=e.y0+n,h=e.x1-n,f=e.y1-n;h<r&&(r=h=(r+h)/2),f<l&&(l=f=(l+f)/2),e.x0=r,e.y0=l,e.x1=h,e.y1=f,e.children&&(n=i[e.depth+1]=a(e)/2,r+=u(e)-n,l+=o(e)-n,(h-=s(e)-n)<r&&(r=h=(r+h)/2),(f-=c(e)-n)<l&&(l=f=(l+f)/2),t(e,r,l,h,f))}return l.round=function(t){return arguments.length?(e=!!t,l):e},l.size=function(t){return arguments.length?(n=+t[0],r=+t[1],l):[n,r]},l.tile=function(e){return arguments.length?(t=Od(e),l):t},l.padding=function(t){return arguments.length?l.paddingInner(t).paddingOuter(t):l.paddingInner()},l.paddingInner=function(t){return arguments.length?(a="function"==typeof t?t:Nd(+t),l):a},l.paddingOuter=function(t){return arguments.length?l.paddingTop(t).paddingRight(t).paddingBottom(t).paddingLeft(t):l.paddingTop()},l.paddingTop=function(t){return arguments.length?(o="function"==typeof t?t:Nd(+t),l):o},l.paddingRight=function(t){return arguments.length?(s="function"==typeof t?t:Nd(+t),l):s},l.paddingBottom=function(t){return arguments.length?(c="function"==typeof t?t:Nd(+t),l):c},l.paddingLeft=function(t){return arguments.length?(u="function"==typeof t?t:Nd(+t),l):u},l},ip=function(t,e,n,r,i){var a,o,s=t.children,c=s.length,u=new Array(c+1);for(u[0]=o=a=0;a<c;++a)u[a+1]=o+=s[a].value;!function t(e,n,r,i,a,o,c){if(e>=n-1){var l=s[e];return l.x0=i,l.y0=a,l.x1=o,void(l.y1=c)}var h=u[e],f=r/2+h,d=e+1,p=n-1;for(;d<p;){var g=d+p>>>1;u[g]<f?d=g+1:p=g}f-u[d-1]<u[d]-f&&e+1<d&&--d;var y=u[d]-h,v=r-y;if(o-i>c-a){var m=(i*v+o*y)/r;t(e,d,y,i,a,m,c),t(d,n,v,m,a,o,c)}else{var b=(a*v+c*y)/r;t(e,d,y,i,a,o,b),t(d,n,v,i,b,o,c)}}(0,c,t.value,e,n,r,i)},ap=function(t,e,n,r,i){(1&t.depth?Qd:Rd)(t,e,n,r,i)},op=function t(e){function n(t,n,r,i,a){if((o=t._squarify)&&o.ratio===e)for(var o,s,c,u,l,h=-1,f=o.length,d=t.value;++h<f;){for(c=(s=o[h]).children,u=s.value=0,l=c.length;u<l;++u)s.value+=c[u].value;s.dice?Rd(s,n,r,i,r+=(a-r)*s.value/d):Qd(s,n,r,n+=(i-n)*s.value/d,a),d-=s.value}else t._squarify=o=ep(e,t,n,r,i,a),o.ratio=e}return n.ratio=function(e){return t((e=+e)>1?e:1)},n}(tp),sp=function(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}},cp=function(t,e){var n=un(+t,+e);return function(t){var e=n(t);return e-360*Math.floor(e/360)}},up=function(t,e){return t=+t,e=+e,function(n){return Math.round(t*(1-n)+e*n)}},lp=Math.SQRT2;function hp(t){return((t=Math.exp(t))+1/t)/2}var fp=function(t,e){var n,r,i=t[0],a=t[1],o=t[2],s=e[0],c=e[1],u=e[2],l=s-i,h=c-a,f=l*l+h*h;if(f<1e-12)r=Math.log(u/o)/lp,n=function(t){return[i+t*l,a+t*h,o*Math.exp(lp*t*r)]};else{var d=Math.sqrt(f),p=(u*u-o*o+4*f)/(2*o*2*d),g=(u*u-o*o-4*f)/(2*u*2*d),y=Math.log(Math.sqrt(p*p+1)-p),v=Math.log(Math.sqrt(g*g+1)-g);r=(v-y)/lp,n=function(t){var e,n=t*r,s=hp(y),c=o/(2*d)*(s*(e=lp*n+y,((e=Math.exp(2*e))-1)/(e+1))-function(t){return((t=Math.exp(t))-1/t)/2}(y));return[i+c*l,a+c*h,o*s/hp(lp*n+y)]}}return n.duration=1e3*r,n};function dp(t){return function(e,n){var r=t((e=tn(e)).h,(n=tn(n)).h),i=hn(e.s,n.s),a=hn(e.l,n.l),o=hn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.s=i(t),e.l=a(t),e.opacity=o(t),e+""}}}var pp=dp(un),gp=dp(hn);function yp(t,e){var n=hn((t=pa(t)).l,(e=pa(e)).l),r=hn(t.a,e.a),i=hn(t.b,e.b),a=hn(t.opacity,e.opacity);return function(e){return t.l=n(e),t.a=r(e),t.b=i(e),t.opacity=a(e),t+""}}function vp(t){return function(e,n){var r=t((e=ka(e)).h,(n=ka(n)).h),i=hn(e.c,n.c),a=hn(e.l,n.l),o=hn(e.opacity,n.opacity);return function(t){return e.h=r(t),e.c=i(t),e.l=a(t),e.opacity=o(t),e+""}}}var mp=vp(un),bp=vp(hn);function xp(t){return function e(n){function r(e,r){var i=t((e=Oa(e)).h,(r=Oa(r)).h),a=hn(e.s,r.s),o=hn(e.l,r.l),s=hn(e.opacity,r.opacity);return function(t){return e.h=i(t),e.s=a(t),e.l=o(Math.pow(t,n)),e.opacity=s(t),e+""}}return n=+n,r.gamma=e,r}(1)}var _p=xp(un),kp=xp(hn);function wp(t,e){for(var n=0,r=e.length-1,i=e[0],a=new Array(r<0?0:r);n<r;)a[n]=t(i,i=e[++n]);return function(t){var e=Math.max(0,Math.min(r-1,Math.floor(t*=r)));return a[e](t-e)}}var Ep=function(t,e){for(var n=new Array(e),r=0;r<e;++r)n[r]=t(r/(e-1));return n},Tp=function(t){for(var e,n=-1,r=t.length,i=t[r-1],a=0;++n<r;)e=i,i=t[n],a+=e[1]*i[0]-e[0]*i[1];return a/2},Cp=function(t){for(var e,n,r=-1,i=t.length,a=0,o=0,s=t[i-1],c=0;++r<i;)e=s,s=t[r],c+=n=e[0]*s[1]-s[0]*e[1],a+=(e[0]+s[0])*n,o+=(e[1]+s[1])*n;return[a/(c*=3),o/c]};function Ap(t,e){return t[0]-e[0]||t[1]-e[1]}function Sp(t){for(var e,n,r,i=t.length,a=[0,1],o=2,s=2;s<i;++s){for(;o>1&&(e=t[a[o-2]],n=t[a[o-1]],r=t[s],(n[0]-e[0])*(r[1]-e[1])-(n[1]-e[1])*(r[0]-e[0])<=0);)--o;a[o++]=s}return a.slice(0,o)}var Mp=function(t){if((n=t.length)<3)return null;var e,n,r=new Array(n),i=new Array(n);for(e=0;e<n;++e)r[e]=[+t[e][0],+t[e][1],e];for(r.sort(Ap),e=0;e<n;++e)i[e]=[r[e][0],-r[e][1]];var a=Sp(r),o=Sp(i),s=o[0]===a[0],c=o[o.length-1]===a[a.length-1],u=[];for(e=a.length-1;e>=0;--e)u.push(t[r[a[e]][2]]);for(e=+s;e<o.length-c;++e)u.push(t[r[o[e]][2]]);return u},Op=function(t,e){for(var n,r,i=t.length,a=t[i-1],o=e[0],s=e[1],c=a[0],u=a[1],l=!1,h=0;h<i;++h)n=(a=t[h])[0],(r=a[1])>s!=u>s&&o<(c-n)*(s-r)/(u-r)+n&&(l=!l),c=n,u=r;return l},Dp=function(t){for(var e,n,r=-1,i=t.length,a=t[i-1],o=a[0],s=a[1],c=0;++r<i;)e=o,n=s,e-=o=(a=t[r])[0],n-=s=a[1],c+=Math.sqrt(e*e+n*n);return c},Np=function(){return Math.random()},Bp=function t(e){function n(t,n){return t=null==t?0:+t,n=null==n?1:+n,1===arguments.length?(n=t,t=0):n-=t,function(){return e()*n+t}}return n.source=t,n}(Np),Lp=function t(e){function n(t,n){var r,i;return t=null==t?0:+t,n=null==n?1:+n,function(){var a;if(null!=r)a=r,r=null;else do{r=2*e()-1,a=2*e()-1,i=r*r+a*a}while(!i||i>1);return t+n*a*Math.sqrt(-2*Math.log(i)/i)}}return n.source=t,n}(Np),Pp=function t(e){function n(){var t=Lp.source(e).apply(this,arguments);return function(){return Math.exp(t())}}return n.source=t,n}(Np),Ip=function t(e){function n(t){return function(){for(var n=0,r=0;r<t;++r)n+=e();return n}}return n.source=t,n}(Np),Fp=function t(e){function n(t){var n=Ip.source(e)(t);return function(){return n()/t}}return n.source=t,n}(Np),jp=function t(e){function n(t){return function(){return-Math.log(1-e())/t}}return n.source=t,n}(Np);function Rp(t,e){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(e).domain(t)}return this}function Yp(t,e){switch(arguments.length){case 0:break;case 1:this.interpolator(t);break;default:this.interpolator(e).domain(t)}return this}var zp=Array.prototype,Up=zp.map,$p=zp.slice,Wp={name:"implicit"};function Hp(){var t=Ji(),e=[],n=[],r=Wp;function i(i){var a=i+"",o=t.get(a);if(!o){if(r!==Wp)return r;t.set(a,o=e.push(i))}return n[(o-1)%n.length]}return i.domain=function(n){if(!arguments.length)return e.slice();e=[],t=Ji();for(var r,a,o=-1,s=n.length;++o<s;)t.has(a=(r=n[o])+"")||t.set(a,e.push(r));return i},i.range=function(t){return arguments.length?(n=$p.call(t),i):n.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return Hp(e,n).unknown(r)},Rp.apply(i,arguments),i}function Vp(){var t,e,n=Hp().unknown(void 0),r=n.domain,i=n.range,a=[0,1],o=!1,s=0,c=0,u=.5;function l(){var n=r().length,l=a[1]<a[0],h=a[l-0],f=a[1-l];t=(f-h)/Math.max(1,n-s+2*c),o&&(t=Math.floor(t)),h+=(f-h-t*(n-s))*u,e=t*(1-s),o&&(h=Math.round(h),e=Math.round(e));var d=k(n).map((function(e){return h+t*e}));return i(l?d.reverse():d)}return delete n.unknown,n.domain=function(t){return arguments.length?(r(t),l()):r()},n.range=function(t){return arguments.length?(a=[+t[0],+t[1]],l()):a.slice()},n.rangeRound=function(t){return a=[+t[0],+t[1]],o=!0,l()},n.bandwidth=function(){return e},n.step=function(){return t},n.round=function(t){return arguments.length?(o=!!t,l()):o},n.padding=function(t){return arguments.length?(s=Math.min(1,c=+t),l()):s},n.paddingInner=function(t){return arguments.length?(s=Math.min(1,t),l()):s},n.paddingOuter=function(t){return arguments.length?(c=+t,l()):c},n.align=function(t){return arguments.length?(u=Math.max(0,Math.min(1,t)),l()):u},n.copy=function(){return Vp(r(),a).round(o).paddingInner(s).paddingOuter(c).align(u)},Rp.apply(l(),arguments)}function Gp(t){var e=t.copy;return t.padding=t.paddingOuter,delete t.paddingInner,delete t.paddingOuter,t.copy=function(){return Gp(e())},t}function qp(){return Gp(Vp.apply(null,arguments).paddingInner(1))}var Xp=function(t){return+t},Zp=[0,1];function Jp(t){return t}function Kp(t,e){return(e-=t=+t)?function(n){return(n-t)/e}:(n=isNaN(e)?NaN:.5,function(){return n});var n}function Qp(t){var e,n=t[0],r=t[t.length-1];return n>r&&(e=n,n=r,r=e),function(t){return Math.max(n,Math.min(r,t))}}function tg(t,e,n){var r=t[0],i=t[1],a=e[0],o=e[1];return i<r?(r=Kp(i,r),a=n(o,a)):(r=Kp(r,i),a=n(a,o)),function(t){return a(r(t))}}function eg(t,e,n){var r=Math.min(t.length,e.length)-1,i=new Array(r),a=new Array(r),o=-1;for(t[r]<t[0]&&(t=t.slice().reverse(),e=e.slice().reverse());++o<r;)i[o]=Kp(t[o],t[o+1]),a[o]=n(e[o],e[o+1]);return function(e){var n=c(t,e,1,r)-1;return a[n](i[n](e))}}function ng(t,e){return e.domain(t.domain()).range(t.range()).interpolate(t.interpolate()).clamp(t.clamp()).unknown(t.unknown())}function rg(){var t,e,n,r,i,a,o=Zp,s=Zp,c=Sn,u=Jp;function l(){return r=Math.min(o.length,s.length)>2?eg:tg,i=a=null,h}function h(e){return isNaN(e=+e)?n:(i||(i=r(o.map(t),s,c)))(t(u(e)))}return h.invert=function(n){return u(e((a||(a=r(s,o.map(t),_n)))(n)))},h.domain=function(t){return arguments.length?(o=Up.call(t,Xp),u===Jp||(u=Qp(o)),l()):o.slice()},h.range=function(t){return arguments.length?(s=$p.call(t),l()):s.slice()},h.rangeRound=function(t){return s=$p.call(t),c=up,l()},h.clamp=function(t){return arguments.length?(u=t?Qp(o):Jp,h):u!==Jp},h.interpolate=function(t){return arguments.length?(c=t,l()):c},h.unknown=function(t){return arguments.length?(n=t,h):n},function(n,r){return t=n,e=r,l()}}function ig(t,e){return rg()(t,e)}var ag=function(t,e,n,r){var i,a=S(t,e,n);switch((r=Hs(null==r?",f":r)).type){case"s":var o=Math.max(Math.abs(t),Math.abs(e));return null!=r.precision||isNaN(i=ac(a,o))||(r.precision=i),Zs(r,o);case"":case"e":case"g":case"p":case"r":null!=r.precision||isNaN(i=oc(a,Math.max(Math.abs(t),Math.abs(e))))||(r.precision=i-("e"===r.type));break;case"f":case"%":null!=r.precision||isNaN(i=ic(a))||(r.precision=i-2*("%"===r.type))}return Xs(r)};function og(t){var e=t.domain;return t.ticks=function(t){var n=e();return C(n[0],n[n.length-1],null==t?10:t)},t.tickFormat=function(t,n){var r=e();return ag(r[0],r[r.length-1],null==t?10:t,n)},t.nice=function(n){null==n&&(n=10);var r,i=e(),a=0,o=i.length-1,s=i[a],c=i[o];return c<s&&(r=s,s=c,c=r,r=a,a=o,o=r),(r=A(s,c,n))>0?r=A(s=Math.floor(s/r)*r,c=Math.ceil(c/r)*r,n):r<0&&(r=A(s=Math.ceil(s*r)/r,c=Math.floor(c*r)/r,n)),r>0?(i[a]=Math.floor(s/r)*r,i[o]=Math.ceil(c/r)*r,e(i)):r<0&&(i[a]=Math.ceil(s*r)/r,i[o]=Math.floor(c*r)/r,e(i)),t},t}function sg(){var t=ig(Jp,Jp);return t.copy=function(){return ng(t,sg())},Rp.apply(t,arguments),og(t)}function cg(t){var e;function n(t){return isNaN(t=+t)?e:t}return n.invert=n,n.domain=n.range=function(e){return arguments.length?(t=Up.call(e,Xp),n):t.slice()},n.unknown=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return cg(t).unknown(e)},t=arguments.length?Up.call(t,Xp):[0,1],og(n)}var ug=function(t,e){var n,r=0,i=(t=t.slice()).length-1,a=t[r],o=t[i];return o<a&&(n=r,r=i,i=n,n=a,a=o,o=n),t[r]=e.floor(a),t[i]=e.ceil(o),t};function lg(t){return Math.log(t)}function hg(t){return Math.exp(t)}function fg(t){return-Math.log(-t)}function dg(t){return-Math.exp(-t)}function pg(t){return isFinite(t)?+("1e"+t):t<0?0:t}function gg(t){return function(e){return-t(-e)}}function yg(t){var e,n,r=t(lg,hg),i=r.domain,a=10;function o(){return e=function(t){return t===Math.E?Math.log:10===t&&Math.log10||2===t&&Math.log2||(t=Math.log(t),function(e){return Math.log(e)/t})}(a),n=function(t){return 10===t?pg:t===Math.E?Math.exp:function(e){return Math.pow(t,e)}}(a),i()[0]<0?(e=gg(e),n=gg(n),t(fg,dg)):t(lg,hg),r}return r.base=function(t){return arguments.length?(a=+t,o()):a},r.domain=function(t){return arguments.length?(i(t),o()):i()},r.ticks=function(t){var r,o=i(),s=o[0],c=o[o.length-1];(r=c<s)&&(f=s,s=c,c=f);var u,l,h,f=e(s),d=e(c),p=null==t?10:+t,g=[];if(!(a%1)&&d-f<p){if(f=Math.round(f)-1,d=Math.round(d)+1,s>0){for(;f<d;++f)for(l=1,u=n(f);l<a;++l)if(!((h=u*l)<s)){if(h>c)break;g.push(h)}}else for(;f<d;++f)for(l=a-1,u=n(f);l>=1;--l)if(!((h=u*l)<s)){if(h>c)break;g.push(h)}}else g=C(f,d,Math.min(d-f,p)).map(n);return r?g.reverse():g},r.tickFormat=function(t,i){if(null==i&&(i=10===a?".0e":","),"function"!=typeof i&&(i=Xs(i)),t===1/0)return i;null==t&&(t=10);var o=Math.max(1,a*t/r.ticks().length);return function(t){var r=t/n(Math.round(e(t)));return r*a<a-.5&&(r*=a),r<=o?i(t):""}},r.nice=function(){return i(ug(i(),{floor:function(t){return n(Math.floor(e(t)))},ceil:function(t){return n(Math.ceil(e(t)))}}))},r}function vg(){var t=yg(rg()).domain([1,10]);return t.copy=function(){return ng(t,vg()).base(t.base())},Rp.apply(t,arguments),t}function mg(t){return function(e){return Math.sign(e)*Math.log1p(Math.abs(e/t))}}function bg(t){return function(e){return Math.sign(e)*Math.expm1(Math.abs(e))*t}}function xg(t){var e=1,n=t(mg(e),bg(e));return n.constant=function(n){return arguments.length?t(mg(e=+n),bg(e)):e},og(n)}function _g(){var t=xg(rg());return t.copy=function(){return ng(t,_g()).constant(t.constant())},Rp.apply(t,arguments)}function kg(t){return function(e){return e<0?-Math.pow(-e,t):Math.pow(e,t)}}function wg(t){return t<0?-Math.sqrt(-t):Math.sqrt(t)}function Eg(t){return t<0?-t*t:t*t}function Tg(t){var e=t(Jp,Jp),n=1;function r(){return 1===n?t(Jp,Jp):.5===n?t(wg,Eg):t(kg(n),kg(1/n))}return e.exponent=function(t){return arguments.length?(n=+t,r()):n},og(e)}function Cg(){var t=Tg(rg());return t.copy=function(){return ng(t,Cg()).exponent(t.exponent())},Rp.apply(t,arguments),t}function Ag(){return Cg.apply(null,arguments).exponent(.5)}function Sg(){var t,e=[],n=[],i=[];function a(){var t=0,r=Math.max(1,n.length);for(i=new Array(r-1);++t<r;)i[t-1]=D(e,t/r);return o}function o(e){return isNaN(e=+e)?t:n[c(i,e)]}return o.invertExtent=function(t){var r=n.indexOf(t);return r<0?[NaN,NaN]:[r>0?i[r-1]:e[0],r<i.length?i[r]:e[e.length-1]]},o.domain=function(t){if(!arguments.length)return e.slice();e=[];for(var n,i=0,o=t.length;i<o;++i)null==(n=t[i])||isNaN(n=+n)||e.push(n);return e.sort(r),a()},o.range=function(t){return arguments.length?(n=$p.call(t),a()):n.slice()},o.unknown=function(e){return arguments.length?(t=e,o):t},o.quantiles=function(){return i.slice()},o.copy=function(){return Sg().domain(e).range(n).unknown(t)},Rp.apply(o,arguments)}function Mg(){var t,e=0,n=1,r=1,i=[.5],a=[0,1];function o(e){return e<=e?a[c(i,e,0,r)]:t}function s(){var t=-1;for(i=new Array(r);++t<r;)i[t]=((t+1)*n-(t-r)*e)/(r+1);return o}return o.domain=function(t){return arguments.length?(e=+t[0],n=+t[1],s()):[e,n]},o.range=function(t){return arguments.length?(r=(a=$p.call(t)).length-1,s()):a.slice()},o.invertExtent=function(t){var o=a.indexOf(t);return o<0?[NaN,NaN]:o<1?[e,i[0]]:o>=r?[i[r-1],n]:[i[o-1],i[o]]},o.unknown=function(e){return arguments.length?(t=e,o):o},o.thresholds=function(){return i.slice()},o.copy=function(){return Mg().domain([e,n]).range(a).unknown(t)},Rp.apply(og(o),arguments)}function Og(){var t,e=[.5],n=[0,1],r=1;function i(i){return i<=i?n[c(e,i,0,r)]:t}return i.domain=function(t){return arguments.length?(e=$p.call(t),r=Math.min(e.length,n.length-1),i):e.slice()},i.range=function(t){return arguments.length?(n=$p.call(t),r=Math.min(e.length,n.length-1),i):n.slice()},i.invertExtent=function(t){var r=n.indexOf(t);return[e[r-1],e[r]]},i.unknown=function(e){return arguments.length?(t=e,i):t},i.copy=function(){return Og().domain(e).range(n).unknown(t)},Rp.apply(i,arguments)}var Dg=new Date,Ng=new Date;function Bg(t,e,n,r){function i(e){return t(e=0===arguments.length?new Date:new Date(+e)),e}return i.floor=function(e){return t(e=new Date(+e)),e},i.ceil=function(n){return t(n=new Date(n-1)),e(n,1),t(n),n},i.round=function(t){var e=i(t),n=i.ceil(t);return t-e<n-t?e:n},i.offset=function(t,n){return e(t=new Date(+t),null==n?1:Math.floor(n)),t},i.range=function(n,r,a){var o,s=[];if(n=i.ceil(n),a=null==a?1:Math.floor(a),!(n<r&&a>0))return s;do{s.push(o=new Date(+n)),e(n,a),t(n)}while(o<n&&n<r);return s},i.filter=function(n){return Bg((function(e){if(e>=e)for(;t(e),!n(e);)e.setTime(e-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;e(t,-1),!n(t););else for(;--r>=0;)for(;e(t,1),!n(t););}))},n&&(i.count=function(e,r){return Dg.setTime(+e),Ng.setTime(+r),t(Dg),t(Ng),Math.floor(n(Dg,Ng))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(e){return r(e)%t==0}:function(e){return i.count(0,e)%t==0}):i:null}),i}var Lg=Bg((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,e){t.setFullYear(t.getFullYear()+e)}),(function(t,e){return e.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));Lg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bg((function(e){e.setFullYear(Math.floor(e.getFullYear()/t)*t),e.setMonth(0,1),e.setHours(0,0,0,0)}),(function(e,n){e.setFullYear(e.getFullYear()+n*t)})):null};var Pg=Lg,Ig=Lg.range,Fg=Bg((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,e){t.setMonth(t.getMonth()+e)}),(function(t,e){return e.getMonth()-t.getMonth()+12*(e.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})),jg=Fg,Rg=Fg.range;function Yg(t){return Bg((function(e){e.setDate(e.getDate()-(e.getDay()+7-t)%7),e.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+7*e)}),(function(t,e){return(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/6048e5}))}var zg=Yg(0),Ug=Yg(1),$g=Yg(2),Wg=Yg(3),Hg=Yg(4),Vg=Yg(5),Gg=Yg(6),qg=zg.range,Xg=Ug.range,Zg=$g.range,Jg=Wg.range,Kg=Hg.range,Qg=Vg.range,ty=Gg.range,ey=Bg((function(t){t.setHours(0,0,0,0)}),(function(t,e){t.setDate(t.getDate()+e)}),(function(t,e){return(e-t-6e4*(e.getTimezoneOffset()-t.getTimezoneOffset()))/864e5}),(function(t){return t.getDate()-1})),ny=ey,ry=ey.range,iy=Bg((function(t){t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds()-6e4*t.getMinutes())}),(function(t,e){t.setTime(+t+36e5*e)}),(function(t,e){return(e-t)/36e5}),(function(t){return t.getHours()})),ay=iy,oy=iy.range,sy=Bg((function(t){t.setTime(t-t.getMilliseconds()-1e3*t.getSeconds())}),(function(t,e){t.setTime(+t+6e4*e)}),(function(t,e){return(e-t)/6e4}),(function(t){return t.getMinutes()})),cy=sy,uy=sy.range,ly=Bg((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,e){t.setTime(+t+1e3*e)}),(function(t,e){return(e-t)/1e3}),(function(t){return t.getUTCSeconds()})),hy=ly,fy=ly.range,dy=Bg((function(){}),(function(t,e){t.setTime(+t+e)}),(function(t,e){return e-t}));dy.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Bg((function(e){e.setTime(Math.floor(e/t)*t)}),(function(e,n){e.setTime(+e+n*t)}),(function(e,n){return(n-e)/t})):dy:null};var py=dy,gy=dy.range;function yy(t){return Bg((function(e){e.setUTCDate(e.getUTCDate()-(e.getUTCDay()+7-t)%7),e.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+7*e)}),(function(t,e){return(e-t)/6048e5}))}var vy=yy(0),my=yy(1),by=yy(2),xy=yy(3),_y=yy(4),ky=yy(5),wy=yy(6),Ey=vy.range,Ty=my.range,Cy=by.range,Ay=xy.range,Sy=_y.range,My=ky.range,Oy=wy.range,Dy=Bg((function(t){t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCDate(t.getUTCDate()+e)}),(function(t,e){return(e-t)/864e5}),(function(t){return t.getUTCDate()-1})),Ny=Dy,By=Dy.range,Ly=Bg((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCFullYear(t.getUTCFullYear()+e)}),(function(t,e){return e.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));Ly.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bg((function(e){e.setUTCFullYear(Math.floor(e.getUTCFullYear()/t)*t),e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)}),(function(e,n){e.setUTCFullYear(e.getUTCFullYear()+n*t)})):null};var Py=Ly,Iy=Ly.range;function Fy(t){if(0<=t.y&&t.y<100){var e=new Date(-1,t.m,t.d,t.H,t.M,t.S,t.L);return e.setFullYear(t.y),e}return new Date(t.y,t.m,t.d,t.H,t.M,t.S,t.L)}function jy(t){if(0<=t.y&&t.y<100){var e=new Date(Date.UTC(-1,t.m,t.d,t.H,t.M,t.S,t.L));return e.setUTCFullYear(t.y),e}return new Date(Date.UTC(t.y,t.m,t.d,t.H,t.M,t.S,t.L))}function Ry(t,e,n){return{y:t,m:e,d:n,H:0,M:0,S:0,L:0}}function Yy(t){var e=t.dateTime,n=t.date,r=t.time,i=t.periods,a=t.days,o=t.shortDays,s=t.months,c=t.shortMonths,u=Ky(i),l=Qy(i),h=Ky(a),f=Qy(a),d=Ky(o),p=Qy(o),g=Ky(s),y=Qy(s),v=Ky(c),m=Qy(c),b={a:function(t){return o[t.getDay()]},A:function(t){return a[t.getDay()]},b:function(t){return c[t.getMonth()]},B:function(t){return s[t.getMonth()]},c:null,d:xv,e:xv,f:Tv,H:_v,I:kv,j:wv,L:Ev,m:Cv,M:Av,p:function(t){return i[+(t.getHours()>=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:em,s:nm,S:Sv,u:Mv,U:Ov,V:Dv,w:Nv,W:Bv,x:null,X:null,y:Lv,Y:Pv,Z:Iv,"%":tm},x={a:function(t){return o[t.getUTCDay()]},A:function(t){return a[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return s[t.getUTCMonth()]},c:null,d:Fv,e:Fv,f:Uv,H:jv,I:Rv,j:Yv,L:zv,m:$v,M:Wv,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:em,s:nm,S:Hv,u:Vv,U:Gv,V:qv,w:Xv,W:Zv,x:null,X:null,y:Jv,Y:Kv,Z:Qv,"%":tm},_={a:function(t,e,n){var r=d.exec(e.slice(n));return r?(t.w=p[r[0].toLowerCase()],n+r[0].length):-1},A:function(t,e,n){var r=h.exec(e.slice(n));return r?(t.w=f[r[0].toLowerCase()],n+r[0].length):-1},b:function(t,e,n){var r=v.exec(e.slice(n));return r?(t.m=m[r[0].toLowerCase()],n+r[0].length):-1},B:function(t,e,n){var r=g.exec(e.slice(n));return r?(t.m=y[r[0].toLowerCase()],n+r[0].length):-1},c:function(t,n,r){return E(t,e,n,r)},d:lv,e:lv,f:yv,H:fv,I:fv,j:hv,L:gv,m:uv,M:dv,p:function(t,e,n){var r=u.exec(e.slice(n));return r?(t.p=l[r[0].toLowerCase()],n+r[0].length):-1},q:cv,Q:mv,s:bv,S:pv,u:ev,U:nv,V:rv,w:tv,W:iv,x:function(t,e,r){return E(t,n,e,r)},X:function(t,e,n){return E(t,r,e,n)},y:ov,Y:av,Z:sv,"%":vv};function k(t,e){return function(n){var r,i,a,o=[],s=-1,c=0,u=t.length;for(n instanceof Date||(n=new Date(+n));++s<u;)37===t.charCodeAt(s)&&(o.push(t.slice(c,s)),null!=(i=Vy[r=t.charAt(++s)])?r=t.charAt(++s):i="e"===r?" ":"0",(a=e[r])&&(r=a(n,i)),o.push(r),c=s+1);return o.push(t.slice(c,s)),o.join("")}}function w(t,e){return function(n){var r,i,a=Ry(1900,void 0,1);if(E(a,t,n+="",0)!=n.length)return null;if("Q"in a)return new Date(a.Q);if("s"in a)return new Date(1e3*a.s+("L"in a?a.L:0));if(!e||"Z"in a||(a.Z=0),"p"in a&&(a.H=a.H%12+12*a.p),void 0===a.m&&(a.m="q"in a?a.q:0),"V"in a){if(a.V<1||a.V>53)return null;"w"in a||(a.w=1),"Z"in a?(i=(r=jy(Ry(a.y,0,1))).getUTCDay(),r=i>4||0===i?my.ceil(r):my(r),r=Ny.offset(r,7*(a.V-1)),a.y=r.getUTCFullYear(),a.m=r.getUTCMonth(),a.d=r.getUTCDate()+(a.w+6)%7):(i=(r=Fy(Ry(a.y,0,1))).getDay(),r=i>4||0===i?Ug.ceil(r):Ug(r),r=ny.offset(r,7*(a.V-1)),a.y=r.getFullYear(),a.m=r.getMonth(),a.d=r.getDate()+(a.w+6)%7)}else("W"in a||"U"in a)&&("w"in a||(a.w="u"in a?a.u%7:"W"in a?1:0),i="Z"in a?jy(Ry(a.y,0,1)).getUTCDay():Fy(Ry(a.y,0,1)).getDay(),a.m=0,a.d="W"in a?(a.w+6)%7+7*a.W-(i+5)%7:a.w+7*a.U-(i+6)%7);return"Z"in a?(a.H+=a.Z/100|0,a.M+=a.Z%100,jy(a)):Fy(a)}}function E(t,e,n,r){for(var i,a,o=0,s=e.length,c=n.length;o<s;){if(r>=c)return-1;if(37===(i=e.charCodeAt(o++))){if(i=e.charAt(o++),!(a=_[i in Vy?e.charAt(o++):i])||(r=a(t,n,r))<0)return-1}else if(i!=n.charCodeAt(r++))return-1}return r}return(b.x=k(n,b),b.X=k(r,b),b.c=k(e,b),x.x=k(n,x),x.X=k(r,x),x.c=k(e,x),{format:function(t){var e=k(t+="",b);return e.toString=function(){return t},e},parse:function(t){var e=w(t+="",!1);return e.toString=function(){return t},e},utcFormat:function(t){var e=k(t+="",x);return e.toString=function(){return t},e},utcParse:function(t){var e=w(t+="",!0);return e.toString=function(){return t},e}})}var zy,Uy,$y,Wy,Hy,Vy={"-":"",_:" ",0:"0"},Gy=/^\s*\d+/,qy=/^%/,Xy=/[\\^$*+?|[\]().{}]/g;function Zy(t,e,n){var r=t<0?"-":"",i=(r?-t:t)+"",a=i.length;return r+(a<n?new Array(n-a+1).join(e)+i:i)}function Jy(t){return t.replace(Xy,"\\$&")}function Ky(t){return new RegExp("^(?:"+t.map(Jy).join("|")+")","i")}function Qy(t){for(var e={},n=-1,r=t.length;++n<r;)e[t[n].toLowerCase()]=n;return e}function tv(t,e,n){var r=Gy.exec(e.slice(n,n+1));return r?(t.w=+r[0],n+r[0].length):-1}function ev(t,e,n){var r=Gy.exec(e.slice(n,n+1));return r?(t.u=+r[0],n+r[0].length):-1}function nv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.U=+r[0],n+r[0].length):-1}function rv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.V=+r[0],n+r[0].length):-1}function iv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.W=+r[0],n+r[0].length):-1}function av(t,e,n){var r=Gy.exec(e.slice(n,n+4));return r?(t.y=+r[0],n+r[0].length):-1}function ov(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),n+r[0].length):-1}function sv(t,e,n){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(e.slice(n,n+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),n+r[0].length):-1}function cv(t,e,n){var r=Gy.exec(e.slice(n,n+1));return r?(t.q=3*r[0]-3,n+r[0].length):-1}function uv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.m=r[0]-1,n+r[0].length):-1}function lv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.d=+r[0],n+r[0].length):-1}function hv(t,e,n){var r=Gy.exec(e.slice(n,n+3));return r?(t.m=0,t.d=+r[0],n+r[0].length):-1}function fv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.H=+r[0],n+r[0].length):-1}function dv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.M=+r[0],n+r[0].length):-1}function pv(t,e,n){var r=Gy.exec(e.slice(n,n+2));return r?(t.S=+r[0],n+r[0].length):-1}function gv(t,e,n){var r=Gy.exec(e.slice(n,n+3));return r?(t.L=+r[0],n+r[0].length):-1}function yv(t,e,n){var r=Gy.exec(e.slice(n,n+6));return r?(t.L=Math.floor(r[0]/1e3),n+r[0].length):-1}function vv(t,e,n){var r=qy.exec(e.slice(n,n+1));return r?n+r[0].length:-1}function mv(t,e,n){var r=Gy.exec(e.slice(n));return r?(t.Q=+r[0],n+r[0].length):-1}function bv(t,e,n){var r=Gy.exec(e.slice(n));return r?(t.s=+r[0],n+r[0].length):-1}function xv(t,e){return Zy(t.getDate(),e,2)}function _v(t,e){return Zy(t.getHours(),e,2)}function kv(t,e){return Zy(t.getHours()%12||12,e,2)}function wv(t,e){return Zy(1+ny.count(Pg(t),t),e,3)}function Ev(t,e){return Zy(t.getMilliseconds(),e,3)}function Tv(t,e){return Ev(t,e)+"000"}function Cv(t,e){return Zy(t.getMonth()+1,e,2)}function Av(t,e){return Zy(t.getMinutes(),e,2)}function Sv(t,e){return Zy(t.getSeconds(),e,2)}function Mv(t){var e=t.getDay();return 0===e?7:e}function Ov(t,e){return Zy(zg.count(Pg(t)-1,t),e,2)}function Dv(t,e){var n=t.getDay();return t=n>=4||0===n?Hg(t):Hg.ceil(t),Zy(Hg.count(Pg(t),t)+(4===Pg(t).getDay()),e,2)}function Nv(t){return t.getDay()}function Bv(t,e){return Zy(Ug.count(Pg(t)-1,t),e,2)}function Lv(t,e){return Zy(t.getFullYear()%100,e,2)}function Pv(t,e){return Zy(t.getFullYear()%1e4,e,4)}function Iv(t){var e=t.getTimezoneOffset();return(e>0?"-":(e*=-1,"+"))+Zy(e/60|0,"0",2)+Zy(e%60,"0",2)}function Fv(t,e){return Zy(t.getUTCDate(),e,2)}function jv(t,e){return Zy(t.getUTCHours(),e,2)}function Rv(t,e){return Zy(t.getUTCHours()%12||12,e,2)}function Yv(t,e){return Zy(1+Ny.count(Py(t),t),e,3)}function zv(t,e){return Zy(t.getUTCMilliseconds(),e,3)}function Uv(t,e){return zv(t,e)+"000"}function $v(t,e){return Zy(t.getUTCMonth()+1,e,2)}function Wv(t,e){return Zy(t.getUTCMinutes(),e,2)}function Hv(t,e){return Zy(t.getUTCSeconds(),e,2)}function Vv(t){var e=t.getUTCDay();return 0===e?7:e}function Gv(t,e){return Zy(vy.count(Py(t)-1,t),e,2)}function qv(t,e){var n=t.getUTCDay();return t=n>=4||0===n?_y(t):_y.ceil(t),Zy(_y.count(Py(t),t)+(4===Py(t).getUTCDay()),e,2)}function Xv(t){return t.getUTCDay()}function Zv(t,e){return Zy(my.count(Py(t)-1,t),e,2)}function Jv(t,e){return Zy(t.getUTCFullYear()%100,e,2)}function Kv(t,e){return Zy(t.getUTCFullYear()%1e4,e,4)}function Qv(){return"+0000"}function tm(){return"%"}function em(t){return+t}function nm(t){return Math.floor(+t/1e3)}function rm(t){return zy=Yy(t),Uy=zy.format,$y=zy.parse,Wy=zy.utcFormat,Hy=zy.utcParse,zy}rm({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});function im(t){return new Date(t)}function am(t){return t instanceof Date?+t:+new Date(+t)}function om(t,e,n,r,a,o,s,c,u){var l=ig(Jp,Jp),h=l.invert,f=l.domain,d=u(".%L"),p=u(":%S"),g=u("%I:%M"),y=u("%I %p"),v=u("%a %d"),m=u("%b %d"),b=u("%B"),x=u("%Y"),_=[[s,1,1e3],[s,5,5e3],[s,15,15e3],[s,30,3e4],[o,1,6e4],[o,5,3e5],[o,15,9e5],[o,30,18e5],[a,1,36e5],[a,3,108e5],[a,6,216e5],[a,12,432e5],[r,1,864e5],[r,2,1728e5],[n,1,6048e5],[e,1,2592e6],[e,3,7776e6],[t,1,31536e6]];function k(i){return(s(i)<i?d:o(i)<i?p:a(i)<i?g:r(i)<i?y:e(i)<i?n(i)<i?v:m:t(i)<i?b:x)(i)}function w(e,n,r,a){if(null==e&&(e=10),"number"==typeof e){var o=Math.abs(r-n)/e,s=i((function(t){return t[2]})).right(_,o);s===_.length?(a=S(n/31536e6,r/31536e6,e),e=t):s?(a=(s=_[o/_[s-1][2]<_[s][2]/o?s-1:s])[1],e=s[0]):(a=Math.max(S(n,r,e),1),e=c)}return null==a?e:e.every(a)}return l.invert=function(t){return new Date(h(t))},l.domain=function(t){return arguments.length?f(Up.call(t,am)):f().map(im)},l.ticks=function(t,e){var n,r=f(),i=r[0],a=r[r.length-1],o=a<i;return o&&(n=i,i=a,a=n),n=(n=w(t,i,a,e))?n.range(i,a+1):[],o?n.reverse():n},l.tickFormat=function(t,e){return null==e?k:u(e)},l.nice=function(t,e){var n=f();return(t=w(t,n[0],n[n.length-1],e))?f(ug(n,t)):l},l.copy=function(){return ng(l,om(t,e,n,r,a,o,s,c,u))},l}var sm=function(){return Rp.apply(om(Pg,jg,zg,ny,ay,cy,hy,py,Uy).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},cm=Bg((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,e){t.setUTCMonth(t.getUTCMonth()+e)}),(function(t,e){return e.getUTCMonth()-t.getUTCMonth()+12*(e.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})),um=cm,lm=cm.range,hm=Bg((function(t){t.setUTCMinutes(0,0,0)}),(function(t,e){t.setTime(+t+36e5*e)}),(function(t,e){return(e-t)/36e5}),(function(t){return t.getUTCHours()})),fm=hm,dm=hm.range,pm=Bg((function(t){t.setUTCSeconds(0,0)}),(function(t,e){t.setTime(+t+6e4*e)}),(function(t,e){return(e-t)/6e4}),(function(t){return t.getUTCMinutes()})),gm=pm,ym=pm.range,vm=function(){return Rp.apply(om(Py,um,vy,Ny,fm,gm,hy,py,Wy).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)};function mm(){var t,e,n,r,i,a=0,o=1,s=Jp,c=!1;function u(e){return isNaN(e=+e)?i:s(0===n?.5:(e=(r(e)-t)*n,c?Math.max(0,Math.min(1,e)):e))}return u.domain=function(i){return arguments.length?(t=r(a=+i[0]),e=r(o=+i[1]),n=t===e?0:1/(e-t),u):[a,o]},u.clamp=function(t){return arguments.length?(c=!!t,u):c},u.interpolator=function(t){return arguments.length?(s=t,u):s},u.unknown=function(t){return arguments.length?(i=t,u):i},function(i){return r=i,t=i(a),e=i(o),n=t===e?0:1/(e-t),u}}function bm(t,e){return e.domain(t.domain()).interpolator(t.interpolator()).clamp(t.clamp()).unknown(t.unknown())}function xm(){var t=og(mm()(Jp));return t.copy=function(){return bm(t,xm())},Yp.apply(t,arguments)}function _m(){var t=yg(mm()).domain([1,10]);return t.copy=function(){return bm(t,_m()).base(t.base())},Yp.apply(t,arguments)}function km(){var t=xg(mm());return t.copy=function(){return bm(t,km()).constant(t.constant())},Yp.apply(t,arguments)}function wm(){var t=Tg(mm());return t.copy=function(){return bm(t,wm()).exponent(t.exponent())},Yp.apply(t,arguments)}function Em(){return wm.apply(null,arguments).exponent(.5)}function Tm(){var t=[],e=Jp;function n(n){if(!isNaN(n=+n))return e((c(t,n)-1)/(t.length-1))}return n.domain=function(e){if(!arguments.length)return t.slice();t=[];for(var i,a=0,o=e.length;a<o;++a)null==(i=e[a])||isNaN(i=+i)||t.push(i);return t.sort(r),n},n.interpolator=function(t){return arguments.length?(e=t,n):e},n.copy=function(){return Tm(e).domain(t)},Yp.apply(n,arguments)}function Cm(){var t,e,n,r,i,a,o,s=0,c=.5,u=1,l=Jp,h=!1;function f(t){return isNaN(t=+t)?o:(t=.5+((t=+a(t))-e)*(t<e?r:i),l(h?Math.max(0,Math.min(1,t)):t))}return f.domain=function(o){return arguments.length?(t=a(s=+o[0]),e=a(c=+o[1]),n=a(u=+o[2]),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f):[s,c,u]},f.clamp=function(t){return arguments.length?(h=!!t,f):h},f.interpolator=function(t){return arguments.length?(l=t,f):l},f.unknown=function(t){return arguments.length?(o=t,f):o},function(o){return a=o,t=o(s),e=o(c),n=o(u),r=t===e?0:.5/(e-t),i=e===n?0:.5/(n-e),f}}function Am(){var t=og(Cm()(Jp));return t.copy=function(){return bm(t,Am())},Yp.apply(t,arguments)}function Sm(){var t=yg(Cm()).domain([.1,1,10]);return t.copy=function(){return bm(t,Sm()).base(t.base())},Yp.apply(t,arguments)}function Mm(){var t=xg(Cm());return t.copy=function(){return bm(t,Mm()).constant(t.constant())},Yp.apply(t,arguments)}function Om(){var t=Tg(Cm());return t.copy=function(){return bm(t,Om()).exponent(t.exponent())},Yp.apply(t,arguments)}function Dm(){return Om.apply(null,arguments).exponent(.5)}var Nm=function(t){for(var e=t.length/6|0,n=new Array(e),r=0;r<e;)n[r]="#"+t.slice(6*r,6*++r);return n},Bm=Nm("1f77b4ff7f0e2ca02cd627289467bd8c564be377c27f7f7fbcbd2217becf"),Lm=Nm("7fc97fbeaed4fdc086ffff99386cb0f0027fbf5b17666666"),Pm=Nm("1b9e77d95f027570b3e7298a66a61ee6ab02a6761d666666"),Im=Nm("a6cee31f78b4b2df8a33a02cfb9a99e31a1cfdbf6fff7f00cab2d66a3d9affff99b15928"),Fm=Nm("fbb4aeb3cde3ccebc5decbe4fed9a6ffffcce5d8bdfddaecf2f2f2"),jm=Nm("b3e2cdfdcdaccbd5e8f4cae4e6f5c9fff2aef1e2cccccccc"),Rm=Nm("e41a1c377eb84daf4a984ea3ff7f00ffff33a65628f781bf999999"),Ym=Nm("66c2a5fc8d628da0cbe78ac3a6d854ffd92fe5c494b3b3b3"),zm=Nm("8dd3c7ffffb3bebadafb807280b1d3fdb462b3de69fccde5d9d9d9bc80bdccebc5ffed6f"),Um=Nm("4e79a7f28e2ce1575976b7b259a14fedc949af7aa1ff9da79c755fbab0ab"),$m=function(t){return pn(t[t.length-1])},Wm=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(Nm),Hm=$m(Wm),Vm=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(Nm),Gm=$m(Vm),qm=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(Nm),Xm=$m(qm),Zm=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(Nm),Jm=$m(Zm),Km=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(Nm),Qm=$m(Km),tb=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(Nm),eb=$m(tb),nb=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(Nm),rb=$m(nb),ib=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(Nm),ab=$m(ib),ob=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(Nm),sb=$m(ob),cb=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(Nm),ub=$m(cb),lb=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(Nm),hb=$m(lb),fb=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(Nm),db=$m(fb),pb=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(Nm),gb=$m(pb),yb=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(Nm),vb=$m(yb),mb=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(Nm),bb=$m(mb),xb=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(Nm),_b=$m(xb),kb=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(Nm),wb=$m(kb),Eb=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(Nm),Tb=$m(Eb),Cb=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(Nm),Ab=$m(Cb),Sb=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(Nm),Mb=$m(Sb),Ob=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(Nm),Db=$m(Ob),Nb=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(Nm),Bb=$m(Nb),Lb=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(Nm),Pb=$m(Lb),Ib=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(Nm),Fb=$m(Ib),jb=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(Nm),Rb=$m(jb),Yb=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(Nm),zb=$m(Yb),Ub=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(Nm),$b=$m(Ub),Wb=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},Hb=kp(Oa(300,.5,0),Oa(-240,.5,1)),Vb=kp(Oa(-100,.75,.35),Oa(80,1.5,.8)),Gb=kp(Oa(260,.75,.35),Oa(80,1.5,.8)),qb=Oa(),Xb=function(t){(t<0||t>1)&&(t-=Math.floor(t));var e=Math.abs(t-.5);return qb.h=360*t-100,qb.s=1.5-1.5*e,qb.l=.8-.9*e,qb+""},Zb=Ge(),Jb=Math.PI/3,Kb=2*Math.PI/3,Qb=function(t){var e;return t=(.5-t)*Math.PI,Zb.r=255*(e=Math.sin(t))*e,Zb.g=255*(e=Math.sin(t+Jb))*e,Zb.b=255*(e=Math.sin(t+Kb))*e,Zb+""},tx=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"};function ex(t){var e=t.length;return function(n){return t[Math.max(0,Math.min(e-1,Math.floor(n*e)))]}}var nx=ex(Nm("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),rx=ex(Nm("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),ix=ex(Nm("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),ax=ex(Nm("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921")),ox=function(t){return ke(ne(t).call(document.documentElement))},sx=0;function cx(){return new ux}function ux(){this._="@"+(++sx).toString(36)}ux.prototype=cx.prototype={constructor:ux,get:function(t){for(var e=this._;!(e in t);)if(!(t=t.parentNode))return;return t[e]},set:function(t,e){return t[this._]=e},remove:function(t){return this._ in t&&delete t[this._]},toString:function(){return this._}};var lx=function(t){return"string"==typeof t?new be([document.querySelectorAll(t)],[document.documentElement]):new be([null==t?[]:t],me)},hx=function(t,e){null==e&&(e=Mn().touches);for(var n=0,r=e?e.length:0,i=new Array(r);n<r;++n)i[n]=On(t,e[n]);return i},fx=function(t){return function(){return t}},dx=Math.abs,px=Math.atan2,gx=Math.cos,yx=Math.max,vx=Math.min,mx=Math.sin,bx=Math.sqrt,xx=Math.PI,_x=xx/2,kx=2*xx;function wx(t){return t>1?0:t<-1?xx:Math.acos(t)}function Ex(t){return t>=1?_x:t<=-1?-_x:Math.asin(t)}function Tx(t){return t.innerRadius}function Cx(t){return t.outerRadius}function Ax(t){return t.startAngle}function Sx(t){return t.endAngle}function Mx(t){return t&&t.padAngle}function Ox(t,e,n,r,i,a,o,s){var c=n-t,u=r-e,l=o-i,h=s-a,f=h*c-l*u;if(!(f*f<1e-12))return[t+(f=(l*(e-a)-h*(t-i))/f)*c,e+f*u]}function Dx(t,e,n,r,i,a,o){var s=t-n,c=e-r,u=(o?a:-a)/bx(s*s+c*c),l=u*c,h=-u*s,f=t+l,d=e+h,p=n+l,g=r+h,y=(f+p)/2,v=(d+g)/2,m=p-f,b=g-d,x=m*m+b*b,_=i-a,k=f*g-p*d,w=(b<0?-1:1)*bx(yx(0,_*_*x-k*k)),E=(k*b-m*w)/x,T=(-k*m-b*w)/x,C=(k*b+m*w)/x,A=(-k*m+b*w)/x,S=E-y,M=T-v,O=C-y,D=A-v;return S*S+M*M>O*O+D*D&&(E=C,T=A),{cx:E,cy:T,x01:-l,y01:-h,x11:E*(i/_-1),y11:T*(i/_-1)}}var Nx=function(){var t=Tx,e=Cx,n=fx(0),r=null,i=Ax,a=Sx,o=Mx,s=null;function c(){var c,u,l=+t.apply(this,arguments),h=+e.apply(this,arguments),f=i.apply(this,arguments)-_x,d=a.apply(this,arguments)-_x,p=dx(d-f),g=d>f;if(s||(s=c=Ui()),h<l&&(u=h,h=l,l=u),h>1e-12)if(p>kx-1e-12)s.moveTo(h*gx(f),h*mx(f)),s.arc(0,0,h,f,d,!g),l>1e-12&&(s.moveTo(l*gx(d),l*mx(d)),s.arc(0,0,l,d,f,g));else{var y,v,m=f,b=d,x=f,_=d,k=p,w=p,E=o.apply(this,arguments)/2,T=E>1e-12&&(r?+r.apply(this,arguments):bx(l*l+h*h)),C=vx(dx(h-l)/2,+n.apply(this,arguments)),A=C,S=C;if(T>1e-12){var M=Ex(T/l*mx(E)),O=Ex(T/h*mx(E));(k-=2*M)>1e-12?(x+=M*=g?1:-1,_-=M):(k=0,x=_=(f+d)/2),(w-=2*O)>1e-12?(m+=O*=g?1:-1,b-=O):(w=0,m=b=(f+d)/2)}var D=h*gx(m),N=h*mx(m),B=l*gx(_),L=l*mx(_);if(C>1e-12){var P,I=h*gx(b),F=h*mx(b),j=l*gx(x),R=l*mx(x);if(p<xx&&(P=Ox(D,N,j,R,I,F,B,L))){var Y=D-P[0],z=N-P[1],U=I-P[0],$=F-P[1],W=1/mx(wx((Y*U+z*$)/(bx(Y*Y+z*z)*bx(U*U+$*$)))/2),H=bx(P[0]*P[0]+P[1]*P[1]);A=vx(C,(l-H)/(W-1)),S=vx(C,(h-H)/(W+1))}}w>1e-12?S>1e-12?(y=Dx(j,R,D,N,h,S,g),v=Dx(I,F,B,L,h,S,g),s.moveTo(y.cx+y.x01,y.cy+y.y01),S<C?s.arc(y.cx,y.cy,S,px(y.y01,y.x01),px(v.y01,v.x01),!g):(s.arc(y.cx,y.cy,S,px(y.y01,y.x01),px(y.y11,y.x11),!g),s.arc(0,0,h,px(y.cy+y.y11,y.cx+y.x11),px(v.cy+v.y11,v.cx+v.x11),!g),s.arc(v.cx,v.cy,S,px(v.y11,v.x11),px(v.y01,v.x01),!g))):(s.moveTo(D,N),s.arc(0,0,h,m,b,!g)):s.moveTo(D,N),l>1e-12&&k>1e-12?A>1e-12?(y=Dx(B,L,I,F,l,-A,g),v=Dx(D,N,j,R,l,-A,g),s.lineTo(y.cx+y.x01,y.cy+y.y01),A<C?s.arc(y.cx,y.cy,A,px(y.y01,y.x01),px(v.y01,v.x01),!g):(s.arc(y.cx,y.cy,A,px(y.y01,y.x01),px(y.y11,y.x11),!g),s.arc(0,0,l,px(y.cy+y.y11,y.cx+y.x11),px(v.cy+v.y11,v.cx+v.x11),g),s.arc(v.cx,v.cy,A,px(v.y11,v.x11),px(v.y01,v.x01),!g))):s.arc(0,0,l,_,x,g):s.lineTo(B,L)}else s.moveTo(0,0);if(s.closePath(),c)return s=null,c+""||null}return c.centroid=function(){var n=(+t.apply(this,arguments)+ +e.apply(this,arguments))/2,r=(+i.apply(this,arguments)+ +a.apply(this,arguments))/2-xx/2;return[gx(r)*n,mx(r)*n]},c.innerRadius=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),c):t},c.outerRadius=function(t){return arguments.length?(e="function"==typeof t?t:fx(+t),c):e},c.cornerRadius=function(t){return arguments.length?(n="function"==typeof t?t:fx(+t),c):n},c.padRadius=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:fx(+t),c):r},c.startAngle=function(t){return arguments.length?(i="function"==typeof t?t:fx(+t),c):i},c.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:fx(+t),c):a},c.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:fx(+t),c):o},c.context=function(t){return arguments.length?(s=null==t?null:t,c):s},c};function Bx(t){this._context=t}Bx.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:this._context.lineTo(t,e)}}};var Lx=function(t){return new Bx(t)};function Px(t){return t[0]}function Ix(t){return t[1]}var Fx=function(){var t=Px,e=Ix,n=fx(!0),r=null,i=Lx,a=null;function o(o){var s,c,u,l=o.length,h=!1;for(null==r&&(a=i(u=Ui())),s=0;s<=l;++s)!(s<l&&n(c=o[s],s,o))===h&&((h=!h)?a.lineStart():a.lineEnd()),h&&a.point(+t(c,s,o),+e(c,s,o));if(u)return a=null,u+""||null}return o.x=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),o):t},o.y=function(t){return arguments.length?(e="function"==typeof t?t:fx(+t),o):e},o.defined=function(t){return arguments.length?(n="function"==typeof t?t:fx(!!t),o):n},o.curve=function(t){return arguments.length?(i=t,null!=r&&(a=i(r)),o):i},o.context=function(t){return arguments.length?(null==t?r=a=null:a=i(r=t),o):r},o},jx=function(){var t=Px,e=null,n=fx(0),r=Ix,i=fx(!0),a=null,o=Lx,s=null;function c(c){var u,l,h,f,d,p=c.length,g=!1,y=new Array(p),v=new Array(p);for(null==a&&(s=o(d=Ui())),u=0;u<=p;++u){if(!(u<p&&i(f=c[u],u,c))===g)if(g=!g)l=u,s.areaStart(),s.lineStart();else{for(s.lineEnd(),s.lineStart(),h=u-1;h>=l;--h)s.point(y[h],v[h]);s.lineEnd(),s.areaEnd()}g&&(y[u]=+t(f,u,c),v[u]=+n(f,u,c),s.point(e?+e(f,u,c):y[u],r?+r(f,u,c):v[u]))}if(d)return s=null,d+""||null}function u(){return Fx().defined(i).curve(o).context(a)}return c.x=function(n){return arguments.length?(t="function"==typeof n?n:fx(+n),e=null,c):t},c.x0=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),c):t},c.x1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:fx(+t),c):e},c.y=function(t){return arguments.length?(n="function"==typeof t?t:fx(+t),r=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:fx(+t),c):n},c.y1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:fx(+t),c):r},c.lineX0=c.lineY0=function(){return u().x(t).y(n)},c.lineY1=function(){return u().x(t).y(r)},c.lineX1=function(){return u().x(e).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:fx(!!t),c):i},c.curve=function(t){return arguments.length?(o=t,null!=a&&(s=o(a)),c):o},c.context=function(t){return arguments.length?(null==t?a=s=null:s=o(a=t),c):a},c},Rx=function(t,e){return e<t?-1:e>t?1:e>=t?0:NaN},Yx=function(t){return t},zx=function(){var t=Yx,e=Rx,n=null,r=fx(0),i=fx(kx),a=fx(0);function o(o){var s,c,u,l,h,f=o.length,d=0,p=new Array(f),g=new Array(f),y=+r.apply(this,arguments),v=Math.min(kx,Math.max(-kx,i.apply(this,arguments)-y)),m=Math.min(Math.abs(v)/f,a.apply(this,arguments)),b=m*(v<0?-1:1);for(s=0;s<f;++s)(h=g[p[s]=s]=+t(o[s],s,o))>0&&(d+=h);for(null!=e?p.sort((function(t,n){return e(g[t],g[n])})):null!=n&&p.sort((function(t,e){return n(o[t],o[e])})),s=0,u=d?(v-f*b)/d:0;s<f;++s,y=l)c=p[s],l=y+((h=g[c])>0?h*u:0)+b,g[c]={data:o[c],index:s,value:h,startAngle:y,endAngle:l,padAngle:m};return g}return o.value=function(e){return arguments.length?(t="function"==typeof e?e:fx(+e),o):t},o.sortValues=function(t){return arguments.length?(e=t,n=null,o):e},o.sort=function(t){return arguments.length?(n=t,e=null,o):n},o.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:fx(+t),o):r},o.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:fx(+t),o):i},o.padAngle=function(t){return arguments.length?(a="function"==typeof t?t:fx(+t),o):a},o},Ux=Wx(Lx);function $x(t){this._curve=t}function Wx(t){function e(e){return new $x(t(e))}return e._curve=t,e}function Hx(t){var e=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?e(Wx(t)):e()._curve},t}$x.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,e){this._curve.point(e*Math.sin(t),e*-Math.cos(t))}};var Vx=function(){return Hx(Fx().curve(Ux))},Gx=function(){var t=jx().curve(Ux),e=t.curve,n=t.lineX0,r=t.lineX1,i=t.lineY0,a=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Hx(n())},delete t.lineX0,t.lineEndAngle=function(){return Hx(r())},delete t.lineX1,t.lineInnerRadius=function(){return Hx(i())},delete t.lineY0,t.lineOuterRadius=function(){return Hx(a())},delete t.lineY1,t.curve=function(t){return arguments.length?e(Wx(t)):e()._curve},t},qx=function(t,e){return[(e=+e)*Math.cos(t-=Math.PI/2),e*Math.sin(t)]},Xx=Array.prototype.slice;function Zx(t){return t.source}function Jx(t){return t.target}function Kx(t){var e=Zx,n=Jx,r=Px,i=Ix,a=null;function o(){var o,s=Xx.call(arguments),c=e.apply(this,s),u=n.apply(this,s);if(a||(a=o=Ui()),t(a,+r.apply(this,(s[0]=c,s)),+i.apply(this,s),+r.apply(this,(s[0]=u,s)),+i.apply(this,s)),o)return a=null,o+""||null}return o.source=function(t){return arguments.length?(e=t,o):e},o.target=function(t){return arguments.length?(n=t,o):n},o.x=function(t){return arguments.length?(r="function"==typeof t?t:fx(+t),o):r},o.y=function(t){return arguments.length?(i="function"==typeof t?t:fx(+t),o):i},o.context=function(t){return arguments.length?(a=null==t?null:t,o):a},o}function Qx(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e=(e+r)/2,n,e,i,r,i)}function t_(t,e,n,r,i){t.moveTo(e,n),t.bezierCurveTo(e,n=(n+i)/2,r,n,r,i)}function e_(t,e,n,r,i){var a=qx(e,n),o=qx(e,n=(n+i)/2),s=qx(r,n),c=qx(r,i);t.moveTo(a[0],a[1]),t.bezierCurveTo(o[0],o[1],s[0],s[1],c[0],c[1])}function n_(){return Kx(Qx)}function r_(){return Kx(t_)}function i_(){var t=Kx(e_);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t}var a_={draw:function(t,e){var n=Math.sqrt(e/xx);t.moveTo(n,0),t.arc(0,0,n,0,kx)}},o_={draw:function(t,e){var n=Math.sqrt(e/5)/2;t.moveTo(-3*n,-n),t.lineTo(-n,-n),t.lineTo(-n,-3*n),t.lineTo(n,-3*n),t.lineTo(n,-n),t.lineTo(3*n,-n),t.lineTo(3*n,n),t.lineTo(n,n),t.lineTo(n,3*n),t.lineTo(-n,3*n),t.lineTo(-n,n),t.lineTo(-3*n,n),t.closePath()}},s_=Math.sqrt(1/3),c_=2*s_,u_={draw:function(t,e){var n=Math.sqrt(e/c_),r=n*s_;t.moveTo(0,-n),t.lineTo(r,0),t.lineTo(0,n),t.lineTo(-r,0),t.closePath()}},l_=Math.sin(xx/10)/Math.sin(7*xx/10),h_=Math.sin(kx/10)*l_,f_=-Math.cos(kx/10)*l_,d_={draw:function(t,e){var n=Math.sqrt(.8908130915292852*e),r=h_*n,i=f_*n;t.moveTo(0,-n),t.lineTo(r,i);for(var a=1;a<5;++a){var o=kx*a/5,s=Math.cos(o),c=Math.sin(o);t.lineTo(c*n,-s*n),t.lineTo(s*r-c*i,c*r+s*i)}t.closePath()}},p_={draw:function(t,e){var n=Math.sqrt(e),r=-n/2;t.rect(r,r,n,n)}},g_=Math.sqrt(3),y_={draw:function(t,e){var n=-Math.sqrt(e/(3*g_));t.moveTo(0,2*n),t.lineTo(-g_*n,-n),t.lineTo(g_*n,-n),t.closePath()}},v_=Math.sqrt(3)/2,m_=1/Math.sqrt(12),b_=3*(m_/2+1),x_={draw:function(t,e){var n=Math.sqrt(e/b_),r=n/2,i=n*m_,a=r,o=n*m_+n,s=-a,c=o;t.moveTo(r,i),t.lineTo(a,o),t.lineTo(s,c),t.lineTo(-.5*r-v_*i,v_*r+-.5*i),t.lineTo(-.5*a-v_*o,v_*a+-.5*o),t.lineTo(-.5*s-v_*c,v_*s+-.5*c),t.lineTo(-.5*r+v_*i,-.5*i-v_*r),t.lineTo(-.5*a+v_*o,-.5*o-v_*a),t.lineTo(-.5*s+v_*c,-.5*c-v_*s),t.closePath()}},__=[a_,o_,u_,p_,d_,y_,x_],k_=function(){var t=fx(a_),e=fx(64),n=null;function r(){var r;if(n||(n=r=Ui()),t.apply(this,arguments).draw(n,+e.apply(this,arguments)),r)return n=null,r+""||null}return r.type=function(e){return arguments.length?(t="function"==typeof e?e:fx(e),r):t},r.size=function(t){return arguments.length?(e="function"==typeof t?t:fx(+t),r):e},r.context=function(t){return arguments.length?(n=null==t?null:t,r):n},r},w_=function(){};function E_(t,e,n){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+e)/6,(t._y0+4*t._y1+n)/6)}function T_(t){this._context=t}T_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:E_(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:E_(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};var C_=function(t){return new T_(t)};function A_(t){this._context=t}A_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x2=t,this._y2=e;break;case 1:this._point=2,this._x3=t,this._y3=e;break;case 2:this._point=3,this._x4=t,this._y4=e,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+e)/6);break;default:E_(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};var S_=function(t){return new A_(t)};function M_(t){this._context=t}M_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var n=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+e)/6;this._line?this._context.lineTo(n,r):this._context.moveTo(n,r);break;case 3:this._point=4;default:E_(this,t,e)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e}};var O_=function(t){return new M_(t)};function D_(t,e){this._basis=new T_(t),this._beta=e}D_.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,e=this._y,n=t.length-1;if(n>0)for(var r,i=t[0],a=e[0],o=t[n]-i,s=e[n]-a,c=-1;++c<=n;)r=c/n,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*o),this._beta*e[c]+(1-this._beta)*(a+r*s));this._x=this._y=null,this._basis.lineEnd()},point:function(t,e){this._x.push(+t),this._y.push(+e)}};var N_=function t(e){function n(t){return 1===e?new T_(t):new D_(t,e)}return n.beta=function(e){return t(+e)},n}(.85);function B_(t,e,n){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-e),t._y2+t._k*(t._y1-n),t._x2,t._y2)}function L_(t,e){this._context=t,this._k=(1-e)/6}L_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:B_(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2,this._x1=t,this._y1=e;break;case 2:this._point=3;default:B_(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var P_=function t(e){function n(t){return new L_(t,e)}return n.tension=function(e){return t(+e)},n}(0);function I_(t,e){this._context=t,this._k=(1-e)/6}I_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:B_(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var F_=function t(e){function n(t){return new I_(t,e)}return n.tension=function(e){return t(+e)},n}(0);function j_(t,e){this._context=t,this._k=(1-e)/6}j_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:B_(this,t,e)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var R_=function t(e){function n(t){return new j_(t,e)}return n.tension=function(e){return t(+e)},n}(0);function Y_(t,e,n){var r=t._x1,i=t._y1,a=t._x2,o=t._y2;if(t._l01_a>1e-12){var s=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*s-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*s-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>1e-12){var u=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,l=3*t._l23_a*(t._l23_a+t._l12_a);a=(a*u+t._x1*t._l23_2a-e*t._l12_2a)/l,o=(o*u+t._y1*t._l23_2a-n*t._l12_2a)/l}t._context.bezierCurveTo(r,i,a,o,t._x2,t._y2)}function z_(t,e){this._context=t,this._alpha=e}z_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3;default:Y_(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var U_=function t(e){function n(t){return e?new z_(t,e):new L_(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function $_(t,e){this._context=t,this._alpha=e}$_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=e;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=e);break;case 2:this._point=3,this._x5=t,this._y5=e;break;default:Y_(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var W_=function t(e){function n(t){return e?new $_(t,e):new I_(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function H_(t,e){this._context=t,this._alpha=e}H_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){if(t=+t,e=+e,this._point){var n=this._x2-t,r=this._y2-e;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(n*n+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Y_(this,t,e)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=e}};var V_=function t(e){function n(t){return e?new H_(t,e):new j_(t,0)}return n.alpha=function(e){return t(+e)},n}(.5);function G_(t){this._context=t}G_.prototype={areaStart:w_,areaEnd:w_,lineStart:function(){this._point=0},lineEnd:function(){this._point&&this._context.closePath()},point:function(t,e){t=+t,e=+e,this._point?this._context.lineTo(t,e):(this._point=1,this._context.moveTo(t,e))}};var q_=function(t){return new G_(t)};function X_(t){return t<0?-1:1}function Z_(t,e,n){var r=t._x1-t._x0,i=e-t._x1,a=(t._y1-t._y0)/(r||i<0&&-0),o=(n-t._y1)/(i||r<0&&-0),s=(a*i+o*r)/(r+i);return(X_(a)+X_(o))*Math.min(Math.abs(a),Math.abs(o),.5*Math.abs(s))||0}function J_(t,e){var n=t._x1-t._x0;return n?(3*(t._y1-t._y0)/n-e)/2:e}function K_(t,e,n){var r=t._x0,i=t._y0,a=t._x1,o=t._y1,s=(a-r)/3;t._context.bezierCurveTo(r+s,i+s*e,a-s,o-s*n,a,o)}function Q_(t){this._context=t}function tk(t){this._context=new ek(t)}function ek(t){this._context=t}function nk(t){return new Q_(t)}function rk(t){return new tk(t)}function ik(t){this._context=t}function ak(t){var e,n,r=t.length-1,i=new Array(r),a=new Array(r),o=new Array(r);for(i[0]=0,a[0]=2,o[0]=t[0]+2*t[1],e=1;e<r-1;++e)i[e]=1,a[e]=4,o[e]=4*t[e]+2*t[e+1];for(i[r-1]=2,a[r-1]=7,o[r-1]=8*t[r-1]+t[r],e=1;e<r;++e)n=i[e]/a[e-1],a[e]-=n,o[e]-=n*o[e-1];for(i[r-1]=o[r-1]/a[r-1],e=r-2;e>=0;--e)i[e]=(o[e]-i[e+1])/a[e];for(a[r-1]=(t[r]+i[r-1])/2,e=0;e<r-1;++e)a[e]=2*t[e+1]-i[e+1];return[i,a]}Q_.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=this._t0=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x1,this._y1);break;case 3:K_(this,this._t0,J_(this,this._t0))}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,e){var n=NaN;if(e=+e,(t=+t)!==this._x1||e!==this._y1){switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;break;case 2:this._point=3,K_(this,J_(this,n=Z_(this,t,e)),n);break;default:K_(this,this._t0,n=Z_(this,t,e))}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=e,this._t0=n}}},(tk.prototype=Object.create(Q_.prototype)).point=function(t,e){Q_.prototype.point.call(this,e,t)},ek.prototype={moveTo:function(t,e){this._context.moveTo(e,t)},closePath:function(){this._context.closePath()},lineTo:function(t,e){this._context.lineTo(e,t)},bezierCurveTo:function(t,e,n,r,i,a){this._context.bezierCurveTo(e,t,r,n,a,i)}},ik.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=[],this._y=[]},lineEnd:function(){var t=this._x,e=this._y,n=t.length;if(n)if(this._line?this._context.lineTo(t[0],e[0]):this._context.moveTo(t[0],e[0]),2===n)this._context.lineTo(t[1],e[1]);else for(var r=ak(t),i=ak(e),a=0,o=1;o<n;++a,++o)this._context.bezierCurveTo(r[0][a],i[0][a],r[1][a],i[1][a],t[o],e[o]);(this._line||0!==this._line&&1===n)&&this._context.closePath(),this._line=1-this._line,this._x=this._y=null},point:function(t,e){this._x.push(+t),this._y.push(+e)}};var ok=function(t){return new ik(t)};function sk(t,e){this._context=t,this._t=e}sk.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x=this._y=NaN,this._point=0},lineEnd:function(){0<this._t&&this._t<1&&2===this._point&&this._context.lineTo(this._x,this._y),(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line>=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,e){switch(t=+t,e=+e,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,e):this._context.moveTo(t,e);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,e),this._context.lineTo(t,e);else{var n=this._x*(1-this._t)+t*this._t;this._context.lineTo(n,this._y),this._context.lineTo(n,e)}}this._x=t,this._y=e}};var ck=function(t){return new sk(t,.5)};function uk(t){return new sk(t,0)}function lk(t){return new sk(t,1)}var hk=function(t,e){if((i=t.length)>1)for(var n,r,i,a=1,o=t[e[0]],s=o.length;a<i;++a)for(r=o,o=t[e[a]],n=0;n<s;++n)o[n][1]+=o[n][0]=isNaN(r[n][1])?r[n][0]:r[n][1]},fk=function(t){for(var e=t.length,n=new Array(e);--e>=0;)n[e]=e;return n};function dk(t,e){return t[e]}var pk=function(){var t=fx([]),e=fk,n=hk,r=dk;function i(i){var a,o,s=t.apply(this,arguments),c=i.length,u=s.length,l=new Array(u);for(a=0;a<u;++a){for(var h,f=s[a],d=l[a]=new Array(c),p=0;p<c;++p)d[p]=h=[0,+r(i[p],f,p,i)],h.data=i[p];d.key=f}for(a=0,o=e(l);a<u;++a)l[o[a]].index=a;return n(l,o),l}return i.keys=function(e){return arguments.length?(t="function"==typeof e?e:fx(Xx.call(e)),i):t},i.value=function(t){return arguments.length?(r="function"==typeof t?t:fx(+t),i):r},i.order=function(t){return arguments.length?(e=null==t?fk:"function"==typeof t?t:fx(Xx.call(t)),i):e},i.offset=function(t){return arguments.length?(n=null==t?hk:t,i):n},i},gk=function(t,e){if((r=t.length)>0){for(var n,r,i,a=0,o=t[0].length;a<o;++a){for(i=n=0;n<r;++n)i+=t[n][a][1]||0;if(i)for(n=0;n<r;++n)t[n][a][1]/=i}hk(t,e)}},yk=function(t,e){if((s=t.length)>0)for(var n,r,i,a,o,s,c=0,u=t[e[0]].length;c<u;++c)for(a=o=0,n=0;n<s;++n)(i=(r=t[e[n]][c])[1]-r[0])>0?(r[0]=a,r[1]=a+=i):i<0?(r[1]=o,r[0]=o+=i):(r[0]=0,r[1]=i)},vk=function(t,e){if((n=t.length)>0){for(var n,r=0,i=t[e[0]],a=i.length;r<a;++r){for(var o=0,s=0;o<n;++o)s+=t[o][r][1]||0;i[r][1]+=i[r][0]=-s/2}hk(t,e)}},mk=function(t,e){if((i=t.length)>0&&(r=(n=t[e[0]]).length)>0){for(var n,r,i,a=0,o=1;o<r;++o){for(var s=0,c=0,u=0;s<i;++s){for(var l=t[e[s]],h=l[o][1]||0,f=(h-(l[o-1][1]||0))/2,d=0;d<s;++d){var p=t[e[d]];f+=(p[o][1]||0)-(p[o-1][1]||0)}c+=h,u+=f*h}n[o-1][1]+=n[o-1][0]=a,c&&(a-=u/c)}n[o-1][1]+=n[o-1][0]=a,hk(t,e)}},bk=function(t){var e=t.map(xk);return fk(t).sort((function(t,n){return e[t]-e[n]}))};function xk(t){for(var e,n=-1,r=0,i=t.length,a=-1/0;++n<i;)(e=+t[n][1])>a&&(a=e,r=n);return r}var _k=function(t){var e=t.map(kk);return fk(t).sort((function(t,n){return e[t]-e[n]}))};function kk(t){for(var e,n=0,r=-1,i=t.length;++r<i;)(e=+t[r][1])&&(n+=e);return n}var wk=function(t){return _k(t).reverse()},Ek=function(t){var e,n,r=t.length,i=t.map(kk),a=bk(t),o=0,s=0,c=[],u=[];for(e=0;e<r;++e)n=a[e],o<s?(o+=i[n],c.push(n)):(s+=i[n],u.push(n));return u.reverse().concat(c)},Tk=function(t){return fk(t).reverse()};var Ck=Date.prototype.toISOString?function(t){return t.toISOString()}:Wy("%Y-%m-%dT%H:%M:%S.%LZ");var Ak=+new Date("2000-01-01T00:00:00.000Z")?function(t){var e=new Date(t);return isNaN(e)?null:e}:Hy("%Y-%m-%dT%H:%M:%S.%LZ"),Sk=function(t,e,n){var r=new $n,i=e;return null==e?(r.restart(t,e,n),r):(e=+e,n=null==n?zn():+n,r.restart((function a(o){o+=i,r.restart(a,i+=e,n),t(o)}),e,n),r)},Mk=function(t){return function(){return t}};function Ok(t){return t[0]}function Dk(t){return t[1]}function Nk(){this._=null}function Bk(t){t.U=t.C=t.L=t.R=t.P=t.N=null}function Lk(t,e){var n=e,r=e.R,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.R=r.L,n.R&&(n.R.U=n),r.L=n}function Pk(t,e){var n=e,r=e.L,i=n.U;i?i.L===n?i.L=r:i.R=r:t._=r,r.U=i,n.U=r,n.L=r.R,n.L&&(n.L.U=n),r.R=n}function Ik(t){for(;t.L;)t=t.L;return t}Nk.prototype={constructor:Nk,insert:function(t,e){var n,r,i;if(t){if(e.P=t,e.N=t.N,t.N&&(t.N.P=e),t.N=e,t.R){for(t=t.R;t.L;)t=t.L;t.L=e}else t.R=e;n=t}else this._?(t=Ik(this._),e.P=null,e.N=t,t.P=t.L=e,n=t):(e.P=e.N=null,this._=e,n=null);for(e.L=e.R=null,e.U=n,e.C=!0,t=e;n&&n.C;)n===(r=n.U).L?(i=r.R)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.R&&(Lk(this,n),n=(t=n).U),n.C=!1,r.C=!0,Pk(this,r)):(i=r.L)&&i.C?(n.C=i.C=!1,r.C=!0,t=r):(t===n.L&&(Pk(this,n),n=(t=n).U),n.C=!1,r.C=!0,Lk(this,r)),n=t.U;this._.C=!1},remove:function(t){t.N&&(t.N.P=t.P),t.P&&(t.P.N=t.N),t.N=t.P=null;var e,n,r,i=t.U,a=t.L,o=t.R;if(n=a?o?Ik(o):a:o,i?i.L===t?i.L=n:i.R=n:this._=n,a&&o?(r=n.C,n.C=t.C,n.L=a,a.U=n,n!==o?(i=n.U,n.U=t.U,t=n.R,i.L=t,n.R=o,o.U=n):(n.U=i,i=n,t=n.R)):(r=t.C,t=n),t&&(t.U=i),!r)if(t&&t.C)t.C=!1;else{do{if(t===this._)break;if(t===i.L){if((e=i.R).C&&(e.C=!1,i.C=!0,Lk(this,i),e=i.R),e.L&&e.L.C||e.R&&e.R.C){e.R&&e.R.C||(e.L.C=!1,e.C=!0,Pk(this,e),e=i.R),e.C=i.C,i.C=e.R.C=!1,Lk(this,i),t=this._;break}}else if((e=i.L).C&&(e.C=!1,i.C=!0,Pk(this,i),e=i.L),e.L&&e.L.C||e.R&&e.R.C){e.L&&e.L.C||(e.R.C=!1,e.C=!0,Lk(this,e),e=i.L),e.C=i.C,i.C=e.L.C=!1,Pk(this,i),t=this._;break}e.C=!0,t=i,i=i.U}while(!t.C);t&&(t.C=!1)}}};var Fk=Nk;function jk(t,e,n,r){var i=[null,null],a=cw.push(i)-1;return i.left=t,i.right=e,n&&Yk(i,t,e,n),r&&Yk(i,e,t,r),ow[t.index].halfedges.push(a),ow[e.index].halfedges.push(a),i}function Rk(t,e,n){var r=[e,n];return r.left=t,r}function Yk(t,e,n,r){t[0]||t[1]?t.left===n?t[1]=r:t[0]=r:(t[0]=r,t.left=e,t.right=n)}function zk(t,e,n,r,i){var a,o=t[0],s=t[1],c=o[0],u=o[1],l=0,h=1,f=s[0]-c,d=s[1]-u;if(a=e-c,f||!(a>0)){if(a/=f,f<0){if(a<l)return;a<h&&(h=a)}else if(f>0){if(a>h)return;a>l&&(l=a)}if(a=r-c,f||!(a<0)){if(a/=f,f<0){if(a>h)return;a>l&&(l=a)}else if(f>0){if(a<l)return;a<h&&(h=a)}if(a=n-u,d||!(a>0)){if(a/=d,d<0){if(a<l)return;a<h&&(h=a)}else if(d>0){if(a>h)return;a>l&&(l=a)}if(a=i-u,d||!(a<0)){if(a/=d,d<0){if(a>h)return;a>l&&(l=a)}else if(d>0){if(a<l)return;a<h&&(h=a)}return!(l>0||h<1)||(l>0&&(t[0]=[c+l*f,u+l*d]),h<1&&(t[1]=[c+h*f,u+h*d]),!0)}}}}}function Uk(t,e,n,r,i){var a=t[1];if(a)return!0;var o,s,c=t[0],u=t.left,l=t.right,h=u[0],f=u[1],d=l[0],p=l[1],g=(h+d)/2,y=(f+p)/2;if(p===f){if(g<e||g>=r)return;if(h>d){if(c){if(c[1]>=i)return}else c=[g,n];a=[g,i]}else{if(c){if(c[1]<n)return}else c=[g,i];a=[g,n]}}else if(s=y-(o=(h-d)/(p-f))*g,o<-1||o>1)if(h>d){if(c){if(c[1]>=i)return}else c=[(n-s)/o,n];a=[(i-s)/o,i]}else{if(c){if(c[1]<n)return}else c=[(i-s)/o,i];a=[(n-s)/o,n]}else if(f<p){if(c){if(c[0]>=r)return}else c=[e,o*e+s];a=[r,o*r+s]}else{if(c){if(c[0]<e)return}else c=[r,o*r+s];a=[e,o*e+s]}return t[0]=c,t[1]=a,!0}function $k(t,e){var n=t.site,r=e.left,i=e.right;return n===i&&(i=r,r=n),i?Math.atan2(i[1]-r[1],i[0]-r[0]):(n===r?(r=e[1],i=e[0]):(r=e[0],i=e[1]),Math.atan2(r[0]-i[0],i[1]-r[1]))}function Wk(t,e){return e[+(e.left!==t.site)]}function Hk(t,e){return e[+(e.left===t.site)]}var Vk,Gk=[];function qk(){Bk(this),this.x=this.y=this.arc=this.site=this.cy=null}function Xk(t){var e=t.P,n=t.N;if(e&&n){var r=e.site,i=t.site,a=n.site;if(r!==a){var o=i[0],s=i[1],c=r[0]-o,u=r[1]-s,l=a[0]-o,h=a[1]-s,f=2*(c*h-u*l);if(!(f>=-lw)){var d=c*c+u*u,p=l*l+h*h,g=(h*d-u*p)/f,y=(c*p-l*d)/f,v=Gk.pop()||new qk;v.arc=t,v.site=i,v.x=g+o,v.y=(v.cy=y+s)+Math.sqrt(g*g+y*y),t.circle=v;for(var m=null,b=sw._;b;)if(v.y<b.y||v.y===b.y&&v.x<=b.x){if(!b.L){m=b.P;break}b=b.L}else{if(!b.R){m=b;break}b=b.R}sw.insert(m,v),m||(Vk=v)}}}}function Zk(t){var e=t.circle;e&&(e.P||(Vk=e.N),sw.remove(e),Gk.push(e),Bk(e),t.circle=null)}var Jk=[];function Kk(){Bk(this),this.edge=this.site=this.circle=null}function Qk(t){var e=Jk.pop()||new Kk;return e.site=t,e}function tw(t){Zk(t),aw.remove(t),Jk.push(t),Bk(t)}function ew(t){var e=t.circle,n=e.x,r=e.cy,i=[n,r],a=t.P,o=t.N,s=[t];tw(t);for(var c=a;c.circle&&Math.abs(n-c.circle.x)<uw&&Math.abs(r-c.circle.cy)<uw;)a=c.P,s.unshift(c),tw(c),c=a;s.unshift(c),Zk(c);for(var u=o;u.circle&&Math.abs(n-u.circle.x)<uw&&Math.abs(r-u.circle.cy)<uw;)o=u.N,s.push(u),tw(u),u=o;s.push(u),Zk(u);var l,h=s.length;for(l=1;l<h;++l)u=s[l],c=s[l-1],Yk(u.edge,c.site,u.site,i);c=s[0],(u=s[h-1]).edge=jk(c.site,u.site,null,i),Xk(c),Xk(u)}function nw(t){for(var e,n,r,i,a=t[0],o=t[1],s=aw._;s;)if((r=rw(s,o)-a)>uw)s=s.L;else{if(!((i=a-iw(s,o))>uw)){r>-uw?(e=s.P,n=s):i>-uw?(e=s,n=s.N):e=n=s;break}if(!s.R){e=s;break}s=s.R}!function(t){ow[t.index]={site:t,halfedges:[]}}(t);var c=Qk(t);if(aw.insert(e,c),e||n){if(e===n)return Zk(e),n=Qk(e.site),aw.insert(c,n),c.edge=n.edge=jk(e.site,c.site),Xk(e),void Xk(n);if(n){Zk(e),Zk(n);var u=e.site,l=u[0],h=u[1],f=t[0]-l,d=t[1]-h,p=n.site,g=p[0]-l,y=p[1]-h,v=2*(f*y-d*g),m=f*f+d*d,b=g*g+y*y,x=[(y*m-d*b)/v+l,(f*b-g*m)/v+h];Yk(n.edge,u,p,x),c.edge=jk(u,t,null,x),n.edge=jk(t,p,null,x),Xk(e),Xk(n)}else c.edge=jk(e.site,c.site)}}function rw(t,e){var n=t.site,r=n[0],i=n[1],a=i-e;if(!a)return r;var o=t.P;if(!o)return-1/0;var s=(n=o.site)[0],c=n[1],u=c-e;if(!u)return s;var l=s-r,h=1/a-1/u,f=l/u;return h?(-f+Math.sqrt(f*f-2*h*(l*l/(-2*u)-c+u/2+i-a/2)))/h+r:(r+s)/2}function iw(t,e){var n=t.N;if(n)return rw(n,e);var r=t.site;return r[1]===e?r[0]:1/0}var aw,ow,sw,cw,uw=1e-6,lw=1e-12;function hw(t,e){return e[1]-t[1]||e[0]-t[0]}function fw(t,e){var n,r,i,a=t.sort(hw).pop();for(cw=[],ow=new Array(t.length),aw=new Fk,sw=new Fk;;)if(i=Vk,a&&(!i||a[1]<i.y||a[1]===i.y&&a[0]<i.x))a[0]===n&&a[1]===r||(nw(a),n=a[0],r=a[1]),a=t.pop();else{if(!i)break;ew(i.arc)}if(function(){for(var t,e,n,r,i=0,a=ow.length;i<a;++i)if((t=ow[i])&&(r=(e=t.halfedges).length)){var o=new Array(r),s=new Array(r);for(n=0;n<r;++n)o[n]=n,s[n]=$k(t,cw[e[n]]);for(o.sort((function(t,e){return s[e]-s[t]})),n=0;n<r;++n)s[n]=e[o[n]];for(n=0;n<r;++n)e[n]=s[n]}}(),e){var o=+e[0][0],s=+e[0][1],c=+e[1][0],u=+e[1][1];!function(t,e,n,r){for(var i,a=cw.length;a--;)Uk(i=cw[a],t,e,n,r)&&zk(i,t,e,n,r)&&(Math.abs(i[0][0]-i[1][0])>uw||Math.abs(i[0][1]-i[1][1])>uw)||delete cw[a]}(o,s,c,u),function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,g,y=ow.length,v=!0;for(i=0;i<y;++i)if(a=ow[i]){for(o=a.site,s=(c=a.halfedges).length;s--;)cw[c[s]]||c.splice(s,1);for(s=0,u=c.length;s<u;)p=(d=Hk(a,cw[c[s]]))[0],g=d[1],h=(l=Wk(a,cw[c[++s%u]]))[0],f=l[1],(Math.abs(p-h)>uw||Math.abs(g-f)>uw)&&(c.splice(s,0,cw.push(Rk(o,d,Math.abs(p-t)<uw&&r-g>uw?[t,Math.abs(h-t)<uw?f:r]:Math.abs(g-r)<uw&&n-p>uw?[Math.abs(f-r)<uw?h:n,r]:Math.abs(p-n)<uw&&g-e>uw?[n,Math.abs(h-n)<uw?f:e]:Math.abs(g-e)<uw&&p-t>uw?[Math.abs(f-e)<uw?h:t,e]:null))-1),++u);u&&(v=!1)}if(v){var m,b,x,_=1/0;for(i=0,v=null;i<y;++i)(a=ow[i])&&(x=(m=(o=a.site)[0]-t)*m+(b=o[1]-e)*b)<_&&(_=x,v=a);if(v){var k=[t,e],w=[t,r],E=[n,r],T=[n,e];v.halfedges.push(cw.push(Rk(o=v.site,k,w))-1,cw.push(Rk(o,w,E))-1,cw.push(Rk(o,E,T))-1,cw.push(Rk(o,T,k))-1)}}for(i=0;i<y;++i)(a=ow[i])&&(a.halfedges.length||delete ow[i])}(o,s,c,u)}this.edges=cw,this.cells=ow,aw=sw=cw=ow=null}fw.prototype={constructor:fw,polygons:function(){var t=this.edges;return this.cells.map((function(e){var n=e.halfedges.map((function(n){return Wk(e,t[n])}));return n.data=e.site.data,n}))},triangles:function(){var t=[],e=this.edges;return this.cells.forEach((function(n,r){if(a=(i=n.halfedges).length)for(var i,a,o,s,c,u,l=n.site,h=-1,f=e[i[a-1]],d=f.left===l?f.right:f.left;++h<a;)o=d,d=(f=e[i[h]]).left===l?f.right:f.left,o&&d&&r<o.index&&r<d.index&&(c=o,u=d,((s=l)[0]-u[0])*(c[1]-s[1])-(s[0]-c[0])*(u[1]-s[1])<0)&&t.push([l.data,o.data,d.data])})),t},links:function(){return this.edges.filter((function(t){return t.right})).map((function(t){return{source:t.left.data,target:t.right.data}}))},find:function(t,e,n){for(var r,i,a=this,o=a._found||0,s=a.cells.length;!(i=a.cells[o]);)if(++o>=s)return null;var c=t-i.site[0],u=e-i.site[1],l=c*c+u*u;do{i=a.cells[r=o],o=null,i.halfedges.forEach((function(n){var r=a.edges[n],s=r.left;if(s!==i.site&&s||(s=r.right)){var c=t-s[0],u=e-s[1],h=c*c+u*u;h<l&&(l=h,o=s.index)}}))}while(null!==o);return a._found=r,null==n||l<=n*n?i.site:null}};var dw=function(){var t=Ok,e=Dk,n=null;function r(r){return new fw(r.map((function(n,i){var a=[Math.round(t(n,i,r)/uw)*uw,Math.round(e(n,i,r)/uw)*uw];return a.index=i,a.data=n,a})),n)}return r.polygons=function(t){return r(t).polygons()},r.links=function(t){return r(t).links()},r.triangles=function(t){return r(t).triangles()},r.x=function(e){return arguments.length?(t="function"==typeof e?e:Mk(+e),r):t},r.y=function(t){return arguments.length?(e="function"==typeof t?t:Mk(+t),r):e},r.extent=function(t){return arguments.length?(n=null==t?null:[[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]],r):n&&[[n[0][0],n[0][1]],[n[1][0],n[1][1]]]},r.size=function(t){return arguments.length?(n=null==t?null:[[0,0],[+t[0],+t[1]]],r):n&&[n[1][0]-n[0][0],n[1][1]-n[0][1]]},r},pw=function(t){return function(){return t}};function gw(t,e,n){this.target=t,this.type=e,this.transform=n}function yw(t,e,n){this.k=t,this.x=e,this.y=n}yw.prototype={constructor:yw,scale:function(t){return 1===t?this:new yw(this.k*t,this.x,this.y)},translate:function(t,e){return 0===t&0===e?this:new yw(this.k,this.x+this.k*t,this.y+this.k*e)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var vw=new yw(1,0,0);function mw(t){for(;!t.__zoom;)if(!(t=t.parentNode))return vw;return t.__zoom}function bw(){ce.stopImmediatePropagation()}mw.prototype=yw.prototype;var xw=function(){ce.preventDefault(),ce.stopImmediatePropagation()};function _w(){return!ce.ctrlKey&&!ce.button}function kw(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function ww(){return this.__zoom||vw}function Ew(){return-ce.deltaY*(1===ce.deltaMode?.05:ce.deltaMode?1:.002)}function Tw(){return navigator.maxTouchPoints||"ontouchstart"in this}function Cw(t,e,n){var r=t.invertX(e[0][0])-n[0][0],i=t.invertX(e[1][0])-n[1][0],a=t.invertY(e[0][1])-n[0][1],o=t.invertY(e[1][1])-n[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),o>a?(a+o)/2:Math.min(0,a)||Math.max(0,o))}var Aw=function(){var t,e,n=_w,r=kw,i=Cw,a=Ew,o=Tw,s=[0,1/0],c=[[-1/0,-1/0],[1/0,1/0]],u=250,l=fp,h=lt("start","zoom","end"),f=0;function d(t){t.property("__zoom",ww).on("wheel.zoom",x).on("mousedown.zoom",_).on("dblclick.zoom",k).filter(o).on("touchstart.zoom",w).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",T).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function p(t,e){return(e=Math.max(s[0],Math.min(s[1],e)))===t.k?t:new yw(e,t.x,t.y)}function g(t,e,n){var r=e[0]-n[0]*t.k,i=e[1]-n[1]*t.k;return r===t.x&&i===t.y?t:new yw(t.k,r,i)}function y(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function v(t,e,n){t.on("start.zoom",(function(){m(this,arguments).start()})).on("interrupt.zoom end.zoom",(function(){m(this,arguments).end()})).tween("zoom",(function(){var t=this,i=arguments,a=m(t,i),o=r.apply(t,i),s=null==n?y(o):"function"==typeof n?n.apply(t,i):n,c=Math.max(o[1][0]-o[0][0],o[1][1]-o[0][1]),u=t.__zoom,h="function"==typeof e?e.apply(t,i):e,f=l(u.invert(s).concat(c/u.k),h.invert(s).concat(c/h.k));return function(t){if(1===t)t=h;else{var e=f(t),n=c/e[2];t=new yw(n,s[0]-e[0]*n,s[1]-e[1]*n)}a.zoom(null,t)}}))}function m(t,e,n){return!n&&t.__zooming||new b(t,e)}function b(t,e){this.that=t,this.args=e,this.active=0,this.extent=r.apply(t,e),this.taps=0}function x(){if(n.apply(this,arguments)){var t=m(this,arguments),e=this.__zoom,r=Math.max(s[0],Math.min(s[1],e.k*Math.pow(2,a.apply(this,arguments)))),o=Nn(this);if(t.wheel)t.mouse[0][0]===o[0]&&t.mouse[0][1]===o[1]||(t.mouse[1]=e.invert(t.mouse[0]=o)),clearTimeout(t.wheel);else{if(e.k===r)return;t.mouse=[o,e.invert(o)],or(this),t.start()}xw(),t.wheel=setTimeout(u,150),t.zoom("mouse",i(g(p(e,r),t.mouse[0],t.mouse[1]),t.extent,c))}function u(){t.wheel=null,t.end()}}function _(){if(!e&&n.apply(this,arguments)){var t=m(this,arguments,!0),r=ke(ce.view).on("mousemove.zoom",u,!0).on("mouseup.zoom",l,!0),a=Nn(this),o=ce.clientX,s=ce.clientY;Te(ce.view),bw(),t.mouse=[a,this.__zoom.invert(a)],or(this),t.start()}function u(){if(xw(),!t.moved){var e=ce.clientX-o,n=ce.clientY-s;t.moved=e*e+n*n>f}t.zoom("mouse",i(g(t.that.__zoom,t.mouse[0]=Nn(t.that),t.mouse[1]),t.extent,c))}function l(){r.on("mousemove.zoom mouseup.zoom",null),Ce(ce.view,t.moved),xw(),t.end()}}function k(){if(n.apply(this,arguments)){var t=this.__zoom,e=Nn(this),a=t.invert(e),o=t.k*(ce.shiftKey?.5:2),s=i(g(p(t,o),e,a),r.apply(this,arguments),c);xw(),u>0?ke(this).transition().duration(u).call(v,s,e):ke(this).call(d.transform,s)}}function w(){if(n.apply(this,arguments)){var e,r,i,a,o=ce.touches,s=o.length,c=m(this,arguments,ce.changedTouches.length===s);for(bw(),r=0;r<s;++r)i=o[r],a=[a=Dn(this,o,i.identifier),this.__zoom.invert(a),i.identifier],c.touch0?c.touch1||c.touch0[2]===a[2]||(c.touch1=a,c.taps=0):(c.touch0=a,e=!0,c.taps=1+!!t);t&&(t=clearTimeout(t)),e&&(c.taps<2&&(t=setTimeout((function(){t=null}),500)),or(this),c.start())}}function E(){if(this.__zooming){var e,n,r,a,o=m(this,arguments),s=ce.changedTouches,u=s.length;for(xw(),t&&(t=clearTimeout(t)),o.taps=0,e=0;e<u;++e)n=s[e],r=Dn(this,s,n.identifier),o.touch0&&o.touch0[2]===n.identifier?o.touch0[0]=r:o.touch1&&o.touch1[2]===n.identifier&&(o.touch1[0]=r);if(n=o.that.__zoom,o.touch1){var l=o.touch0[0],h=o.touch0[1],f=o.touch1[0],d=o.touch1[1],y=(y=f[0]-l[0])*y+(y=f[1]-l[1])*y,v=(v=d[0]-h[0])*v+(v=d[1]-h[1])*v;n=p(n,Math.sqrt(y/v)),r=[(l[0]+f[0])/2,(l[1]+f[1])/2],a=[(h[0]+d[0])/2,(h[1]+d[1])/2]}else{if(!o.touch0)return;r=o.touch0[0],a=o.touch0[1]}o.zoom("touch",i(g(n,r,a),o.extent,c))}}function T(){if(this.__zooming){var t,n,r=m(this,arguments),i=ce.changedTouches,a=i.length;for(bw(),e&&clearTimeout(e),e=setTimeout((function(){e=null}),500),t=0;t<a;++t)n=i[t],r.touch0&&r.touch0[2]===n.identifier?delete r.touch0:r.touch1&&r.touch1[2]===n.identifier&&delete r.touch1;if(r.touch1&&!r.touch0&&(r.touch0=r.touch1,delete r.touch1),r.touch0)r.touch0[1]=this.__zoom.invert(r.touch0[0]);else if(r.end(),2===r.taps){var o=ke(this).on("dblclick.zoom");o&&o.apply(this,arguments)}}}return d.transform=function(t,e,n){var r=t.selection?t.selection():t;r.property("__zoom",ww),t!==r?v(t,e,n):r.interrupt().each((function(){m(this,arguments).start().zoom(null,"function"==typeof e?e.apply(this,arguments):e).end()}))},d.scaleBy=function(t,e,n){d.scaleTo(t,(function(){var t=this.__zoom.k,n="function"==typeof e?e.apply(this,arguments):e;return t*n}),n)},d.scaleTo=function(t,e,n){d.transform(t,(function(){var t=r.apply(this,arguments),a=this.__zoom,o=null==n?y(t):"function"==typeof n?n.apply(this,arguments):n,s=a.invert(o),u="function"==typeof e?e.apply(this,arguments):e;return i(g(p(a,u),o,s),t,c)}),n)},d.translateBy=function(t,e,n){d.transform(t,(function(){return i(this.__zoom.translate("function"==typeof e?e.apply(this,arguments):e,"function"==typeof n?n.apply(this,arguments):n),r.apply(this,arguments),c)}))},d.translateTo=function(t,e,n,a){d.transform(t,(function(){var t=r.apply(this,arguments),o=this.__zoom,s=null==a?y(t):"function"==typeof a?a.apply(this,arguments):a;return i(vw.translate(s[0],s[1]).scale(o.k).translate("function"==typeof e?-e.apply(this,arguments):-e,"function"==typeof n?-n.apply(this,arguments):-n),t,c)}),a)},b.prototype={start:function(){return 1==++this.active&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(t,e){return this.mouse&&"mouse"!==t&&(this.mouse[1]=e.invert(this.mouse[0])),this.touch0&&"touch"!==t&&(this.touch0[1]=e.invert(this.touch0[0])),this.touch1&&"touch"!==t&&(this.touch1[1]=e.invert(this.touch1[0])),this.that.__zoom=e,this.emit("zoom"),this},end:function(){return 0==--this.active&&(delete this.that.__zooming,this.emit("end")),this},emit:function(t){pe(new gw(d,t,this.that.__zoom),h.apply,h,[t,this.that,this.args])}},d.wheelDelta=function(t){return arguments.length?(a="function"==typeof t?t:pw(+t),d):a},d.filter=function(t){return arguments.length?(n="function"==typeof t?t:pw(!!t),d):n},d.touchable=function(t){return arguments.length?(o="function"==typeof t?t:pw(!!t),d):o},d.extent=function(t){return arguments.length?(r="function"==typeof t?t:pw([[+t[0][0],+t[0][1]],[+t[1][0],+t[1][1]]]),d):r},d.scaleExtent=function(t){return arguments.length?(s[0]=+t[0],s[1]=+t[1],d):[s[0],s[1]]},d.translateExtent=function(t){return arguments.length?(c[0][0]=+t[0][0],c[1][0]=+t[1][0],c[0][1]=+t[0][1],c[1][1]=+t[1][1],d):[[c[0][0],c[0][1]],[c[1][0],c[1][1]]]},d.constrain=function(t){return arguments.length?(i=t,d):i},d.duration=function(t){return arguments.length?(u=+t,d):u},d.interpolate=function(t){return arguments.length?(l=t,d):l},d.on=function(){var t=h.on.apply(h,arguments);return t===h?d:t},d.clickDistance=function(t){return arguments.length?(f=(t=+t)*t,d):Math.sqrt(f)},d};n.d(e,"version",(function(){return"5.15.0"})),n.d(e,"bisect",(function(){return c})),n.d(e,"bisectRight",(function(){return o})),n.d(e,"bisectLeft",(function(){return s})),n.d(e,"ascending",(function(){return r})),n.d(e,"bisector",(function(){return i})),n.d(e,"cross",(function(){return h})),n.d(e,"descending",(function(){return f})),n.d(e,"deviation",(function(){return g})),n.d(e,"extent",(function(){return y})),n.d(e,"histogram",(function(){return O})),n.d(e,"thresholdFreedmanDiaconis",(function(){return N})),n.d(e,"thresholdScott",(function(){return B})),n.d(e,"thresholdSturges",(function(){return M})),n.d(e,"max",(function(){return L})),n.d(e,"mean",(function(){return P})),n.d(e,"median",(function(){return I})),n.d(e,"merge",(function(){return F})),n.d(e,"min",(function(){return j})),n.d(e,"pairs",(function(){return u})),n.d(e,"permute",(function(){return R})),n.d(e,"quantile",(function(){return D})),n.d(e,"range",(function(){return k})),n.d(e,"scan",(function(){return Y})),n.d(e,"shuffle",(function(){return z})),n.d(e,"sum",(function(){return U})),n.d(e,"ticks",(function(){return C})),n.d(e,"tickIncrement",(function(){return A})),n.d(e,"tickStep",(function(){return S})),n.d(e,"transpose",(function(){return $})),n.d(e,"variance",(function(){return p})),n.d(e,"zip",(function(){return H})),n.d(e,"axisTop",(function(){return tt})),n.d(e,"axisRight",(function(){return et})),n.d(e,"axisBottom",(function(){return nt})),n.d(e,"axisLeft",(function(){return rt})),n.d(e,"brush",(function(){return Ti})),n.d(e,"brushX",(function(){return wi})),n.d(e,"brushY",(function(){return Ei})),n.d(e,"brushSelection",(function(){return ki})),n.d(e,"chord",(function(){return Li})),n.d(e,"ribbon",(function(){return qi})),n.d(e,"nest",(function(){return Ki})),n.d(e,"set",(function(){return oa})),n.d(e,"map",(function(){return Ji})),n.d(e,"keys",(function(){return sa})),n.d(e,"values",(function(){return ca})),n.d(e,"entries",(function(){return ua})),n.d(e,"color",(function(){return $e})),n.d(e,"rgb",(function(){return Ge})),n.d(e,"hsl",(function(){return tn})),n.d(e,"lab",(function(){return pa})),n.d(e,"hcl",(function(){return ka})),n.d(e,"lch",(function(){return _a})),n.d(e,"gray",(function(){return da})),n.d(e,"cubehelix",(function(){return Oa})),n.d(e,"contours",(function(){return Ya})),n.d(e,"contourDensity",(function(){return Va})),n.d(e,"dispatch",(function(){return lt})),n.d(e,"drag",(function(){return Qa})),n.d(e,"dragDisable",(function(){return Te})),n.d(e,"dragEnable",(function(){return Ce})),n.d(e,"dsvFormat",(function(){return oo})),n.d(e,"csvParse",(function(){return co})),n.d(e,"csvParseRows",(function(){return uo})),n.d(e,"csvFormat",(function(){return lo})),n.d(e,"csvFormatBody",(function(){return ho})),n.d(e,"csvFormatRows",(function(){return fo})),n.d(e,"csvFormatRow",(function(){return po})),n.d(e,"csvFormatValue",(function(){return go})),n.d(e,"tsvParse",(function(){return vo})),n.d(e,"tsvParseRows",(function(){return mo})),n.d(e,"tsvFormat",(function(){return bo})),n.d(e,"tsvFormatBody",(function(){return xo})),n.d(e,"tsvFormatRows",(function(){return _o})),n.d(e,"tsvFormatRow",(function(){return ko})),n.d(e,"tsvFormatValue",(function(){return wo})),n.d(e,"autoType",(function(){return Eo})),n.d(e,"easeLinear",(function(){return Co})),n.d(e,"easeQuad",(function(){return Mo})),n.d(e,"easeQuadIn",(function(){return Ao})),n.d(e,"easeQuadOut",(function(){return So})),n.d(e,"easeQuadInOut",(function(){return Mo})),n.d(e,"easeCubic",(function(){return Vr})),n.d(e,"easeCubicIn",(function(){return Wr})),n.d(e,"easeCubicOut",(function(){return Hr})),n.d(e,"easeCubicInOut",(function(){return Vr})),n.d(e,"easePoly",(function(){return No})),n.d(e,"easePolyIn",(function(){return Oo})),n.d(e,"easePolyOut",(function(){return Do})),n.d(e,"easePolyInOut",(function(){return No})),n.d(e,"easeSin",(function(){return Fo})),n.d(e,"easeSinIn",(function(){return Po})),n.d(e,"easeSinOut",(function(){return Io})),n.d(e,"easeSinInOut",(function(){return Fo})),n.d(e,"easeExp",(function(){return Yo})),n.d(e,"easeExpIn",(function(){return jo})),n.d(e,"easeExpOut",(function(){return Ro})),n.d(e,"easeExpInOut",(function(){return Yo})),n.d(e,"easeCircle",(function(){return $o})),n.d(e,"easeCircleIn",(function(){return zo})),n.d(e,"easeCircleOut",(function(){return Uo})),n.d(e,"easeCircleInOut",(function(){return $o})),n.d(e,"easeBounce",(function(){return Ho})),n.d(e,"easeBounceIn",(function(){return Wo})),n.d(e,"easeBounceOut",(function(){return Ho})),n.d(e,"easeBounceInOut",(function(){return Vo})),n.d(e,"easeBack",(function(){return Xo})),n.d(e,"easeBackIn",(function(){return Go})),n.d(e,"easeBackOut",(function(){return qo})),n.d(e,"easeBackInOut",(function(){return Xo})),n.d(e,"easeElastic",(function(){return Ko})),n.d(e,"easeElasticIn",(function(){return Jo})),n.d(e,"easeElasticOut",(function(){return Ko})),n.d(e,"easeElasticInOut",(function(){return Qo})),n.d(e,"blob",(function(){return es})),n.d(e,"buffer",(function(){return rs})),n.d(e,"dsv",(function(){return ss})),n.d(e,"csv",(function(){return cs})),n.d(e,"tsv",(function(){return us})),n.d(e,"image",(function(){return ls})),n.d(e,"json",(function(){return fs})),n.d(e,"text",(function(){return as})),n.d(e,"xml",(function(){return ps})),n.d(e,"html",(function(){return gs})),n.d(e,"svg",(function(){return ys})),n.d(e,"forceCenter",(function(){return vs})),n.d(e,"forceCollide",(function(){return Os})),n.d(e,"forceLink",(function(){return Bs})),n.d(e,"forceManyBody",(function(){return js})),n.d(e,"forceRadial",(function(){return Rs})),n.d(e,"forceSimulation",(function(){return Fs})),n.d(e,"forceX",(function(){return Ys})),n.d(e,"forceY",(function(){return zs})),n.d(e,"formatDefaultLocale",(function(){return rc})),n.d(e,"format",(function(){return Xs})),n.d(e,"formatPrefix",(function(){return Zs})),n.d(e,"formatLocale",(function(){return nc})),n.d(e,"formatSpecifier",(function(){return Hs})),n.d(e,"FormatSpecifier",(function(){return Vs})),n.d(e,"precisionFixed",(function(){return ic})),n.d(e,"precisionPrefix",(function(){return ac})),n.d(e,"precisionRound",(function(){return oc})),n.d(e,"geoArea",(function(){return Jc})),n.d(e,"geoBounds",(function(){return $u})),n.d(e,"geoCentroid",(function(){return el})),n.d(e,"geoCircle",(function(){return fl})),n.d(e,"geoClipAntimeridian",(function(){return El})),n.d(e,"geoClipCircle",(function(){return Tl})),n.d(e,"geoClipExtent",(function(){return Ol})),n.d(e,"geoClipRectangle",(function(){return Cl})),n.d(e,"geoContains",(function(){return ql})),n.d(e,"geoDistance",(function(){return Rl})),n.d(e,"geoGraticule",(function(){return Jl})),n.d(e,"geoGraticule10",(function(){return Kl})),n.d(e,"geoInterpolate",(function(){return rh})),n.d(e,"geoLength",(function(){return Il})),n.d(e,"geoPath",(function(){return ef})),n.d(e,"geoAlbers",(function(){return _f})),n.d(e,"geoAlbersUsa",(function(){return kf})),n.d(e,"geoAzimuthalEqualArea",(function(){return Cf})),n.d(e,"geoAzimuthalEqualAreaRaw",(function(){return Tf})),n.d(e,"geoAzimuthalEquidistant",(function(){return Sf})),n.d(e,"geoAzimuthalEquidistantRaw",(function(){return Af})),n.d(e,"geoConicConformal",(function(){return Lf})),n.d(e,"geoConicConformalRaw",(function(){return Bf})),n.d(e,"geoConicEqualArea",(function(){return xf})),n.d(e,"geoConicEqualAreaRaw",(function(){return bf})),n.d(e,"geoConicEquidistant",(function(){return jf})),n.d(e,"geoConicEquidistantRaw",(function(){return Ff})),n.d(e,"geoEqualEarth",(function(){return Hf})),n.d(e,"geoEqualEarthRaw",(function(){return Wf})),n.d(e,"geoEquirectangular",(function(){return If})),n.d(e,"geoEquirectangularRaw",(function(){return Pf})),n.d(e,"geoGnomonic",(function(){return Gf})),n.d(e,"geoGnomonicRaw",(function(){return Vf})),n.d(e,"geoIdentity",(function(){return Xf})),n.d(e,"geoProjection",(function(){return yf})),n.d(e,"geoProjectionMutator",(function(){return vf})),n.d(e,"geoMercator",(function(){return Of})),n.d(e,"geoMercatorRaw",(function(){return Mf})),n.d(e,"geoNaturalEarth1",(function(){return Jf})),n.d(e,"geoNaturalEarth1Raw",(function(){return Zf})),n.d(e,"geoOrthographic",(function(){return Qf})),n.d(e,"geoOrthographicRaw",(function(){return Kf})),n.d(e,"geoStereographic",(function(){return ed})),n.d(e,"geoStereographicRaw",(function(){return td})),n.d(e,"geoTransverseMercator",(function(){return rd})),n.d(e,"geoTransverseMercatorRaw",(function(){return nd})),n.d(e,"geoRotation",(function(){return ul})),n.d(e,"geoStream",(function(){return $c})),n.d(e,"geoTransform",(function(){return nf})),n.d(e,"cluster",(function(){return sd})),n.d(e,"hierarchy",(function(){return ud})),n.d(e,"pack",(function(){return Ld})),n.d(e,"packSiblings",(function(){return Sd})),n.d(e,"packEnclose",(function(){return gd})),n.d(e,"partition",(function(){return Yd})),n.d(e,"stratify",(function(){return Hd})),n.d(e,"tree",(function(){return Kd})),n.d(e,"treemap",(function(){return rp})),n.d(e,"treemapBinary",(function(){return ip})),n.d(e,"treemapDice",(function(){return Rd})),n.d(e,"treemapSlice",(function(){return Qd})),n.d(e,"treemapSliceDice",(function(){return ap})),n.d(e,"treemapSquarify",(function(){return np})),n.d(e,"treemapResquarify",(function(){return op})),n.d(e,"interpolate",(function(){return Sn})),n.d(e,"interpolateArray",(function(){return mn})),n.d(e,"interpolateBasis",(function(){return an})),n.d(e,"interpolateBasisClosed",(function(){return on})),n.d(e,"interpolateDate",(function(){return xn})),n.d(e,"interpolateDiscrete",(function(){return sp})),n.d(e,"interpolateHue",(function(){return cp})),n.d(e,"interpolateNumber",(function(){return _n})),n.d(e,"interpolateNumberArray",(function(){return yn})),n.d(e,"interpolateObject",(function(){return kn})),n.d(e,"interpolateRound",(function(){return up})),n.d(e,"interpolateString",(function(){return An})),n.d(e,"interpolateTransformCss",(function(){return hr})),n.d(e,"interpolateTransformSvg",(function(){return fr})),n.d(e,"interpolateZoom",(function(){return fp})),n.d(e,"interpolateRgb",(function(){return fn})),n.d(e,"interpolateRgbBasis",(function(){return pn})),n.d(e,"interpolateRgbBasisClosed",(function(){return gn})),n.d(e,"interpolateHsl",(function(){return pp})),n.d(e,"interpolateHslLong",(function(){return gp})),n.d(e,"interpolateLab",(function(){return yp})),n.d(e,"interpolateHcl",(function(){return mp})),n.d(e,"interpolateHclLong",(function(){return bp})),n.d(e,"interpolateCubehelix",(function(){return _p})),n.d(e,"interpolateCubehelixLong",(function(){return kp})),n.d(e,"piecewise",(function(){return wp})),n.d(e,"quantize",(function(){return Ep})),n.d(e,"path",(function(){return Ui})),n.d(e,"polygonArea",(function(){return Tp})),n.d(e,"polygonCentroid",(function(){return Cp})),n.d(e,"polygonHull",(function(){return Mp})),n.d(e,"polygonContains",(function(){return Op})),n.d(e,"polygonLength",(function(){return Dp})),n.d(e,"quadtree",(function(){return Es})),n.d(e,"randomUniform",(function(){return Bp})),n.d(e,"randomNormal",(function(){return Lp})),n.d(e,"randomLogNormal",(function(){return Pp})),n.d(e,"randomBates",(function(){return Fp})),n.d(e,"randomIrwinHall",(function(){return Ip})),n.d(e,"randomExponential",(function(){return jp})),n.d(e,"scaleBand",(function(){return Vp})),n.d(e,"scalePoint",(function(){return qp})),n.d(e,"scaleIdentity",(function(){return cg})),n.d(e,"scaleLinear",(function(){return sg})),n.d(e,"scaleLog",(function(){return vg})),n.d(e,"scaleSymlog",(function(){return _g})),n.d(e,"scaleOrdinal",(function(){return Hp})),n.d(e,"scaleImplicit",(function(){return Wp})),n.d(e,"scalePow",(function(){return Cg})),n.d(e,"scaleSqrt",(function(){return Ag})),n.d(e,"scaleQuantile",(function(){return Sg})),n.d(e,"scaleQuantize",(function(){return Mg})),n.d(e,"scaleThreshold",(function(){return Og})),n.d(e,"scaleTime",(function(){return sm})),n.d(e,"scaleUtc",(function(){return vm})),n.d(e,"scaleSequential",(function(){return xm})),n.d(e,"scaleSequentialLog",(function(){return _m})),n.d(e,"scaleSequentialPow",(function(){return wm})),n.d(e,"scaleSequentialSqrt",(function(){return Em})),n.d(e,"scaleSequentialSymlog",(function(){return km})),n.d(e,"scaleSequentialQuantile",(function(){return Tm})),n.d(e,"scaleDiverging",(function(){return Am})),n.d(e,"scaleDivergingLog",(function(){return Sm})),n.d(e,"scaleDivergingPow",(function(){return Om})),n.d(e,"scaleDivergingSqrt",(function(){return Dm})),n.d(e,"scaleDivergingSymlog",(function(){return Mm})),n.d(e,"tickFormat",(function(){return ag})),n.d(e,"schemeCategory10",(function(){return Bm})),n.d(e,"schemeAccent",(function(){return Lm})),n.d(e,"schemeDark2",(function(){return Pm})),n.d(e,"schemePaired",(function(){return Im})),n.d(e,"schemePastel1",(function(){return Fm})),n.d(e,"schemePastel2",(function(){return jm})),n.d(e,"schemeSet1",(function(){return Rm})),n.d(e,"schemeSet2",(function(){return Ym})),n.d(e,"schemeSet3",(function(){return zm})),n.d(e,"schemeTableau10",(function(){return Um})),n.d(e,"interpolateBrBG",(function(){return Hm})),n.d(e,"schemeBrBG",(function(){return Wm})),n.d(e,"interpolatePRGn",(function(){return Gm})),n.d(e,"schemePRGn",(function(){return Vm})),n.d(e,"interpolatePiYG",(function(){return Xm})),n.d(e,"schemePiYG",(function(){return qm})),n.d(e,"interpolatePuOr",(function(){return Jm})),n.d(e,"schemePuOr",(function(){return Zm})),n.d(e,"interpolateRdBu",(function(){return Qm})),n.d(e,"schemeRdBu",(function(){return Km})),n.d(e,"interpolateRdGy",(function(){return eb})),n.d(e,"schemeRdGy",(function(){return tb})),n.d(e,"interpolateRdYlBu",(function(){return rb})),n.d(e,"schemeRdYlBu",(function(){return nb})),n.d(e,"interpolateRdYlGn",(function(){return ab})),n.d(e,"schemeRdYlGn",(function(){return ib})),n.d(e,"interpolateSpectral",(function(){return sb})),n.d(e,"schemeSpectral",(function(){return ob})),n.d(e,"interpolateBuGn",(function(){return ub})),n.d(e,"schemeBuGn",(function(){return cb})),n.d(e,"interpolateBuPu",(function(){return hb})),n.d(e,"schemeBuPu",(function(){return lb})),n.d(e,"interpolateGnBu",(function(){return db})),n.d(e,"schemeGnBu",(function(){return fb})),n.d(e,"interpolateOrRd",(function(){return gb})),n.d(e,"schemeOrRd",(function(){return pb})),n.d(e,"interpolatePuBuGn",(function(){return vb})),n.d(e,"schemePuBuGn",(function(){return yb})),n.d(e,"interpolatePuBu",(function(){return bb})),n.d(e,"schemePuBu",(function(){return mb})),n.d(e,"interpolatePuRd",(function(){return _b})),n.d(e,"schemePuRd",(function(){return xb})),n.d(e,"interpolateRdPu",(function(){return wb})),n.d(e,"schemeRdPu",(function(){return kb})),n.d(e,"interpolateYlGnBu",(function(){return Tb})),n.d(e,"schemeYlGnBu",(function(){return Eb})),n.d(e,"interpolateYlGn",(function(){return Ab})),n.d(e,"schemeYlGn",(function(){return Cb})),n.d(e,"interpolateYlOrBr",(function(){return Mb})),n.d(e,"schemeYlOrBr",(function(){return Sb})),n.d(e,"interpolateYlOrRd",(function(){return Db})),n.d(e,"schemeYlOrRd",(function(){return Ob})),n.d(e,"interpolateBlues",(function(){return Bb})),n.d(e,"schemeBlues",(function(){return Nb})),n.d(e,"interpolateGreens",(function(){return Pb})),n.d(e,"schemeGreens",(function(){return Lb})),n.d(e,"interpolateGreys",(function(){return Fb})),n.d(e,"schemeGreys",(function(){return Ib})),n.d(e,"interpolatePurples",(function(){return Rb})),n.d(e,"schemePurples",(function(){return jb})),n.d(e,"interpolateReds",(function(){return zb})),n.d(e,"schemeReds",(function(){return Yb})),n.d(e,"interpolateOranges",(function(){return $b})),n.d(e,"schemeOranges",(function(){return Ub})),n.d(e,"interpolateCividis",(function(){return Wb})),n.d(e,"interpolateCubehelixDefault",(function(){return Hb})),n.d(e,"interpolateRainbow",(function(){return Xb})),n.d(e,"interpolateWarm",(function(){return Vb})),n.d(e,"interpolateCool",(function(){return Gb})),n.d(e,"interpolateSinebow",(function(){return Qb})),n.d(e,"interpolateTurbo",(function(){return tx})),n.d(e,"interpolateViridis",(function(){return nx})),n.d(e,"interpolateMagma",(function(){return rx})),n.d(e,"interpolateInferno",(function(){return ix})),n.d(e,"interpolatePlasma",(function(){return ax})),n.d(e,"create",(function(){return ox})),n.d(e,"creator",(function(){return ne})),n.d(e,"local",(function(){return cx})),n.d(e,"matcher",(function(){return gt})),n.d(e,"mouse",(function(){return Nn})),n.d(e,"namespace",(function(){return wt})),n.d(e,"namespaces",(function(){return kt})),n.d(e,"clientPoint",(function(){return On})),n.d(e,"select",(function(){return ke})),n.d(e,"selectAll",(function(){return lx})),n.d(e,"selection",(function(){return _e})),n.d(e,"selector",(function(){return ft})),n.d(e,"selectorAll",(function(){return pt})),n.d(e,"style",(function(){return Lt})),n.d(e,"touch",(function(){return Dn})),n.d(e,"touches",(function(){return hx})),n.d(e,"window",(function(){return Ot})),n.d(e,"event",(function(){return ce})),n.d(e,"customEvent",(function(){return pe})),n.d(e,"arc",(function(){return Nx})),n.d(e,"area",(function(){return jx})),n.d(e,"line",(function(){return Fx})),n.d(e,"pie",(function(){return zx})),n.d(e,"areaRadial",(function(){return Gx})),n.d(e,"radialArea",(function(){return Gx})),n.d(e,"lineRadial",(function(){return Vx})),n.d(e,"radialLine",(function(){return Vx})),n.d(e,"pointRadial",(function(){return qx})),n.d(e,"linkHorizontal",(function(){return n_})),n.d(e,"linkVertical",(function(){return r_})),n.d(e,"linkRadial",(function(){return i_})),n.d(e,"symbol",(function(){return k_})),n.d(e,"symbols",(function(){return __})),n.d(e,"symbolCircle",(function(){return a_})),n.d(e,"symbolCross",(function(){return o_})),n.d(e,"symbolDiamond",(function(){return u_})),n.d(e,"symbolSquare",(function(){return p_})),n.d(e,"symbolStar",(function(){return d_})),n.d(e,"symbolTriangle",(function(){return y_})),n.d(e,"symbolWye",(function(){return x_})),n.d(e,"curveBasisClosed",(function(){return S_})),n.d(e,"curveBasisOpen",(function(){return O_})),n.d(e,"curveBasis",(function(){return C_})),n.d(e,"curveBundle",(function(){return N_})),n.d(e,"curveCardinalClosed",(function(){return F_})),n.d(e,"curveCardinalOpen",(function(){return R_})),n.d(e,"curveCardinal",(function(){return P_})),n.d(e,"curveCatmullRomClosed",(function(){return W_})),n.d(e,"curveCatmullRomOpen",(function(){return V_})),n.d(e,"curveCatmullRom",(function(){return U_})),n.d(e,"curveLinearClosed",(function(){return q_})),n.d(e,"curveLinear",(function(){return Lx})),n.d(e,"curveMonotoneX",(function(){return nk})),n.d(e,"curveMonotoneY",(function(){return rk})),n.d(e,"curveNatural",(function(){return ok})),n.d(e,"curveStep",(function(){return ck})),n.d(e,"curveStepAfter",(function(){return lk})),n.d(e,"curveStepBefore",(function(){return uk})),n.d(e,"stack",(function(){return pk})),n.d(e,"stackOffsetExpand",(function(){return gk})),n.d(e,"stackOffsetDiverging",(function(){return yk})),n.d(e,"stackOffsetNone",(function(){return hk})),n.d(e,"stackOffsetSilhouette",(function(){return vk})),n.d(e,"stackOffsetWiggle",(function(){return mk})),n.d(e,"stackOrderAppearance",(function(){return bk})),n.d(e,"stackOrderAscending",(function(){return _k})),n.d(e,"stackOrderDescending",(function(){return wk})),n.d(e,"stackOrderInsideOut",(function(){return Ek})),n.d(e,"stackOrderNone",(function(){return fk})),n.d(e,"stackOrderReverse",(function(){return Tk})),n.d(e,"timeInterval",(function(){return Bg})),n.d(e,"timeMillisecond",(function(){return py})),n.d(e,"timeMilliseconds",(function(){return gy})),n.d(e,"utcMillisecond",(function(){return py})),n.d(e,"utcMilliseconds",(function(){return gy})),n.d(e,"timeSecond",(function(){return hy})),n.d(e,"timeSeconds",(function(){return fy})),n.d(e,"utcSecond",(function(){return hy})),n.d(e,"utcSeconds",(function(){return fy})),n.d(e,"timeMinute",(function(){return cy})),n.d(e,"timeMinutes",(function(){return uy})),n.d(e,"timeHour",(function(){return ay})),n.d(e,"timeHours",(function(){return oy})),n.d(e,"timeDay",(function(){return ny})),n.d(e,"timeDays",(function(){return ry})),n.d(e,"timeWeek",(function(){return zg})),n.d(e,"timeWeeks",(function(){return qg})),n.d(e,"timeSunday",(function(){return zg})),n.d(e,"timeSundays",(function(){return qg})),n.d(e,"timeMonday",(function(){return Ug})),n.d(e,"timeMondays",(function(){return Xg})),n.d(e,"timeTuesday",(function(){return $g})),n.d(e,"timeTuesdays",(function(){return Zg})),n.d(e,"timeWednesday",(function(){return Wg})),n.d(e,"timeWednesdays",(function(){return Jg})),n.d(e,"timeThursday",(function(){return Hg})),n.d(e,"timeThursdays",(function(){return Kg})),n.d(e,"timeFriday",(function(){return Vg})),n.d(e,"timeFridays",(function(){return Qg})),n.d(e,"timeSaturday",(function(){return Gg})),n.d(e,"timeSaturdays",(function(){return ty})),n.d(e,"timeMonth",(function(){return jg})),n.d(e,"timeMonths",(function(){return Rg})),n.d(e,"timeYear",(function(){return Pg})),n.d(e,"timeYears",(function(){return Ig})),n.d(e,"utcMinute",(function(){return gm})),n.d(e,"utcMinutes",(function(){return ym})),n.d(e,"utcHour",(function(){return fm})),n.d(e,"utcHours",(function(){return dm})),n.d(e,"utcDay",(function(){return Ny})),n.d(e,"utcDays",(function(){return By})),n.d(e,"utcWeek",(function(){return vy})),n.d(e,"utcWeeks",(function(){return Ey})),n.d(e,"utcSunday",(function(){return vy})),n.d(e,"utcSundays",(function(){return Ey})),n.d(e,"utcMonday",(function(){return my})),n.d(e,"utcMondays",(function(){return Ty})),n.d(e,"utcTuesday",(function(){return by})),n.d(e,"utcTuesdays",(function(){return Cy})),n.d(e,"utcWednesday",(function(){return xy})),n.d(e,"utcWednesdays",(function(){return Ay})),n.d(e,"utcThursday",(function(){return _y})),n.d(e,"utcThursdays",(function(){return Sy})),n.d(e,"utcFriday",(function(){return ky})),n.d(e,"utcFridays",(function(){return My})),n.d(e,"utcSaturday",(function(){return wy})),n.d(e,"utcSaturdays",(function(){return Oy})),n.d(e,"utcMonth",(function(){return um})),n.d(e,"utcMonths",(function(){return lm})),n.d(e,"utcYear",(function(){return Py})),n.d(e,"utcYears",(function(){return Iy})),n.d(e,"timeFormatDefaultLocale",(function(){return rm})),n.d(e,"timeFormat",(function(){return Uy})),n.d(e,"timeParse",(function(){return $y})),n.d(e,"utcFormat",(function(){return Wy})),n.d(e,"utcParse",(function(){return Hy})),n.d(e,"timeFormatLocale",(function(){return Yy})),n.d(e,"isoFormat",(function(){return Ck})),n.d(e,"isoParse",(function(){return Ak})),n.d(e,"now",(function(){return zn})),n.d(e,"timer",(function(){return Wn})),n.d(e,"timerFlush",(function(){return Hn})),n.d(e,"timeout",(function(){return Xn})),n.d(e,"interval",(function(){return Sk})),n.d(e,"transition",(function(){return zr})),n.d(e,"active",(function(){return Zr})),n.d(e,"interrupt",(function(){return or})),n.d(e,"voronoi",(function(){return dw})),n.d(e,"zoom",(function(){return Aw})),n.d(e,"zoomTransform",(function(){return mw})),n.d(e,"zoomIdentity",(function(){return vw}))},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),function(t){for(var n in t)e.hasOwnProperty(n)||(e[n]=t[n])}(n(172))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,18],u=[1,20],l=[1,21],h=[1,22],f=[1,24],d=[1,25],p=[1,26],g=[1,27],y=[1,28],v=[1,29],m=[1,32],b=[1,33],x=[1,36],_=[1,4,5,16,21,22,23,25,27,28,29,30,31,33,35,36,37,48,58],k=[1,44],w=[4,5,16,21,22,23,25,27,28,29,30,31,33,37,48,58],E=[4,5,16,21,22,23,25,27,28,29,30,31,33,36,37,48,58],T=[4,5,16,21,22,23,25,27,28,29,30,31,33,35,37,48,58],C=[46,47,48],A=[1,4,5,7,16,21,22,23,25,27,28,29,30,31,33,35,36,37,48,58],S={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NEWLINE:5,directive:6,SD:7,document:8,line:9,statement:10,openDirective:11,typeDirective:12,closeDirective:13,":":14,argDirective:15,participant:16,actor:17,AS:18,restOfLine:19,signal:20,autonumber:21,activate:22,deactivate:23,note_statement:24,title:25,text2:26,loop:27,end:28,rect:29,opt:30,alt:31,else_sections:32,par:33,par_sections:34,and:35,else:36,note:37,placement:38,over:39,actor_pair:40,spaceList:41,",":42,left_of:43,right_of:44,signaltype:45,"+":46,"-":47,ACTOR:48,SOLID_OPEN_ARROW:49,DOTTED_OPEN_ARROW:50,SOLID_ARROW:51,DOTTED_ARROW:52,SOLID_CROSS:53,DOTTED_CROSS:54,SOLID_POINT:55,DOTTED_POINT:56,TXT:57,open_directive:58,type_directive:59,arg_directive:60,close_directive:61,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NEWLINE",7:"SD",14:":",16:"participant",18:"AS",19:"restOfLine",21:"autonumber",22:"activate",23:"deactivate",25:"title",27:"loop",28:"end",29:"rect",30:"opt",31:"alt",33:"par",35:"and",36:"else",37:"note",39:"over",42:",",43:"left_of",44:"right_of",46:"+",47:"-",48:"ACTOR",49:"SOLID_OPEN_ARROW",50:"DOTTED_OPEN_ARROW",51:"SOLID_ARROW",52:"DOTTED_ARROW",53:"SOLID_CROSS",54:"DOTTED_CROSS",55:"SOLID_POINT",56:"DOTTED_POINT",57:"TXT",58:"open_directive",59:"type_directive",60:"arg_directive",61:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[6,4],[6,6],[10,5],[10,3],[10,2],[10,1],[10,3],[10,3],[10,2],[10,3],[10,4],[10,4],[10,4],[10,4],[10,4],[10,1],[34,1],[34,4],[32,1],[32,4],[24,4],[24,4],[41,2],[41,1],[40,3],[40,1],[38,1],[38,1],[20,5],[20,5],[20,4],[17,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[45,1],[26,1],[11,1],[12,1],[15,1],[13,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.apply(a[s]),a[s];case 5:this.$=[];break;case 6:a[s-1].push(a[s]),this.$=a[s-1];break;case 7:case 8:this.$=a[s];break;case 9:this.$=[];break;case 12:a[s-3].description=r.parseMessage(a[s-1]),this.$=a[s-3];break;case 13:this.$=a[s-1];break;case 15:r.enableSequenceNumbers();break;case 16:this.$={type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]};break;case 17:this.$={type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-1]};break;case 19:this.$=[{type:"setTitle",text:a[s-1]}];break;case 20:a[s-1].unshift({type:"loopStart",loopText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.LOOP_START}),a[s-1].push({type:"loopEnd",loopText:a[s-2],signalType:r.LINETYPE.LOOP_END}),this.$=a[s-1];break;case 21:a[s-1].unshift({type:"rectStart",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_START}),a[s-1].push({type:"rectEnd",color:r.parseMessage(a[s-2]),signalType:r.LINETYPE.RECT_END}),this.$=a[s-1];break;case 22:a[s-1].unshift({type:"optStart",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_START}),a[s-1].push({type:"optEnd",optText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.OPT_END}),this.$=a[s-1];break;case 23:a[s-1].unshift({type:"altStart",altText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.ALT_START}),a[s-1].push({type:"altEnd",signalType:r.LINETYPE.ALT_END}),this.$=a[s-1];break;case 24:a[s-1].unshift({type:"parStart",parText:r.parseMessage(a[s-2]),signalType:r.LINETYPE.PAR_START}),a[s-1].push({type:"parEnd",signalType:r.LINETYPE.PAR_END}),this.$=a[s-1];break;case 27:this.$=a[s-3].concat([{type:"and",parText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.PAR_AND},a[s]]);break;case 29:this.$=a[s-3].concat([{type:"else",altText:r.parseMessage(a[s-1]),signalType:r.LINETYPE.ALT_ELSE},a[s]]);break;case 30:this.$=[a[s-1],{type:"addNote",placement:a[s-2],actor:a[s-1].actor,text:a[s]}];break;case 31:a[s-2]=[].concat(a[s-1],a[s-1]).slice(0,2),a[s-2][0]=a[s-2][0].actor,a[s-2][1]=a[s-2][1].actor,this.$=[a[s-1],{type:"addNote",placement:r.PLACEMENT.OVER,actor:a[s-2].slice(0,2),text:a[s]}];break;case 34:this.$=[a[s-2],a[s]];break;case 35:this.$=a[s];break;case 36:this.$=r.PLACEMENT.LEFTOF;break;case 37:this.$=r.PLACEMENT.RIGHTOF;break;case 38:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeStart",signalType:r.LINETYPE.ACTIVE_START,actor:a[s-1]}];break;case 39:this.$=[a[s-4],a[s-1],{type:"addMessage",from:a[s-4].actor,to:a[s-1].actor,signalType:a[s-3],msg:a[s]},{type:"activeEnd",signalType:r.LINETYPE.ACTIVE_END,actor:a[s-4]}];break;case 40:this.$=[a[s-3],a[s-1],{type:"addMessage",from:a[s-3].actor,to:a[s-1].actor,signalType:a[s-2],msg:a[s]}];break;case 41:this.$={type:"addActor",actor:a[s]};break;case 42:this.$=r.LINETYPE.SOLID_OPEN;break;case 43:this.$=r.LINETYPE.DOTTED_OPEN;break;case 44:this.$=r.LINETYPE.SOLID;break;case 45:this.$=r.LINETYPE.DOTTED;break;case 46:this.$=r.LINETYPE.SOLID_CROSS;break;case 47:this.$=r.LINETYPE.DOTTED_CROSS;break;case 48:this.$=r.LINETYPE.SOLID_POINT;break;case 49:this.$=r.LINETYPE.DOTTED_POINT;break;case 50:this.$=r.parseMessage(a[s].trim().substring(1));break;case 51:r.parseDirective("%%{","open_directive");break;case 52:r.parseDirective(a[s],"type_directive");break;case 53:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 54:r.parseDirective("}%%","close_directive","sequence")}},table:[{3:1,4:e,5:n,6:4,7:r,11:6,58:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,11:6,58:i},{3:9,4:e,5:n,6:4,7:r,11:6,58:i},{3:10,4:e,5:n,6:4,7:r,11:6,58:i},t([1,4,5,16,21,22,23,25,27,29,30,31,33,37,48,58],a,{8:11}),{12:12,59:[1,13]},{59:[2,51]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,29:p,30:g,31:y,33:v,37:m,48:b,58:i},{13:34,14:[1,35],61:x},t([14,61],[2,52]),t(_,[2,6]),{6:30,10:37,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,29:p,30:g,31:y,33:v,37:m,48:b,58:i},t(_,[2,8]),t(_,[2,9]),{17:38,48:b},{5:[1,39]},t(_,[2,15]),{17:40,48:b},{17:41,48:b},{5:[1,42]},{26:43,57:k},{19:[1,45]},{19:[1,46]},{19:[1,47]},{19:[1,48]},{19:[1,49]},t(_,[2,25]),{45:50,49:[1,51],50:[1,52],51:[1,53],52:[1,54],53:[1,55],54:[1,56],55:[1,57],56:[1,58]},{38:59,39:[1,60],43:[1,61],44:[1,62]},t([5,18,42,49,50,51,52,53,54,55,56,57],[2,41]),{5:[1,63]},{15:64,60:[1,65]},{5:[2,54]},t(_,[2,7]),{5:[1,67],18:[1,66]},t(_,[2,14]),{5:[1,68]},{5:[1,69]},t(_,[2,18]),{5:[1,70]},{5:[2,50]},t(w,a,{8:71}),t(w,a,{8:72}),t(w,a,{8:73}),t(E,a,{32:74,8:75}),t(T,a,{34:76,8:77}),{17:80,46:[1,78],47:[1,79],48:b},t(C,[2,42]),t(C,[2,43]),t(C,[2,44]),t(C,[2,45]),t(C,[2,46]),t(C,[2,47]),t(C,[2,48]),t(C,[2,49]),{17:81,48:b},{17:83,40:82,48:b},{48:[2,36]},{48:[2,37]},t(A,[2,10]),{13:84,61:x},{61:[2,53]},{19:[1,85]},t(_,[2,13]),t(_,[2,16]),t(_,[2,17]),t(_,[2,19]),{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[1,86],29:p,30:g,31:y,33:v,37:m,48:b,58:i},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[1,87],29:p,30:g,31:y,33:v,37:m,48:b,58:i},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[1,88],29:p,30:g,31:y,33:v,37:m,48:b,58:i},{28:[1,89]},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[2,28],29:p,30:g,31:y,33:v,36:[1,90],37:m,48:b,58:i},{28:[1,91]},{4:o,5:s,6:30,9:14,10:16,11:6,16:c,17:31,20:19,21:u,22:l,23:h,24:23,25:f,27:d,28:[2,26],29:p,30:g,31:y,33:v,35:[1,92],37:m,48:b,58:i},{17:93,48:b},{17:94,48:b},{26:95,57:k},{26:96,57:k},{26:97,57:k},{42:[1,98],57:[2,35]},{5:[1,99]},{5:[1,100]},t(_,[2,20]),t(_,[2,21]),t(_,[2,22]),t(_,[2,23]),{19:[1,101]},t(_,[2,24]),{19:[1,102]},{26:103,57:k},{26:104,57:k},{5:[2,40]},{5:[2,30]},{5:[2,31]},{17:105,48:b},t(A,[2,11]),t(_,[2,12]),t(E,a,{8:75,32:106}),t(T,a,{8:77,34:107}),{5:[2,38]},{5:[2,39]},{57:[2,34]},{28:[2,29]},{28:[2,27]}],defaultActions:{7:[2,51],8:[2,1],9:[2,2],10:[2,3],36:[2,54],44:[2,50],61:[2,36],62:[2,37],65:[2,53],95:[2,40],96:[2,30],97:[2,31],103:[2,38],104:[2,39],105:[2,34],106:[2,29],107:[2,27]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},M={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),58;case 1:return this.begin("type_directive"),59;case 2:return this.popState(),this.begin("arg_directive"),14;case 3:return this.popState(),this.popState(),61;case 4:return 60;case 5:return 5;case 6:case 7:case 8:case 9:case 10:break;case 11:return this.begin("ID"),16;case 12:return e.yytext=e.yytext.trim(),this.begin("ALIAS"),48;case 13:return this.popState(),this.popState(),this.begin("LINE"),18;case 14:return this.popState(),this.popState(),5;case 15:return this.begin("LINE"),27;case 16:return this.begin("LINE"),29;case 17:return this.begin("LINE"),30;case 18:return this.begin("LINE"),31;case 19:return this.begin("LINE"),36;case 20:return this.begin("LINE"),33;case 21:return this.begin("LINE"),35;case 22:return this.popState(),19;case 23:return 28;case 24:return 43;case 25:return 44;case 26:return 39;case 27:return 37;case 28:return this.begin("ID"),22;case 29:return this.begin("ID"),23;case 30:return 25;case 31:return 7;case 32:return 21;case 33:return 42;case 34:return 5;case 35:return e.yytext=e.yytext.trim(),48;case 36:return 51;case 37:return 52;case 38:return 49;case 39:return 50;case 40:return 53;case 41:return 54;case 42:return 55;case 43:return 56;case 44:return 57;case 45:return 46;case 46:return 47;case 47:return 5;case 48:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:participant\b)/i,/^(?:[^\->:\n,;]+?(?=((?!\n)\s)+as(?!\n)\s|[#\n;]|$))/i,/^(?:as\b)/i,/^(?:(?:))/i,/^(?:loop\b)/i,/^(?:rect\b)/i,/^(?:opt\b)/i,/^(?:alt\b)/i,/^(?:else\b)/i,/^(?:par\b)/i,/^(?:and\b)/i,/^(?:(?:[:]?(?:no)?wrap)?[^#\n;]*)/i,/^(?:end\b)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:over\b)/i,/^(?:note\b)/i,/^(?:activate\b)/i,/^(?:deactivate\b)/i,/^(?:title\b)/i,/^(?:sequenceDiagram\b)/i,/^(?:autonumber\b)/i,/^(?:,)/i,/^(?:;)/i,/^(?:[^\+\->:\n,;]+((?!(-x|--x|-\)|--\)))[\-]*[^\+\->:\n,;]+)*)/i,/^(?:->>)/i,/^(?:-->>)/i,/^(?:->)/i,/^(?:-->)/i,/^(?:-[x])/i,/^(?:--[x])/i,/^(?:-[\)])/i,/^(?:--[\)])/i,/^(?::(?:(?:no)?wrap)?[^#\n;]+)/i,/^(?:\+)/i,/^(?:-)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1,8],inclusive:!1},type_directive:{rules:[2,3,8],inclusive:!1},arg_directive:{rules:[3,4,8],inclusive:!1},ID:{rules:[7,8,12],inclusive:!1},ALIAS:{rules:[7,8,13,14],inclusive:!1},LINE:{rules:[7,8,22],inclusive:!1},INITIAL:{rules:[0,5,6,8,9,10,11,15,16,17,18,19,20,21,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],inclusive:!0}}};function O(){this.yy={}}return S.lexer=M,O.prototype=S,S.Parser=O,new O}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){var r=n(198);t.exports={Graph:r.Graph,json:n(301),alg:n(302),version:r.version}},function(t,e,n){var r;try{r={cloneDeep:n(313),constant:n(86),defaults:n(154),each:n(87),filter:n(128),find:n(314),flatten:n(156),forEach:n(126),forIn:n(319),has:n(93),isUndefined:n(139),last:n(320),map:n(140),mapValues:n(321),max:n(322),merge:n(324),min:n(329),minBy:n(330),now:n(331),pick:n(161),range:n(162),reduce:n(142),sortBy:n(338),uniqueId:n(163),values:n(147),zipObject:n(343)}}catch(t){}r||(r=window._),t.exports=r},function(t,e){var n=Array.isArray;t.exports=n},function(t,e,n){ +/** + * @license + * Copyright (c) 2012-2013 Chris Pettitt + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +t.exports={graphlib:n(311),dagre:n(153),intersect:n(368),render:n(370),util:n(12),version:n(382)}},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children||(t.children=[]),Object.defineProperty(t,"loaded",{enumerable:!0,get:function(){return t.l}}),Object.defineProperty(t,"id",{enumerable:!0,get:function(){return t.i}}),t.webpackPolyfill=1),t}},function(t,e,n){"use strict";var r=n(4),i=n(17).Graph;function a(t,e,n,i){var a;do{a=r.uniqueId(i)}while(t.hasNode(a));return n.dummy=e,t.setNode(a,n),a}function o(t){return r.max(r.map(t.nodes(),(function(e){var n=t.node(e).rank;if(!r.isUndefined(n))return n})))}t.exports={addDummyNode:a,simplify:function(t){var e=(new i).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){var r=e.edge(n.v,n.w)||{weight:0,minlen:1},i=t.edge(n);e.setEdge(n.v,n.w,{weight:r.weight+i.weight,minlen:Math.max(r.minlen,i.minlen)})})),e},asNonCompoundGraph:function(t){var e=new i({multigraph:t.isMultigraph()}).setGraph(t.graph());return r.forEach(t.nodes(),(function(n){t.children(n).length||e.setNode(n,t.node(n))})),r.forEach(t.edges(),(function(n){e.setEdge(n,t.edge(n))})),e},successorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.outEdges(e),(function(e){n[e.w]=(n[e.w]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},predecessorWeights:function(t){var e=r.map(t.nodes(),(function(e){var n={};return r.forEach(t.inEdges(e),(function(e){n[e.v]=(n[e.v]||0)+t.edge(e).weight})),n}));return r.zipObject(t.nodes(),e)},intersectRect:function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;if(!o&&!s)throw new Error("Not possible to find intersection inside of the rectangle");Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=u*o/s,r=u):(o<0&&(c=-c),n=c,r=c*s/o);return{x:i+n,y:a+r}},buildLayerMatrix:function(t){var e=r.map(r.range(o(t)+1),(function(){return[]}));return r.forEach(t.nodes(),(function(n){var i=t.node(n),a=i.rank;r.isUndefined(a)||(e[a][i.order]=n)})),e},normalizeRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank})));r.forEach(t.nodes(),(function(n){var i=t.node(n);r.has(i,"rank")&&(i.rank-=e)}))},removeEmptyRanks:function(t){var e=r.min(r.map(t.nodes(),(function(e){return t.node(e).rank}))),n=[];r.forEach(t.nodes(),(function(r){var i=t.node(r).rank-e;n[i]||(n[i]=[]),n[i].push(r)}));var i=0,a=t.graph().nodeRankFactor;r.forEach(n,(function(e,n){r.isUndefined(e)&&n%a!=0?--i:i&&r.forEach(e,(function(e){t.node(e).rank+=i}))}))},addBorderNode:function(t,e,n,r){var i={width:0,height:0};arguments.length>=4&&(i.rank=n,i.order=r);return a(t,"border",i,e)},maxRank:o,partition:function(t,e){var n={lhs:[],rhs:[]};return r.forEach(t,(function(t){e(t)?n.lhs.push(t):n.rhs.push(t)})),n},time:function(t,e){var n=r.now();try{return e()}finally{console.log(t+" time: "+(r.now()-n)+"ms")}},notime:function(t,e){return e()}}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(173),i=n(174),a=n(175),o={channel:r.default,lang:i.default,unit:a.default};e.default=o},function(t,e,n){var r;try{r={clone:n(199),constant:n(86),each:n(87),filter:n(128),has:n(93),isArray:n(5),isEmpty:n(276),isFunction:n(37),isUndefined:n(139),keys:n(30),map:n(140),reduce:n(142),size:n(279),transform:n(285),union:n(286),values:n(147)}}catch(t){}r||(r=window._),t.exports=r},function(t,e){t.exports=function(t){var e=typeof t;return null!=t&&("object"==e||"function"==e)}},function(t,e,n){var r=n(43);t.exports={isSubgraph:function(t,e){return!!t.children(e).length},edgeToId:function(t){return a(t.v)+":"+a(t.w)+":"+a(t.name)},applyStyle:function(t,e){e&&t.attr("style",e)},applyClass:function(t,e,n){e&&t.attr("class",e).attr("class",n+" "+t.attr("class"))},applyTransition:function(t,e){var n=e.graph();if(r.isPlainObject(n)){var i=n.transition;if(r.isFunction(i))return i(t)}return t}};var i=/:/g;function a(t){return t?String(t).replace(i,"\\:"):""}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,7],n=[1,6],r=[1,14],i=[1,25],a=[1,28],o=[1,26],s=[1,27],c=[1,29],u=[1,30],l=[1,31],h=[1,32],f=[1,34],d=[1,35],p=[1,36],g=[10,19],y=[1,48],v=[1,49],m=[1,50],b=[1,51],x=[1,52],_=[1,53],k=[10,19,25,32,33,41,44,45,46,47,48,49,54,56],w=[10,19,23,25,32,33,37,41,44,45,46,47,48,49,54,56,71,72,73],E=[10,13,17,19],T=[41,71,72,73],C=[41,48,49,71,72,73],A=[41,44,45,46,47,71,72,73],S=[10,19,25],M=[1,85],O={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,directive:5,graphConfig:6,openDirective:7,typeDirective:8,closeDirective:9,NEWLINE:10,":":11,argDirective:12,open_directive:13,type_directive:14,arg_directive:15,close_directive:16,CLASS_DIAGRAM:17,statements:18,EOF:19,statement:20,className:21,alphaNumToken:22,GENERICTYPE:23,relationStatement:24,LABEL:25,classStatement:26,methodStatement:27,annotationStatement:28,clickStatement:29,cssClassStatement:30,CLASS:31,STYLE_SEPARATOR:32,STRUCT_START:33,members:34,STRUCT_STOP:35,ANNOTATION_START:36,ANNOTATION_END:37,MEMBER:38,SEPARATOR:39,relation:40,STR:41,relationType:42,lineType:43,AGGREGATION:44,EXTENSION:45,COMPOSITION:46,DEPENDENCY:47,LINE:48,DOTTED_LINE:49,CALLBACK:50,LINK:51,LINK_TARGET:52,CLICK:53,CALLBACK_NAME:54,CALLBACK_ARGS:55,HREF:56,CSSCLASS:57,commentToken:58,textToken:59,graphCodeTokens:60,textNoTagsToken:61,TAGSTART:62,TAGEND:63,"==":64,"--":65,PCT:66,DEFAULT:67,SPACE:68,MINUS:69,keywords:70,UNICODE_TEXT:71,NUM:72,ALPHA:73,$accept:0,$end:1},terminals_:{2:"error",10:"NEWLINE",11:":",13:"open_directive",14:"type_directive",15:"arg_directive",16:"close_directive",17:"CLASS_DIAGRAM",19:"EOF",23:"GENERICTYPE",25:"LABEL",31:"CLASS",32:"STYLE_SEPARATOR",33:"STRUCT_START",35:"STRUCT_STOP",36:"ANNOTATION_START",37:"ANNOTATION_END",38:"MEMBER",39:"SEPARATOR",41:"STR",44:"AGGREGATION",45:"EXTENSION",46:"COMPOSITION",47:"DEPENDENCY",48:"LINE",49:"DOTTED_LINE",50:"CALLBACK",51:"LINK",52:"LINK_TARGET",53:"CLICK",54:"CALLBACK_NAME",55:"CALLBACK_ARGS",56:"HREF",57:"CSSCLASS",60:"graphCodeTokens",62:"TAGSTART",63:"TAGEND",64:"==",65:"--",66:"PCT",67:"DEFAULT",68:"SPACE",69:"MINUS",70:"keywords",71:"UNICODE_TEXT",72:"NUM",73:"ALPHA"},productions_:[0,[3,1],[3,2],[4,1],[5,4],[5,6],[7,1],[8,1],[12,1],[9,1],[6,4],[18,1],[18,2],[18,3],[21,1],[21,2],[21,3],[21,2],[20,1],[20,2],[20,1],[20,1],[20,1],[20,1],[20,1],[20,1],[26,2],[26,4],[26,5],[26,7],[28,4],[34,1],[34,2],[27,1],[27,2],[27,1],[27,1],[24,3],[24,4],[24,4],[24,5],[40,3],[40,2],[40,2],[40,1],[42,1],[42,1],[42,1],[42,1],[43,1],[43,1],[29,3],[29,4],[29,3],[29,4],[29,4],[29,5],[29,3],[29,4],[29,4],[29,5],[29,3],[29,4],[29,4],[29,5],[30,3],[58,1],[58,1],[59,1],[59,1],[59,1],[59,1],[59,1],[59,1],[59,1],[61,1],[61,1],[61,1],[61,1],[22,1],[22,1],[22,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 6:r.parseDirective("%%{","open_directive");break;case 7:r.parseDirective(a[s],"type_directive");break;case 8:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 9:r.parseDirective("}%%","close_directive","class");break;case 14:this.$=a[s];break;case 15:this.$=a[s-1]+a[s];break;case 16:this.$=a[s-2]+"~"+a[s-1]+a[s];break;case 17:this.$=a[s-1]+"~"+a[s];break;case 18:r.addRelation(a[s]);break;case 19:a[s-1].title=r.cleanupLabel(a[s]),r.addRelation(a[s-1]);break;case 26:r.addClass(a[s]);break;case 27:r.addClass(a[s-2]),r.setCssClass(a[s-2],a[s]);break;case 28:r.addClass(a[s-3]),r.addMembers(a[s-3],a[s-1]);break;case 29:r.addClass(a[s-5]),r.setCssClass(a[s-5],a[s-3]),r.addMembers(a[s-5],a[s-1]);break;case 30:r.addAnnotation(a[s],a[s-2]);break;case 31:this.$=[a[s]];break;case 32:a[s].push(a[s-1]),this.$=a[s];break;case 33:break;case 34:r.addMember(a[s-1],r.cleanupLabel(a[s]));break;case 35:case 36:break;case 37:this.$={id1:a[s-2],id2:a[s],relation:a[s-1],relationTitle1:"none",relationTitle2:"none"};break;case 38:this.$={id1:a[s-3],id2:a[s],relation:a[s-1],relationTitle1:a[s-2],relationTitle2:"none"};break;case 39:this.$={id1:a[s-3],id2:a[s],relation:a[s-2],relationTitle1:"none",relationTitle2:a[s-1]};break;case 40:this.$={id1:a[s-4],id2:a[s],relation:a[s-2],relationTitle1:a[s-3],relationTitle2:a[s-1]};break;case 41:this.$={type1:a[s-2],type2:a[s],lineType:a[s-1]};break;case 42:this.$={type1:"none",type2:a[s],lineType:a[s-1]};break;case 43:this.$={type1:a[s-1],type2:"none",lineType:a[s]};break;case 44:this.$={type1:"none",type2:"none",lineType:a[s]};break;case 45:this.$=r.relationType.AGGREGATION;break;case 46:this.$=r.relationType.EXTENSION;break;case 47:this.$=r.relationType.COMPOSITION;break;case 48:this.$=r.relationType.DEPENDENCY;break;case 49:this.$=r.lineType.LINE;break;case 50:this.$=r.lineType.DOTTED_LINE;break;case 51:case 57:this.$=a[s-2],r.setClickEvent(a[s-1],a[s]);break;case 52:case 58:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 53:case 61:this.$=a[s-2],r.setLink(a[s-1],a[s]);break;case 54:this.$=a[s-3],r.setLink(a[s-2],a[s-1],a[s]);break;case 55:case 63:this.$=a[s-3],r.setLink(a[s-2],a[s-1]),r.setTooltip(a[s-2],a[s]);break;case 56:case 64:this.$=a[s-4],r.setLink(a[s-3],a[s-2],a[s]),r.setTooltip(a[s-3],a[s-1]);break;case 59:this.$=a[s-3],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 60:this.$=a[s-4],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setTooltip(a[s-3],a[s]);break;case 62:this.$=a[s-3],r.setLink(a[s-2],a[s-1],a[s]);break;case 65:r.setCssClass(a[s-1],a[s])}},table:[{3:1,4:2,5:3,6:4,7:5,13:e,17:n},{1:[3]},{1:[2,1]},{3:8,4:2,5:3,6:4,7:5,13:e,17:n},{1:[2,3]},{8:9,14:[1,10]},{10:[1,11]},{14:[2,6]},{1:[2,2]},{9:12,11:[1,13],16:r},t([11,16],[2,7]),{5:23,7:5,13:e,18:15,20:16,21:24,22:33,24:17,26:18,27:19,28:20,29:21,30:22,31:i,36:a,38:o,39:s,50:c,51:u,53:l,57:h,71:f,72:d,73:p},{10:[1,37]},{12:38,15:[1,39]},{10:[2,9]},{19:[1,40]},{10:[1,41],19:[2,11]},t(g,[2,18],{25:[1,42]}),t(g,[2,20]),t(g,[2,21]),t(g,[2,22]),t(g,[2,23]),t(g,[2,24]),t(g,[2,25]),t(g,[2,33],{40:43,42:46,43:47,25:[1,45],41:[1,44],44:y,45:v,46:m,47:b,48:x,49:_}),{21:54,22:33,71:f,72:d,73:p},t(g,[2,35]),t(g,[2,36]),{22:55,71:f,72:d,73:p},{21:56,22:33,71:f,72:d,73:p},{21:57,22:33,71:f,72:d,73:p},{21:58,22:33,71:f,72:d,73:p},{41:[1,59]},t(k,[2,14],{22:33,21:60,23:[1,61],71:f,72:d,73:p}),t(w,[2,79]),t(w,[2,80]),t(w,[2,81]),t(E,[2,4]),{9:62,16:r},{16:[2,8]},{1:[2,10]},{5:23,7:5,13:e,18:63,19:[2,12],20:16,21:24,22:33,24:17,26:18,27:19,28:20,29:21,30:22,31:i,36:a,38:o,39:s,50:c,51:u,53:l,57:h,71:f,72:d,73:p},t(g,[2,19]),{21:64,22:33,41:[1,65],71:f,72:d,73:p},{40:66,42:46,43:47,44:y,45:v,46:m,47:b,48:x,49:_},t(g,[2,34]),{43:67,48:x,49:_},t(T,[2,44],{42:68,44:y,45:v,46:m,47:b}),t(C,[2,45]),t(C,[2,46]),t(C,[2,47]),t(C,[2,48]),t(A,[2,49]),t(A,[2,50]),t(g,[2,26],{32:[1,69],33:[1,70]}),{37:[1,71]},{41:[1,72]},{41:[1,73]},{54:[1,74],56:[1,75]},{22:76,71:f,72:d,73:p},t(k,[2,15]),t(k,[2,17],{22:33,21:77,71:f,72:d,73:p}),{10:[1,78]},{19:[2,13]},t(S,[2,37]),{21:79,22:33,71:f,72:d,73:p},{21:80,22:33,41:[1,81],71:f,72:d,73:p},t(T,[2,43],{42:82,44:y,45:v,46:m,47:b}),t(T,[2,42]),{22:83,71:f,72:d,73:p},{34:84,38:M},{21:86,22:33,71:f,72:d,73:p},t(g,[2,51],{41:[1,87]}),t(g,[2,53],{41:[1,89],52:[1,88]}),t(g,[2,57],{41:[1,90],55:[1,91]}),t(g,[2,61],{41:[1,93],52:[1,92]}),t(g,[2,65]),t(k,[2,16]),t(E,[2,5]),t(S,[2,39]),t(S,[2,38]),{21:94,22:33,71:f,72:d,73:p},t(T,[2,41]),t(g,[2,27],{33:[1,95]}),{35:[1,96]},{34:97,35:[2,31],38:M},t(g,[2,30]),t(g,[2,52]),t(g,[2,54]),t(g,[2,55],{52:[1,98]}),t(g,[2,58]),t(g,[2,59],{41:[1,99]}),t(g,[2,62]),t(g,[2,63],{52:[1,100]}),t(S,[2,40]),{34:101,38:M},t(g,[2,28]),{35:[2,32]},t(g,[2,56]),t(g,[2,60]),t(g,[2,64]),{35:[1,102]},t(g,[2,29])],defaultActions:{2:[2,1],4:[2,3],7:[2,6],8:[2,2],14:[2,9],39:[2,8],40:[2,10],63:[2,13],97:[2,32]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},D={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),13;case 1:return this.begin("type_directive"),14;case 2:return this.popState(),this.begin("arg_directive"),11;case 3:return this.popState(),this.popState(),16;case 4:return 15;case 5:case 6:break;case 7:return 10;case 8:break;case 9:case 10:return 17;case 11:return this.begin("struct"),33;case 12:return"EOF_IN_STRUCT";case 13:return"OPEN_IN_STRUCT";case 14:return this.popState(),35;case 15:break;case 16:return"MEMBER";case 17:return 31;case 18:return 57;case 19:return 50;case 20:return 51;case 21:return 53;case 22:return 36;case 23:return 37;case 24:this.begin("generic");break;case 25:this.popState();break;case 26:return"GENERICTYPE";case 27:this.begin("string");break;case 28:this.popState();break;case 29:return"STR";case 30:this.begin("href");break;case 31:this.popState();break;case 32:return 56;case 33:this.begin("callback_name");break;case 34:this.popState();break;case 35:this.popState(),this.begin("callback_args");break;case 36:return 54;case 37:this.popState();break;case 38:return 55;case 39:case 40:case 41:case 42:return 52;case 43:case 44:return 45;case 45:case 46:return 47;case 47:return 46;case 48:return 44;case 49:return 48;case 50:return 49;case 51:return 25;case 52:return 32;case 53:return 69;case 54:return"DOT";case 55:return"PLUS";case 56:return 66;case 57:case 58:return"EQUALS";case 59:return 73;case 60:return"PUNCTUATION";case 61:return 72;case 62:return 71;case 63:return 68;case 64:return 19}},rules:[/^(?:%%\{)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)*[^\n]*(\r?\n?)+)/,/^(?:%%[^\n]*(\r?\n)*)/,/^(?:(\r?\n)+)/,/^(?:\s+)/,/^(?:classDiagram-v2\b)/,/^(?:classDiagram\b)/,/^(?:[{])/,/^(?:$)/,/^(?:[{])/,/^(?:[}])/,/^(?:[\n])/,/^(?:[^{}\n]*)/,/^(?:class\b)/,/^(?:cssClass\b)/,/^(?:callback\b)/,/^(?:link\b)/,/^(?:click\b)/,/^(?:<<)/,/^(?:>>)/,/^(?:[~])/,/^(?:[~])/,/^(?:[^~]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:\s*<\|)/,/^(?:\s*\|>)/,/^(?:\s*>)/,/^(?:\s*<)/,/^(?:\s*\*)/,/^(?:\s*o\b)/,/^(?:--)/,/^(?:\.\.)/,/^(?::{1}[^:\n;]+)/,/^(?::{3})/,/^(?:-)/,/^(?:\.)/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:\w+)/,/^(?:[!"#$%&'*+,-.`?\\/])/,/^(?:[0-9]+)/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\s)/,/^(?:$)/],conditions:{arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callback_args:{rules:[37,38],inclusive:!1},callback_name:{rules:[34,35,36],inclusive:!1},href:{rules:[31,32],inclusive:!1},struct:{rules:[12,13,14,15,16],inclusive:!1},generic:{rules:[25,26],inclusive:!1},string:{rules:[28,29],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,17,18,19,20,21,22,23,24,27,30,33,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64],inclusive:!0}}};function N(){this.yy={}}return O.lexer=D,N.prototype=O,O.Parser=N,new N}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e){var n,r,i=t.exports={};function a(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function s(t){if(n===setTimeout)return setTimeout(t,0);if((n===a||!n)&&setTimeout)return n=setTimeout,setTimeout(t,0);try{return n(t,0)}catch(e){try{return n.call(null,t,0)}catch(e){return n.call(this,t,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:a}catch(t){n=a}try{r="function"==typeof clearTimeout?clearTimeout:o}catch(t){r=o}}();var c,u=[],l=!1,h=-1;function f(){l&&c&&(l=!1,c.length?u=c.concat(u):h=-1,u.length&&d())}function d(){if(!l){var t=s(f);l=!0;for(var e=u.length;e;){for(c=u,u=[];++h<e;)c&&c[h].run();h=-1,e=u.length}c=null,l=!1,function(t){if(r===clearTimeout)return clearTimeout(t);if((r===o||!r)&&clearTimeout)return r=clearTimeout,clearTimeout(t);try{r(t)}catch(e){try{return r.call(null,t)}catch(e){return r.call(this,t)}}}(t)}}function p(t,e){this.fun=t,this.array=e}function g(){}i.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var n=1;n<arguments.length;n++)e[n-1]=arguments[n];u.push(new p(t,e)),1!==u.length||l||s(d)},p.prototype.run=function(){this.fun.apply(null,this.array)},i.title="browser",i.browser=!0,i.env={},i.argv=[],i.version="",i.versions={},i.on=g,i.addListener=g,i.once=g,i.off=g,i.removeListener=g,i.removeAllListeners=g,i.emit=g,i.prependListener=g,i.prependOnceListener=g,i.listeners=function(t){return[]},i.binding=function(t){throw new Error("process.binding is not supported")},i.cwd=function(){return"/"},i.chdir=function(t){throw new Error("process.chdir is not supported")},i.umask=function(){return 0}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(75),i=n(99),a=n(179),o=n(180),s=n(181),c={format:{keyword:a.default,hex:i.default,rgb:o.default,rgba:o.default,hsl:s.default,hsla:s.default},parse:function(t){if("string"!=typeof t)return t;var e=i.default.parse(t)||o.default.parse(t)||s.default.parse(t)||a.default.parse(t);if(e)return e;throw new Error('Unsupported color format: "'+t+'"')},stringify:function(t){return!t.changed&&t.color?t.color:t.type.is(r.TYPE.HSL)||void 0===t.data.r?s.default.stringify(t):t.a<1||!Number.isInteger(t.r)||!Number.isInteger(t.g)||!Number.isInteger(t.b)?o.default.stringify(t):i.default.stringify(t)}};e.default=c},function(t,e,n){var r=n(109),i="object"==typeof self&&self&&self.Object===Object&&self,a=r||i||Function("return this")();t.exports=a},function(t,e,n){var r;try{r=n(3)}catch(t){}r||(r=window.graphlib),t.exports=r},function(t,e,n){t.exports={graphlib:n(17),layout:n(312),debug:n(366),util:{time:n(8).time,notime:n(8).notime},version:n(367)}},function(t,e){},function(t,e,n){(function(t){function n(t,e){for(var n=0,r=t.length-1;r>=0;r--){var i=t[r];"."===i?t.splice(r,1):".."===i?(t.splice(r,1),n++):n&&(t.splice(r,1),n--)}if(e)for(;n--;n)t.unshift("..");return t}function r(t,e){if(t.filter)return t.filter(e);for(var n=[],r=0;r<t.length;r++)e(t[r],r,t)&&n.push(t[r]);return n}e.resolve=function(){for(var e="",i=!1,a=arguments.length-1;a>=-1&&!i;a--){var o=a>=0?arguments[a]:t.cwd();if("string"!=typeof o)throw new TypeError("Arguments to path.resolve must be strings");o&&(e=o+"/"+e,i="/"===o.charAt(0))}return(i?"/":"")+(e=n(r(e.split("/"),(function(t){return!!t})),!i).join("/"))||"."},e.normalize=function(t){var a=e.isAbsolute(t),o="/"===i(t,-1);return(t=n(r(t.split("/"),(function(t){return!!t})),!a).join("/"))||a||(t="."),t&&o&&(t+="/"),(a?"/":"")+t},e.isAbsolute=function(t){return"/"===t.charAt(0)},e.join=function(){var t=Array.prototype.slice.call(arguments,0);return e.normalize(r(t,(function(t,e){if("string"!=typeof t)throw new TypeError("Arguments to path.join must be strings");return t})).join("/"))},e.relative=function(t,n){function r(t){for(var e=0;e<t.length&&""===t[e];e++);for(var n=t.length-1;n>=0&&""===t[n];n--);return e>n?[]:t.slice(e,n-e+1)}t=e.resolve(t).substr(1),n=e.resolve(n).substr(1);for(var i=r(t.split("/")),a=r(n.split("/")),o=Math.min(i.length,a.length),s=o,c=0;c<o;c++)if(i[c]!==a[c]){s=c;break}var u=[];for(c=s;c<i.length;c++)u.push("..");return(u=u.concat(a.slice(s))).join("/")},e.sep="/",e.delimiter=":",e.dirname=function(t){if("string"!=typeof t&&(t+=""),0===t.length)return".";for(var e=t.charCodeAt(0),n=47===e,r=-1,i=!0,a=t.length-1;a>=1;--a)if(47===(e=t.charCodeAt(a))){if(!i){r=a;break}}else i=!1;return-1===r?n?"/":".":n&&1===r?"/":t.slice(0,r)},e.basename=function(t,e){var n=function(t){"string"!=typeof t&&(t+="");var e,n=0,r=-1,i=!0;for(e=t.length-1;e>=0;--e)if(47===t.charCodeAt(e)){if(!i){n=e+1;break}}else-1===r&&(i=!1,r=e+1);return-1===r?"":t.slice(n,r)}(t);return e&&n.substr(-1*e.length)===e&&(n=n.substr(0,n.length-e.length)),n},e.extname=function(t){"string"!=typeof t&&(t+="");for(var e=-1,n=0,r=-1,i=!0,a=0,o=t.length-1;o>=0;--o){var s=t.charCodeAt(o);if(47!==s)-1===r&&(i=!1,r=o+1),46===s?-1===e?e=o:1!==a&&(a=1):-1!==e&&(a=-1);else if(!i){n=o+1;break}}return-1===e||-1===r||0===a||1===a&&e===r-1&&e===n+1?"":t.slice(e,r)};var i="b"==="ab".substr(-1)?function(t,e,n){return t.substr(e,n)}:function(t,e,n){return e<0&&(e=t.length+e),t.substr(e,n)}}).call(this,n(14))},function(t,e){t.exports=function(t){return null!=t&&"object"==typeof t}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,3],r=[1,5],i=[1,7],a=[2,5],o=[1,15],s=[1,17],c=[1,19],u=[1,20],l=[1,21],h=[1,22],f=[1,28],d=[1,23],p=[1,24],g=[1,25],y=[1,26],v=[1,29],m=[1,32],b=[1,4,5,14,15,17,19,20,22,23,24,25,26,36,39],x=[1,4,5,12,13,14,15,17,19,20,22,23,24,25,26,36,39],_=[1,4,5,7,14,15,17,19,20,22,23,24,25,26,36,39],k=[4,5,14,15,17,19,20,22,23,24,25,26,36,39],w={trace:function(){},yy:{},symbols_:{error:2,start:3,SPACE:4,NL:5,directive:6,SD:7,document:8,line:9,statement:10,idStatement:11,DESCR:12,"--\x3e":13,HIDE_EMPTY:14,scale:15,WIDTH:16,COMPOSIT_STATE:17,STRUCT_START:18,STRUCT_STOP:19,STATE_DESCR:20,AS:21,ID:22,FORK:23,JOIN:24,CONCURRENT:25,note:26,notePosition:27,NOTE_TEXT:28,openDirective:29,typeDirective:30,closeDirective:31,":":32,argDirective:33,eol:34,";":35,EDGE_STATE:36,left_of:37,right_of:38,open_directive:39,type_directive:40,arg_directive:41,close_directive:42,$accept:0,$end:1},terminals_:{2:"error",4:"SPACE",5:"NL",7:"SD",12:"DESCR",13:"--\x3e",14:"HIDE_EMPTY",15:"scale",16:"WIDTH",17:"COMPOSIT_STATE",18:"STRUCT_START",19:"STRUCT_STOP",20:"STATE_DESCR",21:"AS",22:"ID",23:"FORK",24:"JOIN",25:"CONCURRENT",26:"note",28:"NOTE_TEXT",32:":",35:";",36:"EDGE_STATE",37:"left_of",38:"right_of",39:"open_directive",40:"type_directive",41:"arg_directive",42:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[3,2],[8,0],[8,2],[9,2],[9,1],[9,1],[10,1],[10,2],[10,3],[10,4],[10,1],[10,2],[10,1],[10,4],[10,3],[10,6],[10,1],[10,1],[10,1],[10,4],[10,4],[10,1],[6,3],[6,5],[34,1],[34,1],[11,1],[11,1],[27,1],[27,1],[29,1],[30,1],[33,1],[31,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 4:return r.setRootDoc(a[s]),a[s];case 5:this.$=[];break;case 6:"nl"!=a[s]&&(a[s-1].push(a[s]),this.$=a[s-1]);break;case 7:case 8:this.$=a[s];break;case 9:this.$="nl";break;case 10:this.$={stmt:"state",id:a[s],type:"default",description:""};break;case 11:this.$={stmt:"state",id:a[s-1],type:"default",description:r.trimColon(a[s])};break;case 12:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-2],type:"default",description:""},state2:{stmt:"state",id:a[s],type:"default",description:""}};break;case 13:this.$={stmt:"relation",state1:{stmt:"state",id:a[s-3],type:"default",description:""},state2:{stmt:"state",id:a[s-1],type:"default",description:""},description:a[s].substr(1).trim()};break;case 17:this.$={stmt:"state",id:a[s-3],type:"default",description:"",doc:a[s-1]};break;case 18:var c=a[s],u=a[s-2].trim();if(a[s].match(":")){var l=a[s].split(":");c=l[0],u=[u,l[1]]}this.$={stmt:"state",id:c,type:"default",description:u};break;case 19:this.$={stmt:"state",id:a[s-3],type:"default",description:a[s-5],doc:a[s-1]};break;case 20:this.$={stmt:"state",id:a[s],type:"fork"};break;case 21:this.$={stmt:"state",id:a[s],type:"join"};break;case 22:this.$={stmt:"state",id:r.getDividerId(),type:"divider"};break;case 23:this.$={stmt:"state",id:a[s-1].trim(),note:{position:a[s-2].trim(),text:a[s].trim()}};break;case 30:case 31:this.$=a[s];break;case 34:r.parseDirective("%%{","open_directive");break;case 35:r.parseDirective(a[s],"type_directive");break;case 36:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 37:r.parseDirective("}%%","close_directive","state")}},table:[{3:1,4:e,5:n,6:4,7:r,29:6,39:i},{1:[3]},{3:8,4:e,5:n,6:4,7:r,29:6,39:i},{3:9,4:e,5:n,6:4,7:r,29:6,39:i},{3:10,4:e,5:n,6:4,7:r,29:6,39:i},t([1,4,5,14,15,17,20,22,23,24,25,26,36,39],a,{8:11}),{30:12,40:[1,13]},{40:[2,34]},{1:[2,1]},{1:[2,2]},{1:[2,3]},{1:[2,4],4:o,5:s,6:27,9:14,10:16,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},{31:30,32:[1,31],42:m},t([32,42],[2,35]),t(b,[2,6]),{6:27,10:33,11:18,14:c,15:u,17:l,20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},t(b,[2,8]),t(b,[2,9]),t(b,[2,10],{12:[1,34],13:[1,35]}),t(b,[2,14]),{16:[1,36]},t(b,[2,16],{18:[1,37]}),{21:[1,38]},t(b,[2,20]),t(b,[2,21]),t(b,[2,22]),{27:39,28:[1,40],37:[1,41],38:[1,42]},t(b,[2,25]),t(x,[2,30]),t(x,[2,31]),t(_,[2,26]),{33:43,41:[1,44]},t(_,[2,37]),t(b,[2,7]),t(b,[2,11]),{11:45,22:f,36:v},t(b,[2,15]),t(k,a,{8:46}),{22:[1,47]},{22:[1,48]},{21:[1,49]},{22:[2,32]},{22:[2,33]},{31:50,42:m},{42:[2,36]},t(b,[2,12],{12:[1,51]}),{4:o,5:s,6:27,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,52],20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},t(b,[2,18],{18:[1,53]}),{28:[1,54]},{22:[1,55]},t(_,[2,27]),t(b,[2,13]),t(b,[2,17]),t(k,a,{8:56}),t(b,[2,23]),t(b,[2,24]),{4:o,5:s,6:27,9:14,10:16,11:18,14:c,15:u,17:l,19:[1,57],20:h,22:f,23:d,24:p,25:g,26:y,29:6,36:v,39:i},t(b,[2,19])],defaultActions:{7:[2,34],8:[2,1],9:[2,2],10:[2,3],41:[2,32],42:[2,33],44:[2,36]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},E={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),39;case 1:return this.begin("type_directive"),40;case 2:return this.popState(),this.begin("arg_directive"),32;case 3:return this.popState(),this.popState(),42;case 4:return 41;case 5:break;case 6:console.log("Crap after close");break;case 7:return 5;case 8:case 9:case 10:case 11:break;case 12:return this.pushState("SCALE"),15;case 13:return 16;case 14:this.popState();break;case 15:this.pushState("STATE");break;case 16:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),23;case 17:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),24;case 18:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),23;case 19:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),24;case 20:this.begin("STATE_STRING");break;case 21:return this.popState(),this.pushState("STATE_ID"),"AS";case 22:return this.popState(),"ID";case 23:this.popState();break;case 24:return"STATE_DESCR";case 25:return 17;case 26:this.popState();break;case 27:return this.popState(),this.pushState("struct"),18;case 28:return this.popState(),19;case 29:break;case 30:return this.begin("NOTE"),26;case 31:return this.popState(),this.pushState("NOTE_ID"),37;case 32:return this.popState(),this.pushState("NOTE_ID"),38;case 33:this.popState(),this.pushState("FLOATING_NOTE");break;case 34:return this.popState(),this.pushState("FLOATING_NOTE_ID"),"AS";case 35:break;case 36:return"NOTE_TEXT";case 37:return this.popState(),"ID";case 38:return this.popState(),this.pushState("NOTE_TEXT"),22;case 39:return this.popState(),e.yytext=e.yytext.substr(2).trim(),28;case 40:return this.popState(),e.yytext=e.yytext.slice(0,-8).trim(),28;case 41:case 42:return 7;case 43:return 14;case 44:return 36;case 45:return 22;case 46:return e.yytext=e.yytext.trim(),12;case 47:return 13;case 48:return 25;case 49:return 5;case 50:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:[\s]+)/i,/^(?:((?!\n)\s)+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:scale\s+)/i,/^(?:\d+)/i,/^(?:\s+width\b)/i,/^(?:state\s+)/i,/^(?:.*<<fork>>)/i,/^(?:.*<<join>>)/i,/^(?:.*\[\[fork\]\])/i,/^(?:.*\[\[join\]\])/i,/^(?:["])/i,/^(?:\s*as\s+)/i,/^(?:[^\n\{]*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n\s\{]+)/i,/^(?:\n)/i,/^(?:\{)/i,/^(?:\})/i,/^(?:[\n])/i,/^(?:note\s+)/i,/^(?:left of\b)/i,/^(?:right of\b)/i,/^(?:")/i,/^(?:\s*as\s*)/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[^\n]*)/i,/^(?:\s*[^:\n\s\-]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:[\s\S]*?end note\b)/i,/^(?:stateDiagram\s+)/i,/^(?:stateDiagram-v2\s+)/i,/^(?:hide empty description\b)/i,/^(?:\[\*\])/i,/^(?:[^:\n\s\-\{]+)/i,/^(?:\s*:[^:\n;]+)/i,/^(?:-->)/i,/^(?:--)/i,/^(?:$)/i,/^(?:.)/i],conditions:{LINE:{rules:[9,10],inclusive:!1},close_directive:{rules:[9,10],inclusive:!1},arg_directive:{rules:[3,4,9,10],inclusive:!1},type_directive:{rules:[2,3,9,10],inclusive:!1},open_directive:{rules:[1,9,10],inclusive:!1},struct:{rules:[9,10,15,28,29,30,44,45,46,47,48],inclusive:!1},FLOATING_NOTE_ID:{rules:[37],inclusive:!1},FLOATING_NOTE:{rules:[34,35,36],inclusive:!1},NOTE_TEXT:{rules:[39,40],inclusive:!1},NOTE_ID:{rules:[38],inclusive:!1},NOTE:{rules:[31,32,33],inclusive:!1},SCALE:{rules:[13,14],inclusive:!1},ALIAS:{rules:[],inclusive:!1},STATE_ID:{rules:[22],inclusive:!1},STATE_STRING:{rules:[23,24],inclusive:!1},FORK_STATE:{rules:[],inclusive:!1},STATE:{rules:[9,10,16,17,18,19,20,21,25,26,27],inclusive:!1},ID:{rules:[9,10],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,10,11,12,15,27,30,41,42,43,44,45,46,47,49,50],inclusive:!0}}};function T(){this.yy={}}return w.lexer=E,T.prototype=w,w.Parser=T,new T}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t){t.exports=function(){"use strict";var e,r;function i(){return e.apply(null,arguments)}function a(t){return t instanceof Array||"[object Array]"===Object.prototype.toString.call(t)}function o(t){return null!=t&&"[object Object]"===Object.prototype.toString.call(t)}function s(t){return void 0===t}function c(t){return"number"==typeof t||"[object Number]"===Object.prototype.toString.call(t)}function u(t){return t instanceof Date||"[object Date]"===Object.prototype.toString.call(t)}function l(t,e){var n,r=[];for(n=0;n<t.length;++n)r.push(e(t[n],n));return r}function h(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function f(t,e){for(var n in e)h(e,n)&&(t[n]=e[n]);return h(e,"toString")&&(t.toString=e.toString),h(e,"valueOf")&&(t.valueOf=e.valueOf),t}function d(t,e,n,r){return be(t,e,n,r,!0).utc()}function p(t){return null==t._pf&&(t._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],meridiem:null,rfc2822:!1,weekdayMismatch:!1}),t._pf}function g(t){if(null==t._isValid){var e=p(t),n=r.call(e.parsedDateParts,(function(t){return null!=t})),i=!isNaN(t._d.getTime())&&e.overflow<0&&!e.empty&&!e.invalidMonth&&!e.invalidWeekday&&!e.weekdayMismatch&&!e.nullInput&&!e.invalidFormat&&!e.userInvalidated&&(!e.meridiem||e.meridiem&&n);if(t._strict&&(i=i&&0===e.charsLeftOver&&0===e.unusedTokens.length&&void 0===e.bigHour),null!=Object.isFrozen&&Object.isFrozen(t))return i;t._isValid=i}return t._isValid}function y(t){var e=d(NaN);return null!=t?f(p(e),t):p(e).userInvalidated=!0,e}r=Array.prototype.some?Array.prototype.some:function(t){for(var e=Object(this),n=e.length>>>0,r=0;r<n;r++)if(r in e&&t.call(this,e[r],r,e))return!0;return!1};var v=i.momentProperties=[];function m(t,e){var n,r,i;if(s(e._isAMomentObject)||(t._isAMomentObject=e._isAMomentObject),s(e._i)||(t._i=e._i),s(e._f)||(t._f=e._f),s(e._l)||(t._l=e._l),s(e._strict)||(t._strict=e._strict),s(e._tzm)||(t._tzm=e._tzm),s(e._isUTC)||(t._isUTC=e._isUTC),s(e._offset)||(t._offset=e._offset),s(e._pf)||(t._pf=p(e)),s(e._locale)||(t._locale=e._locale),0<v.length)for(n=0;n<v.length;n++)s(i=e[r=v[n]])||(t[r]=i);return t}var b=!1;function x(t){m(this,t),this._d=new Date(null!=t._d?t._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===b&&(b=!0,i.updateOffset(this),b=!1)}function _(t){return t instanceof x||null!=t&&null!=t._isAMomentObject}function k(t){return t<0?Math.ceil(t)||0:Math.floor(t)}function w(t){var e=+t,n=0;return 0!==e&&isFinite(e)&&(n=k(e)),n}function E(t,e,n){var r,i=Math.min(t.length,e.length),a=Math.abs(t.length-e.length),o=0;for(r=0;r<i;r++)(n&&t[r]!==e[r]||!n&&w(t[r])!==w(e[r]))&&o++;return o+a}function T(t){!1===i.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+t)}function C(t,e){var n=!0;return f((function(){if(null!=i.deprecationHandler&&i.deprecationHandler(null,t),n){for(var r,a=[],o=0;o<arguments.length;o++){if(r="","object"==typeof arguments[o]){for(var s in r+="\n["+o+"] ",arguments[0])r+=s+": "+arguments[0][s]+", ";r=r.slice(0,-2)}else r=arguments[o];a.push(r)}T(t+"\nArguments: "+Array.prototype.slice.call(a).join("")+"\n"+(new Error).stack),n=!1}return e.apply(this,arguments)}),e)}var A,S={};function M(t,e){null!=i.deprecationHandler&&i.deprecationHandler(t,e),S[t]||(T(e),S[t]=!0)}function O(t){return t instanceof Function||"[object Function]"===Object.prototype.toString.call(t)}function D(t,e){var n,r=f({},t);for(n in e)h(e,n)&&(o(t[n])&&o(e[n])?(r[n]={},f(r[n],t[n]),f(r[n],e[n])):null!=e[n]?r[n]=e[n]:delete r[n]);for(n in t)h(t,n)&&!h(e,n)&&o(t[n])&&(r[n]=f({},r[n]));return r}function N(t){null!=t&&this.set(t)}i.suppressDeprecationWarnings=!1,i.deprecationHandler=null,A=Object.keys?Object.keys:function(t){var e,n=[];for(e in t)h(t,e)&&n.push(e);return n};var B={};function L(t,e){var n=t.toLowerCase();B[n]=B[n+"s"]=B[e]=t}function P(t){return"string"==typeof t?B[t]||B[t.toLowerCase()]:void 0}function I(t){var e,n,r={};for(n in t)h(t,n)&&(e=P(n))&&(r[e]=t[n]);return r}var F={};function j(t,e){F[t]=e}function R(t,e,n){var r=""+Math.abs(t),i=e-r.length;return(0<=t?n?"+":"":"-")+Math.pow(10,Math.max(0,i)).toString().substr(1)+r}var Y=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,z=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,U={},$={};function W(t,e,n,r){var i=r;"string"==typeof r&&(i=function(){return this[r]()}),t&&($[t]=i),e&&($[e[0]]=function(){return R(i.apply(this,arguments),e[1],e[2])}),n&&($[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),t)})}function H(t,e){return t.isValid()?(e=V(e,t.localeData()),U[e]=U[e]||function(t){var e,n,r,i=t.match(Y);for(e=0,n=i.length;e<n;e++)$[i[e]]?i[e]=$[i[e]]:i[e]=(r=i[e]).match(/\[[\s\S]/)?r.replace(/^\[|\]$/g,""):r.replace(/\\/g,"");return function(e){var r,a="";for(r=0;r<n;r++)a+=O(i[r])?i[r].call(e,t):i[r];return a}}(e),U[e](t)):t.localeData().invalidDate()}function V(t,e){var n=5;function r(t){return e.longDateFormat(t)||t}for(z.lastIndex=0;0<=n&&z.test(t);)t=t.replace(z,r),z.lastIndex=0,n-=1;return t}var G=/\d/,q=/\d\d/,X=/\d{3}/,Z=/\d{4}/,J=/[+-]?\d{6}/,K=/\d\d?/,Q=/\d\d\d\d?/,tt=/\d\d\d\d\d\d?/,et=/\d{1,3}/,nt=/\d{1,4}/,rt=/[+-]?\d{1,6}/,it=/\d+/,at=/[+-]?\d+/,ot=/Z|[+-]\d\d:?\d\d/gi,st=/Z|[+-]\d\d(?::?\d\d)?/gi,ct=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,ut={};function lt(t,e,n){ut[t]=O(e)?e:function(t,r){return t&&n?n:e}}function ht(t,e){return h(ut,t)?ut[t](e._strict,e._locale):new RegExp(ft(t.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,(function(t,e,n,r,i){return e||n||r||i}))))}function ft(t){return t.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var dt={};function pt(t,e){var n,r=e;for("string"==typeof t&&(t=[t]),c(e)&&(r=function(t,n){n[e]=w(t)}),n=0;n<t.length;n++)dt[t[n]]=r}function gt(t,e){pt(t,(function(t,n,r,i){r._w=r._w||{},e(t,r._w,r,i)}))}function yt(t){return vt(t)?366:365}function vt(t){return t%4==0&&t%100!=0||t%400==0}W("Y",0,0,(function(){var t=this.year();return t<=9999?""+t:"+"+t})),W(0,["YY",2],0,(function(){return this.year()%100})),W(0,["YYYY",4],0,"year"),W(0,["YYYYY",5],0,"year"),W(0,["YYYYYY",6,!0],0,"year"),L("year","y"),j("year",1),lt("Y",at),lt("YY",K,q),lt("YYYY",nt,Z),lt("YYYYY",rt,J),lt("YYYYYY",rt,J),pt(["YYYYY","YYYYYY"],0),pt("YYYY",(function(t,e){e[0]=2===t.length?i.parseTwoDigitYear(t):w(t)})),pt("YY",(function(t,e){e[0]=i.parseTwoDigitYear(t)})),pt("Y",(function(t,e){e[0]=parseInt(t,10)})),i.parseTwoDigitYear=function(t){return w(t)+(68<w(t)?1900:2e3)};var mt,bt=xt("FullYear",!0);function xt(t,e){return function(n){return null!=n?(kt(this,t,n),i.updateOffset(this,e),this):_t(this,t)}}function _t(t,e){return t.isValid()?t._d["get"+(t._isUTC?"UTC":"")+e]():NaN}function kt(t,e,n){t.isValid()&&!isNaN(n)&&("FullYear"===e&&vt(t.year())&&1===t.month()&&29===t.date()?t._d["set"+(t._isUTC?"UTC":"")+e](n,t.month(),wt(n,t.month())):t._d["set"+(t._isUTC?"UTC":"")+e](n))}function wt(t,e){if(isNaN(t)||isNaN(e))return NaN;var n=(e%12+12)%12;return t+=(e-n)/12,1===n?vt(t)?29:28:31-n%7%2}mt=Array.prototype.indexOf?Array.prototype.indexOf:function(t){var e;for(e=0;e<this.length;++e)if(this[e]===t)return e;return-1},W("M",["MM",2],"Mo",(function(){return this.month()+1})),W("MMM",0,0,(function(t){return this.localeData().monthsShort(this,t)})),W("MMMM",0,0,(function(t){return this.localeData().months(this,t)})),L("month","M"),j("month",8),lt("M",K),lt("MM",K,q),lt("MMM",(function(t,e){return e.monthsShortRegex(t)})),lt("MMMM",(function(t,e){return e.monthsRegex(t)})),pt(["M","MM"],(function(t,e){e[1]=w(t)-1})),pt(["MMM","MMMM"],(function(t,e,n,r){var i=n._locale.monthsParse(t,r,n._strict);null!=i?e[1]=i:p(n).invalidMonth=t}));var Et=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Tt="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Ct="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_");function At(t,e){var n;if(!t.isValid())return t;if("string"==typeof e)if(/^\d+$/.test(e))e=w(e);else if(!c(e=t.localeData().monthsParse(e)))return t;return n=Math.min(t.date(),wt(t.year(),e)),t._d["set"+(t._isUTC?"UTC":"")+"Month"](e,n),t}function St(t){return null!=t?(At(this,t),i.updateOffset(this,!0),this):_t(this,"Month")}var Mt=ct,Ot=ct;function Dt(){function t(t,e){return e.length-t.length}var e,n,r=[],i=[],a=[];for(e=0;e<12;e++)n=d([2e3,e]),r.push(this.monthsShort(n,"")),i.push(this.months(n,"")),a.push(this.months(n,"")),a.push(this.monthsShort(n,""));for(r.sort(t),i.sort(t),a.sort(t),e=0;e<12;e++)r[e]=ft(r[e]),i[e]=ft(i[e]);for(e=0;e<24;e++)a[e]=ft(a[e]);this._monthsRegex=new RegExp("^("+a.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+r.join("|")+")","i")}function Nt(t){var e;if(t<100&&0<=t){var n=Array.prototype.slice.call(arguments);n[0]=t+400,e=new Date(Date.UTC.apply(null,n)),isFinite(e.getUTCFullYear())&&e.setUTCFullYear(t)}else e=new Date(Date.UTC.apply(null,arguments));return e}function Bt(t,e,n){var r=7+e-n;return-(7+Nt(t,0,r).getUTCDay()-e)%7+r-1}function Lt(t,e,n,r,i){var a,o,s=1+7*(e-1)+(7+n-r)%7+Bt(t,r,i);return o=s<=0?yt(a=t-1)+s:s>yt(t)?(a=t+1,s-yt(t)):(a=t,s),{year:a,dayOfYear:o}}function Pt(t,e,n){var r,i,a=Bt(t.year(),e,n),o=Math.floor((t.dayOfYear()-a-1)/7)+1;return o<1?r=o+It(i=t.year()-1,e,n):o>It(t.year(),e,n)?(r=o-It(t.year(),e,n),i=t.year()+1):(i=t.year(),r=o),{week:r,year:i}}function It(t,e,n){var r=Bt(t,e,n),i=Bt(t+1,e,n);return(yt(t)-r+i)/7}function Ft(t,e){return t.slice(e,7).concat(t.slice(0,e))}W("w",["ww",2],"wo","week"),W("W",["WW",2],"Wo","isoWeek"),L("week","w"),L("isoWeek","W"),j("week",5),j("isoWeek",5),lt("w",K),lt("ww",K,q),lt("W",K),lt("WW",K,q),gt(["w","ww","W","WW"],(function(t,e,n,r){e[r.substr(0,1)]=w(t)})),W("d",0,"do","day"),W("dd",0,0,(function(t){return this.localeData().weekdaysMin(this,t)})),W("ddd",0,0,(function(t){return this.localeData().weekdaysShort(this,t)})),W("dddd",0,0,(function(t){return this.localeData().weekdays(this,t)})),W("e",0,0,"weekday"),W("E",0,0,"isoWeekday"),L("day","d"),L("weekday","e"),L("isoWeekday","E"),j("day",11),j("weekday",11),j("isoWeekday",11),lt("d",K),lt("e",K),lt("E",K),lt("dd",(function(t,e){return e.weekdaysMinRegex(t)})),lt("ddd",(function(t,e){return e.weekdaysShortRegex(t)})),lt("dddd",(function(t,e){return e.weekdaysRegex(t)})),gt(["dd","ddd","dddd"],(function(t,e,n,r){var i=n._locale.weekdaysParse(t,r,n._strict);null!=i?e.d=i:p(n).invalidWeekday=t})),gt(["d","e","E"],(function(t,e,n,r){e[r]=w(t)}));var jt="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Rt="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Yt="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),zt=ct,Ut=ct,$t=ct;function Wt(){function t(t,e){return e.length-t.length}var e,n,r,i,a,o=[],s=[],c=[],u=[];for(e=0;e<7;e++)n=d([2e3,1]).day(e),r=this.weekdaysMin(n,""),i=this.weekdaysShort(n,""),a=this.weekdays(n,""),o.push(r),s.push(i),c.push(a),u.push(r),u.push(i),u.push(a);for(o.sort(t),s.sort(t),c.sort(t),u.sort(t),e=0;e<7;e++)s[e]=ft(s[e]),c[e]=ft(c[e]),u[e]=ft(u[e]);this._weekdaysRegex=new RegExp("^("+u.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+c.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+s.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+o.join("|")+")","i")}function Ht(){return this.hours()%12||12}function Vt(t,e){W(t,0,0,(function(){return this.localeData().meridiem(this.hours(),this.minutes(),e)}))}function Gt(t,e){return e._meridiemParse}W("H",["HH",2],0,"hour"),W("h",["hh",2],0,Ht),W("k",["kk",2],0,(function(){return this.hours()||24})),W("hmm",0,0,(function(){return""+Ht.apply(this)+R(this.minutes(),2)})),W("hmmss",0,0,(function(){return""+Ht.apply(this)+R(this.minutes(),2)+R(this.seconds(),2)})),W("Hmm",0,0,(function(){return""+this.hours()+R(this.minutes(),2)})),W("Hmmss",0,0,(function(){return""+this.hours()+R(this.minutes(),2)+R(this.seconds(),2)})),Vt("a",!0),Vt("A",!1),L("hour","h"),j("hour",13),lt("a",Gt),lt("A",Gt),lt("H",K),lt("h",K),lt("k",K),lt("HH",K,q),lt("hh",K,q),lt("kk",K,q),lt("hmm",Q),lt("hmmss",tt),lt("Hmm",Q),lt("Hmmss",tt),pt(["H","HH"],3),pt(["k","kk"],(function(t,e,n){var r=w(t);e[3]=24===r?0:r})),pt(["a","A"],(function(t,e,n){n._isPm=n._locale.isPM(t),n._meridiem=t})),pt(["h","hh"],(function(t,e,n){e[3]=w(t),p(n).bigHour=!0})),pt("hmm",(function(t,e,n){var r=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r)),p(n).bigHour=!0})),pt("hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r,2)),e[5]=w(t.substr(i)),p(n).bigHour=!0})),pt("Hmm",(function(t,e,n){var r=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r))})),pt("Hmmss",(function(t,e,n){var r=t.length-4,i=t.length-2;e[3]=w(t.substr(0,r)),e[4]=w(t.substr(r,2)),e[5]=w(t.substr(i))}));var qt,Xt=xt("Hours",!0),Zt={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Tt,monthsShort:Ct,week:{dow:0,doy:6},weekdays:jt,weekdaysMin:Yt,weekdaysShort:Rt,meridiemParse:/[ap]\.?m?\.?/i},Jt={},Kt={};function Qt(t){return t?t.toLowerCase().replace("_","-"):t}function te(e){var r=null;if(!Jt[e]&&void 0!==t&&t&&t.exports)try{r=qt._abbr,n(171)("./"+e),ee(r)}catch(e){}return Jt[e]}function ee(t,e){var n;return t&&((n=s(e)?re(t):ne(t,e))?qt=n:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+t+" not found. Did you forget to load it?")),qt._abbr}function ne(t,e){if(null===e)return delete Jt[t],null;var n,r=Zt;if(e.abbr=t,null!=Jt[t])M("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),r=Jt[t]._config;else if(null!=e.parentLocale)if(null!=Jt[e.parentLocale])r=Jt[e.parentLocale]._config;else{if(null==(n=te(e.parentLocale)))return Kt[e.parentLocale]||(Kt[e.parentLocale]=[]),Kt[e.parentLocale].push({name:t,config:e}),null;r=n._config}return Jt[t]=new N(D(r,e)),Kt[t]&&Kt[t].forEach((function(t){ne(t.name,t.config)})),ee(t),Jt[t]}function re(t){var e;if(t&&t._locale&&t._locale._abbr&&(t=t._locale._abbr),!t)return qt;if(!a(t)){if(e=te(t))return e;t=[t]}return function(t){for(var e,n,r,i,a=0;a<t.length;){for(e=(i=Qt(t[a]).split("-")).length,n=(n=Qt(t[a+1]))?n.split("-"):null;0<e;){if(r=te(i.slice(0,e).join("-")))return r;if(n&&n.length>=e&&E(i,n,!0)>=e-1)break;e--}a++}return qt}(t)}function ie(t){var e,n=t._a;return n&&-2===p(t).overflow&&(e=n[1]<0||11<n[1]?1:n[2]<1||n[2]>wt(n[0],n[1])?2:n[3]<0||24<n[3]||24===n[3]&&(0!==n[4]||0!==n[5]||0!==n[6])?3:n[4]<0||59<n[4]?4:n[5]<0||59<n[5]?5:n[6]<0||999<n[6]?6:-1,p(t)._overflowDayOfYear&&(e<0||2<e)&&(e=2),p(t)._overflowWeeks&&-1===e&&(e=7),p(t)._overflowWeekday&&-1===e&&(e=8),p(t).overflow=e),t}function ae(t,e,n){return null!=t?t:null!=e?e:n}function oe(t){var e,n,r,a,o,s=[];if(!t._d){var c,u;for(c=t,u=new Date(i.now()),r=c._useUTC?[u.getUTCFullYear(),u.getUTCMonth(),u.getUTCDate()]:[u.getFullYear(),u.getMonth(),u.getDate()],t._w&&null==t._a[2]&&null==t._a[1]&&function(t){var e,n,r,i,a,o,s,c;if(null!=(e=t._w).GG||null!=e.W||null!=e.E)a=1,o=4,n=ae(e.GG,t._a[0],Pt(xe(),1,4).year),r=ae(e.W,1),((i=ae(e.E,1))<1||7<i)&&(c=!0);else{a=t._locale._week.dow,o=t._locale._week.doy;var u=Pt(xe(),a,o);n=ae(e.gg,t._a[0],u.year),r=ae(e.w,u.week),null!=e.d?((i=e.d)<0||6<i)&&(c=!0):null!=e.e?(i=e.e+a,(e.e<0||6<e.e)&&(c=!0)):i=a}r<1||r>It(n,a,o)?p(t)._overflowWeeks=!0:null!=c?p(t)._overflowWeekday=!0:(s=Lt(n,r,i,a,o),t._a[0]=s.year,t._dayOfYear=s.dayOfYear)}(t),null!=t._dayOfYear&&(o=ae(t._a[0],r[0]),(t._dayOfYear>yt(o)||0===t._dayOfYear)&&(p(t)._overflowDayOfYear=!0),n=Nt(o,0,t._dayOfYear),t._a[1]=n.getUTCMonth(),t._a[2]=n.getUTCDate()),e=0;e<3&&null==t._a[e];++e)t._a[e]=s[e]=r[e];for(;e<7;e++)t._a[e]=s[e]=null==t._a[e]?2===e?1:0:t._a[e];24===t._a[3]&&0===t._a[4]&&0===t._a[5]&&0===t._a[6]&&(t._nextDay=!0,t._a[3]=0),t._d=(t._useUTC?Nt:function(t,e,n,r,i,a,o){var s;return t<100&&0<=t?(s=new Date(t+400,e,n,r,i,a,o),isFinite(s.getFullYear())&&s.setFullYear(t)):s=new Date(t,e,n,r,i,a,o),s}).apply(null,s),a=t._useUTC?t._d.getUTCDay():t._d.getDay(),null!=t._tzm&&t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),t._nextDay&&(t._a[3]=24),t._w&&void 0!==t._w.d&&t._w.d!==a&&(p(t).weekdayMismatch=!0)}}var se=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ce=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,ue=/Z|[+-]\d\d(?::?\d\d)?/,le=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/]],he=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],fe=/^\/?Date\((\-?\d+)/i;function de(t){var e,n,r,i,a,o,s=t._i,c=se.exec(s)||ce.exec(s);if(c){for(p(t).iso=!0,e=0,n=le.length;e<n;e++)if(le[e][1].exec(c[1])){i=le[e][0],r=!1!==le[e][2];break}if(null==i)return void(t._isValid=!1);if(c[3]){for(e=0,n=he.length;e<n;e++)if(he[e][1].exec(c[3])){a=(c[2]||" ")+he[e][0];break}if(null==a)return void(t._isValid=!1)}if(!r&&null!=a)return void(t._isValid=!1);if(c[4]){if(!ue.exec(c[4]))return void(t._isValid=!1);o="Z"}t._f=i+(a||"")+(o||""),ve(t)}else t._isValid=!1}var pe=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,ge={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function ye(t){var e,n,r,i=pe.exec(t._i.replace(/\([^)]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));if(i){var a=function(t,e,n,r,i,a){var o=[function(t){var e=parseInt(t,10);return e<=49?2e3+e:e<=999?1900+e:e}(t),Ct.indexOf(e),parseInt(n,10),parseInt(r,10),parseInt(i,10)];return a&&o.push(parseInt(a,10)),o}(i[4],i[3],i[2],i[5],i[6],i[7]);if(n=a,r=t,(e=i[1])&&Rt.indexOf(e)!==new Date(n[0],n[1],n[2]).getDay()&&(p(r).weekdayMismatch=!0,!(r._isValid=!1)))return;t._a=a,t._tzm=function(t,e,n){if(t)return ge[t];if(e)return 0;var r=parseInt(n,10),i=r%100;return(r-i)/100*60+i}(i[8],i[9],i[10]),t._d=Nt.apply(null,t._a),t._d.setUTCMinutes(t._d.getUTCMinutes()-t._tzm),p(t).rfc2822=!0}else t._isValid=!1}function ve(t){if(t._f!==i.ISO_8601)if(t._f!==i.RFC_2822){t._a=[],p(t).empty=!0;var e,n,r,a,o,s,c,u,l=""+t._i,f=l.length,d=0;for(r=V(t._f,t._locale).match(Y)||[],e=0;e<r.length;e++)a=r[e],(n=(l.match(ht(a,t))||[])[0])&&(0<(o=l.substr(0,l.indexOf(n))).length&&p(t).unusedInput.push(o),l=l.slice(l.indexOf(n)+n.length),d+=n.length),$[a]?(n?p(t).empty=!1:p(t).unusedTokens.push(a),s=a,u=t,null!=(c=n)&&h(dt,s)&&dt[s](c,u._a,u,s)):t._strict&&!n&&p(t).unusedTokens.push(a);p(t).charsLeftOver=f-d,0<l.length&&p(t).unusedInput.push(l),t._a[3]<=12&&!0===p(t).bigHour&&0<t._a[3]&&(p(t).bigHour=void 0),p(t).parsedDateParts=t._a.slice(0),p(t).meridiem=t._meridiem,t._a[3]=function(t,e,n){var r;return null==n?e:null!=t.meridiemHour?t.meridiemHour(e,n):(null!=t.isPM&&((r=t.isPM(n))&&e<12&&(e+=12),r||12!==e||(e=0)),e)}(t._locale,t._a[3],t._meridiem),oe(t),ie(t)}else ye(t);else de(t)}function me(t){var e,n,r,h,d=t._i,v=t._f;return t._locale=t._locale||re(t._l),null===d||void 0===v&&""===d?y({nullInput:!0}):("string"==typeof d&&(t._i=d=t._locale.preparse(d)),_(d)?new x(ie(d)):(u(d)?t._d=d:a(v)?function(t){var e,n,r,i,a;if(0===t._f.length)return p(t).invalidFormat=!0,t._d=new Date(NaN);for(i=0;i<t._f.length;i++)a=0,e=m({},t),null!=t._useUTC&&(e._useUTC=t._useUTC),e._f=t._f[i],ve(e),g(e)&&(a+=p(e).charsLeftOver,a+=10*p(e).unusedTokens.length,p(e).score=a,(null==r||a<r)&&(r=a,n=e));f(t,n||e)}(t):v?ve(t):s(n=(e=t)._i)?e._d=new Date(i.now()):u(n)?e._d=new Date(n.valueOf()):"string"==typeof n?(r=e,null===(h=fe.exec(r._i))?(de(r),!1===r._isValid&&(delete r._isValid,ye(r),!1===r._isValid&&(delete r._isValid,i.createFromInputFallback(r)))):r._d=new Date(+h[1])):a(n)?(e._a=l(n.slice(0),(function(t){return parseInt(t,10)})),oe(e)):o(n)?function(t){if(!t._d){var e=I(t._i);t._a=l([e.year,e.month,e.day||e.date,e.hour,e.minute,e.second,e.millisecond],(function(t){return t&&parseInt(t,10)})),oe(t)}}(e):c(n)?e._d=new Date(n):i.createFromInputFallback(e),g(t)||(t._d=null),t))}function be(t,e,n,r,i){var s,c={};return!0!==n&&!1!==n||(r=n,n=void 0),(o(t)&&function(t){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(t).length;var e;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}(t)||a(t)&&0===t.length)&&(t=void 0),c._isAMomentObject=!0,c._useUTC=c._isUTC=i,c._l=n,c._i=t,c._f=e,c._strict=r,(s=new x(ie(me(c))))._nextDay&&(s.add(1,"d"),s._nextDay=void 0),s}function xe(t,e,n,r){return be(t,e,n,r,!1)}i.createFromInputFallback=C("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged and will be removed in an upcoming major release. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",(function(t){t._d=new Date(t._i+(t._useUTC?" UTC":""))})),i.ISO_8601=function(){},i.RFC_2822=function(){};var _e=C("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=xe.apply(null,arguments);return this.isValid()&&t.isValid()?t<this?this:t:y()})),ke=C("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",(function(){var t=xe.apply(null,arguments);return this.isValid()&&t.isValid()?this<t?this:t:y()}));function we(t,e){var n,r;if(1===e.length&&a(e[0])&&(e=e[0]),!e.length)return xe();for(n=e[0],r=1;r<e.length;++r)e[r].isValid()&&!e[r][t](n)||(n=e[r]);return n}var Ee=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Te(t){var e=I(t),n=e.year||0,r=e.quarter||0,i=e.month||0,a=e.week||e.isoWeek||0,o=e.day||0,s=e.hour||0,c=e.minute||0,u=e.second||0,l=e.millisecond||0;this._isValid=function(t){for(var e in t)if(-1===mt.call(Ee,e)||null!=t[e]&&isNaN(t[e]))return!1;for(var n=!1,r=0;r<Ee.length;++r)if(t[Ee[r]]){if(n)return!1;parseFloat(t[Ee[r]])!==w(t[Ee[r]])&&(n=!0)}return!0}(e),this._milliseconds=+l+1e3*u+6e4*c+1e3*s*60*60,this._days=+o+7*a,this._months=+i+3*r+12*n,this._data={},this._locale=re(),this._bubble()}function Ce(t){return t instanceof Te}function Ae(t){return t<0?-1*Math.round(-1*t):Math.round(t)}function Se(t,e){W(t,0,0,(function(){var t=this.utcOffset(),n="+";return t<0&&(t=-t,n="-"),n+R(~~(t/60),2)+e+R(~~t%60,2)}))}Se("Z",":"),Se("ZZ",""),lt("Z",st),lt("ZZ",st),pt(["Z","ZZ"],(function(t,e,n){n._useUTC=!0,n._tzm=Oe(st,t)}));var Me=/([\+\-]|\d\d)/gi;function Oe(t,e){var n=(e||"").match(t);if(null===n)return null;var r=((n[n.length-1]||[])+"").match(Me)||["-",0,0],i=60*r[1]+w(r[2]);return 0===i?0:"+"===r[0]?i:-i}function De(t,e){var n,r;return e._isUTC?(n=e.clone(),r=(_(t)||u(t)?t.valueOf():xe(t).valueOf())-n.valueOf(),n._d.setTime(n._d.valueOf()+r),i.updateOffset(n,!1),n):xe(t).local()}function Ne(t){return 15*-Math.round(t._d.getTimezoneOffset()/15)}function Be(){return!!this.isValid()&&this._isUTC&&0===this._offset}i.updateOffset=function(){};var Le=/^(\-|\+)?(?:(\d*)[. ])?(\d+)\:(\d+)(?:\:(\d+)(\.\d*)?)?$/,Pe=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function Ie(t,e){var n,r,i,a=t,o=null;return Ce(t)?a={ms:t._milliseconds,d:t._days,M:t._months}:c(t)?(a={},e?a[e]=t:a.milliseconds=t):(o=Le.exec(t))?(n="-"===o[1]?-1:1,a={y:0,d:w(o[2])*n,h:w(o[3])*n,m:w(o[4])*n,s:w(o[5])*n,ms:w(Ae(1e3*o[6]))*n}):(o=Pe.exec(t))?(n="-"===o[1]?-1:1,a={y:Fe(o[2],n),M:Fe(o[3],n),w:Fe(o[4],n),d:Fe(o[5],n),h:Fe(o[6],n),m:Fe(o[7],n),s:Fe(o[8],n)}):null==a?a={}:"object"==typeof a&&("from"in a||"to"in a)&&(i=function(t,e){var n;return t.isValid()&&e.isValid()?(e=De(e,t),t.isBefore(e)?n=je(t,e):((n=je(e,t)).milliseconds=-n.milliseconds,n.months=-n.months),n):{milliseconds:0,months:0}}(xe(a.from),xe(a.to)),(a={}).ms=i.milliseconds,a.M=i.months),r=new Te(a),Ce(t)&&h(t,"_locale")&&(r._locale=t._locale),r}function Fe(t,e){var n=t&&parseFloat(t.replace(",","."));return(isNaN(n)?0:n)*e}function je(t,e){var n={};return n.months=e.month()-t.month()+12*(e.year()-t.year()),t.clone().add(n.months,"M").isAfter(e)&&--n.months,n.milliseconds=+e-+t.clone().add(n.months,"M"),n}function Re(t,e){return function(n,r){var i;return null===r||isNaN(+r)||(M(e,"moment()."+e+"(period, number) is deprecated. Please use moment()."+e+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),i=n,n=r,r=i),Ye(this,Ie(n="string"==typeof n?+n:n,r),t),this}}function Ye(t,e,n,r){var a=e._milliseconds,o=Ae(e._days),s=Ae(e._months);t.isValid()&&(r=null==r||r,s&&At(t,_t(t,"Month")+s*n),o&&kt(t,"Date",_t(t,"Date")+o*n),a&&t._d.setTime(t._d.valueOf()+a*n),r&&i.updateOffset(t,o||s))}Ie.fn=Te.prototype,Ie.invalid=function(){return Ie(NaN)};var ze=Re(1,"add"),Ue=Re(-1,"subtract");function $e(t,e){var n=12*(e.year()-t.year())+(e.month()-t.month()),r=t.clone().add(n,"months");return-(n+(e-r<0?(e-r)/(r-t.clone().add(n-1,"months")):(e-r)/(t.clone().add(n+1,"months")-r)))||0}function We(t){var e;return void 0===t?this._locale._abbr:(null!=(e=re(t))&&(this._locale=e),this)}i.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",i.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";var He=C("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",(function(t){return void 0===t?this.localeData():this.locale(t)}));function Ve(){return this._locale}var Ge=126227808e5;function qe(t,e){return(t%e+e)%e}function Xe(t,e,n){return t<100&&0<=t?new Date(t+400,e,n)-Ge:new Date(t,e,n).valueOf()}function Ze(t,e,n){return t<100&&0<=t?Date.UTC(t+400,e,n)-Ge:Date.UTC(t,e,n)}function Je(t,e){W(0,[t,t.length],0,e)}function Ke(t,e,n,r,i){var a;return null==t?Pt(this,r,i).year:((a=It(t,r,i))<e&&(e=a),function(t,e,n,r,i){var a=Lt(t,e,n,r,i),o=Nt(a.year,0,a.dayOfYear);return this.year(o.getUTCFullYear()),this.month(o.getUTCMonth()),this.date(o.getUTCDate()),this}.call(this,t,e,n,r,i))}W(0,["gg",2],0,(function(){return this.weekYear()%100})),W(0,["GG",2],0,(function(){return this.isoWeekYear()%100})),Je("gggg","weekYear"),Je("ggggg","weekYear"),Je("GGGG","isoWeekYear"),Je("GGGGG","isoWeekYear"),L("weekYear","gg"),L("isoWeekYear","GG"),j("weekYear",1),j("isoWeekYear",1),lt("G",at),lt("g",at),lt("GG",K,q),lt("gg",K,q),lt("GGGG",nt,Z),lt("gggg",nt,Z),lt("GGGGG",rt,J),lt("ggggg",rt,J),gt(["gggg","ggggg","GGGG","GGGGG"],(function(t,e,n,r){e[r.substr(0,2)]=w(t)})),gt(["gg","GG"],(function(t,e,n,r){e[r]=i.parseTwoDigitYear(t)})),W("Q",0,"Qo","quarter"),L("quarter","Q"),j("quarter",7),lt("Q",G),pt("Q",(function(t,e){e[1]=3*(w(t)-1)})),W("D",["DD",2],"Do","date"),L("date","D"),j("date",9),lt("D",K),lt("DD",K,q),lt("Do",(function(t,e){return t?e._dayOfMonthOrdinalParse||e._ordinalParse:e._dayOfMonthOrdinalParseLenient})),pt(["D","DD"],2),pt("Do",(function(t,e){e[2]=w(t.match(K)[0])}));var Qe=xt("Date",!0);W("DDD",["DDDD",3],"DDDo","dayOfYear"),L("dayOfYear","DDD"),j("dayOfYear",4),lt("DDD",et),lt("DDDD",X),pt(["DDD","DDDD"],(function(t,e,n){n._dayOfYear=w(t)})),W("m",["mm",2],0,"minute"),L("minute","m"),j("minute",14),lt("m",K),lt("mm",K,q),pt(["m","mm"],4);var tn=xt("Minutes",!1);W("s",["ss",2],0,"second"),L("second","s"),j("second",15),lt("s",K),lt("ss",K,q),pt(["s","ss"],5);var en,nn=xt("Seconds",!1);for(W("S",0,0,(function(){return~~(this.millisecond()/100)})),W(0,["SS",2],0,(function(){return~~(this.millisecond()/10)})),W(0,["SSS",3],0,"millisecond"),W(0,["SSSS",4],0,(function(){return 10*this.millisecond()})),W(0,["SSSSS",5],0,(function(){return 100*this.millisecond()})),W(0,["SSSSSS",6],0,(function(){return 1e3*this.millisecond()})),W(0,["SSSSSSS",7],0,(function(){return 1e4*this.millisecond()})),W(0,["SSSSSSSS",8],0,(function(){return 1e5*this.millisecond()})),W(0,["SSSSSSSSS",9],0,(function(){return 1e6*this.millisecond()})),L("millisecond","ms"),j("millisecond",16),lt("S",et,G),lt("SS",et,q),lt("SSS",et,X),en="SSSS";en.length<=9;en+="S")lt(en,it);function rn(t,e){e[6]=w(1e3*("0."+t))}for(en="S";en.length<=9;en+="S")pt(en,rn);var an=xt("Milliseconds",!1);W("z",0,0,"zoneAbbr"),W("zz",0,0,"zoneName");var on=x.prototype;function sn(t){return t}on.add=ze,on.calendar=function(t,e){var n=t||xe(),r=De(n,this).startOf("day"),a=i.calendarFormat(this,r)||"sameElse",o=e&&(O(e[a])?e[a].call(this,n):e[a]);return this.format(o||this.localeData().calendar(a,this,xe(n)))},on.clone=function(){return new x(this)},on.diff=function(t,e,n){var r,i,a;if(!this.isValid())return NaN;if(!(r=De(t,this)).isValid())return NaN;switch(i=6e4*(r.utcOffset()-this.utcOffset()),e=P(e)){case"year":a=$e(this,r)/12;break;case"month":a=$e(this,r);break;case"quarter":a=$e(this,r)/3;break;case"second":a=(this-r)/1e3;break;case"minute":a=(this-r)/6e4;break;case"hour":a=(this-r)/36e5;break;case"day":a=(this-r-i)/864e5;break;case"week":a=(this-r-i)/6048e5;break;default:a=this-r}return n?a:k(a)},on.endOf=function(t){var e;if(void 0===(t=P(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year()+1,0,1)-1;break;case"quarter":e=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":e=n(this.year(),this.month()+1,1)-1;break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":e=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":e=this._d.valueOf(),e+=36e5-qe(e+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":e=this._d.valueOf(),e+=6e4-qe(e,6e4)-1;break;case"second":e=this._d.valueOf(),e+=1e3-qe(e,1e3)-1}return this._d.setTime(e),i.updateOffset(this,!0),this},on.format=function(t){t||(t=this.isUtc()?i.defaultFormatUtc:i.defaultFormat);var e=H(this,t);return this.localeData().postformat(e)},on.from=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||xe(t).isValid())?Ie({to:this,from:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.fromNow=function(t){return this.from(xe(),t)},on.to=function(t,e){return this.isValid()&&(_(t)&&t.isValid()||xe(t).isValid())?Ie({from:this,to:t}).locale(this.locale()).humanize(!e):this.localeData().invalidDate()},on.toNow=function(t){return this.to(xe(),t)},on.get=function(t){return O(this[t=P(t)])?this[t]():this},on.invalidAt=function(){return p(this).overflow},on.isAfter=function(t,e){var n=_(t)?t:xe(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=P(e)||"millisecond")?this.valueOf()>n.valueOf():n.valueOf()<this.clone().startOf(e).valueOf())},on.isBefore=function(t,e){var n=_(t)?t:xe(t);return!(!this.isValid()||!n.isValid())&&("millisecond"===(e=P(e)||"millisecond")?this.valueOf()<n.valueOf():this.clone().endOf(e).valueOf()<n.valueOf())},on.isBetween=function(t,e,n,r){var i=_(t)?t:xe(t),a=_(e)?e:xe(e);return!!(this.isValid()&&i.isValid()&&a.isValid())&&("("===(r=r||"()")[0]?this.isAfter(i,n):!this.isBefore(i,n))&&(")"===r[1]?this.isBefore(a,n):!this.isAfter(a,n))},on.isSame=function(t,e){var n,r=_(t)?t:xe(t);return!(!this.isValid()||!r.isValid())&&("millisecond"===(e=P(e)||"millisecond")?this.valueOf()===r.valueOf():(n=r.valueOf(),this.clone().startOf(e).valueOf()<=n&&n<=this.clone().endOf(e).valueOf()))},on.isSameOrAfter=function(t,e){return this.isSame(t,e)||this.isAfter(t,e)},on.isSameOrBefore=function(t,e){return this.isSame(t,e)||this.isBefore(t,e)},on.isValid=function(){return g(this)},on.lang=He,on.locale=We,on.localeData=Ve,on.max=ke,on.min=_e,on.parsingFlags=function(){return f({},p(this))},on.set=function(t,e){if("object"==typeof t)for(var n=function(t){var e=[];for(var n in t)e.push({unit:n,priority:F[n]});return e.sort((function(t,e){return t.priority-e.priority})),e}(t=I(t)),r=0;r<n.length;r++)this[n[r].unit](t[n[r].unit]);else if(O(this[t=P(t)]))return this[t](e);return this},on.startOf=function(t){var e;if(void 0===(t=P(t))||"millisecond"===t||!this.isValid())return this;var n=this._isUTC?Ze:Xe;switch(t){case"year":e=n(this.year(),0,1);break;case"quarter":e=n(this.year(),this.month()-this.month()%3,1);break;case"month":e=n(this.year(),this.month(),1);break;case"week":e=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":e=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":e=n(this.year(),this.month(),this.date());break;case"hour":e=this._d.valueOf(),e-=qe(e+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":e=this._d.valueOf(),e-=qe(e,6e4);break;case"second":e=this._d.valueOf(),e-=qe(e,1e3)}return this._d.setTime(e),i.updateOffset(this,!0),this},on.subtract=Ue,on.toArray=function(){var t=this;return[t.year(),t.month(),t.date(),t.hour(),t.minute(),t.second(),t.millisecond()]},on.toObject=function(){var t=this;return{years:t.year(),months:t.month(),date:t.date(),hours:t.hours(),minutes:t.minutes(),seconds:t.seconds(),milliseconds:t.milliseconds()}},on.toDate=function(){return new Date(this.valueOf())},on.toISOString=function(t){if(!this.isValid())return null;var e=!0!==t,n=e?this.clone().utc():this;return n.year()<0||9999<n.year()?H(n,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):O(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",H(n,"Z")):H(n,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},on.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var t="moment",e="";this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",e="Z");var n="["+t+'("]',r=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",i=e+'[")]';return this.format(n+r+"-MM-DD[T]HH:mm:ss.SSS"+i)},on.toJSON=function(){return this.isValid()?this.toISOString():null},on.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},on.unix=function(){return Math.floor(this.valueOf()/1e3)},on.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},on.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},on.year=bt,on.isLeapYear=function(){return vt(this.year())},on.weekYear=function(t){return Ke.call(this,t,this.week(),this.weekday(),this.localeData()._week.dow,this.localeData()._week.doy)},on.isoWeekYear=function(t){return Ke.call(this,t,this.isoWeek(),this.isoWeekday(),1,4)},on.quarter=on.quarters=function(t){return null==t?Math.ceil((this.month()+1)/3):this.month(3*(t-1)+this.month()%3)},on.month=St,on.daysInMonth=function(){return wt(this.year(),this.month())},on.week=on.weeks=function(t){var e=this.localeData().week(this);return null==t?e:this.add(7*(t-e),"d")},on.isoWeek=on.isoWeeks=function(t){var e=Pt(this,1,4).week;return null==t?e:this.add(7*(t-e),"d")},on.weeksInYear=function(){var t=this.localeData()._week;return It(this.year(),t.dow,t.doy)},on.isoWeeksInYear=function(){return It(this.year(),1,4)},on.date=Qe,on.day=on.days=function(t){if(!this.isValid())return null!=t?this:NaN;var e,n,r=this._isUTC?this._d.getUTCDay():this._d.getDay();return null!=t?(e=t,n=this.localeData(),t="string"!=typeof e?e:isNaN(e)?"number"==typeof(e=n.weekdaysParse(e))?e:null:parseInt(e,10),this.add(t-r,"d")):r},on.weekday=function(t){if(!this.isValid())return null!=t?this:NaN;var e=(this.day()+7-this.localeData()._week.dow)%7;return null==t?e:this.add(t-e,"d")},on.isoWeekday=function(t){if(!this.isValid())return null!=t?this:NaN;if(null==t)return this.day()||7;var e,n,r=(e=t,n=this.localeData(),"string"==typeof e?n.weekdaysParse(e)%7||7:isNaN(e)?null:e);return this.day(this.day()%7?r:r-7)},on.dayOfYear=function(t){var e=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==t?e:this.add(t-e,"d")},on.hour=on.hours=Xt,on.minute=on.minutes=tn,on.second=on.seconds=nn,on.millisecond=on.milliseconds=an,on.utcOffset=function(t,e,n){var r,a=this._offset||0;if(!this.isValid())return null!=t?this:NaN;if(null==t)return this._isUTC?a:Ne(this);if("string"==typeof t){if(null===(t=Oe(st,t)))return this}else Math.abs(t)<16&&!n&&(t*=60);return!this._isUTC&&e&&(r=Ne(this)),this._offset=t,this._isUTC=!0,null!=r&&this.add(r,"m"),a!==t&&(!e||this._changeInProgress?Ye(this,Ie(t-a,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,i.updateOffset(this,!0),this._changeInProgress=null)),this},on.utc=function(t){return this.utcOffset(0,t)},on.local=function(t){return this._isUTC&&(this.utcOffset(0,t),this._isUTC=!1,t&&this.subtract(Ne(this),"m")),this},on.parseZone=function(){if(null!=this._tzm)this.utcOffset(this._tzm,!1,!0);else if("string"==typeof this._i){var t=Oe(ot,this._i);null!=t?this.utcOffset(t):this.utcOffset(0,!0)}return this},on.hasAlignedHourOffset=function(t){return!!this.isValid()&&(t=t?xe(t).utcOffset():0,(this.utcOffset()-t)%60==0)},on.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},on.isLocal=function(){return!!this.isValid()&&!this._isUTC},on.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},on.isUtc=Be,on.isUTC=Be,on.zoneAbbr=function(){return this._isUTC?"UTC":""},on.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},on.dates=C("dates accessor is deprecated. Use date instead.",Qe),on.months=C("months accessor is deprecated. Use month instead",St),on.years=C("years accessor is deprecated. Use year instead",bt),on.zone=C("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",(function(t,e){return null!=t?("string"!=typeof t&&(t=-t),this.utcOffset(t,e),this):-this.utcOffset()})),on.isDSTShifted=C("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",(function(){if(!s(this._isDSTShifted))return this._isDSTShifted;var t={};if(m(t,this),(t=me(t))._a){var e=t._isUTC?d(t._a):xe(t._a);this._isDSTShifted=this.isValid()&&0<E(t._a,e.toArray())}else this._isDSTShifted=!1;return this._isDSTShifted}));var cn=N.prototype;function un(t,e,n,r){var i=re(),a=d().set(r,e);return i[n](a,t)}function ln(t,e,n){if(c(t)&&(e=t,t=void 0),t=t||"",null!=e)return un(t,e,n,"month");var r,i=[];for(r=0;r<12;r++)i[r]=un(t,r,n,"month");return i}function hn(t,e,n,r){"boolean"==typeof t?c(e)&&(n=e,e=void 0):(e=t,t=!1,c(n=e)&&(n=e,e=void 0)),e=e||"";var i,a=re(),o=t?a._week.dow:0;if(null!=n)return un(e,(n+o)%7,r,"day");var s=[];for(i=0;i<7;i++)s[i]=un(e,(i+o)%7,r,"day");return s}cn.calendar=function(t,e,n){var r=this._calendar[t]||this._calendar.sameElse;return O(r)?r.call(e,n):r},cn.longDateFormat=function(t){var e=this._longDateFormat[t],n=this._longDateFormat[t.toUpperCase()];return e||!n?e:(this._longDateFormat[t]=n.replace(/MMMM|MM|DD|dddd/g,(function(t){return t.slice(1)})),this._longDateFormat[t])},cn.invalidDate=function(){return this._invalidDate},cn.ordinal=function(t){return this._ordinal.replace("%d",t)},cn.preparse=sn,cn.postformat=sn,cn.relativeTime=function(t,e,n,r){var i=this._relativeTime[n];return O(i)?i(t,e,n,r):i.replace(/%d/i,t)},cn.pastFuture=function(t,e){var n=this._relativeTime[0<t?"future":"past"];return O(n)?n(e):n.replace(/%s/i,e)},cn.set=function(t){var e,n;for(n in t)O(e=t[n])?this[n]=e:this["_"+n]=e;this._config=t,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},cn.months=function(t,e){return t?a(this._months)?this._months[t.month()]:this._months[(this._months.isFormat||Et).test(e)?"format":"standalone"][t.month()]:a(this._months)?this._months:this._months.standalone},cn.monthsShort=function(t,e){return t?a(this._monthsShort)?this._monthsShort[t.month()]:this._monthsShort[Et.test(e)?"format":"standalone"][t.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},cn.monthsParse=function(t,e,n){var r,i,a;if(this._monthsParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],r=0;r<12;++r)a=d([2e3,r]),this._shortMonthsParse[r]=this.monthsShort(a,"").toLocaleLowerCase(),this._longMonthsParse[r]=this.months(a,"").toLocaleLowerCase();return n?"MMM"===e?-1!==(i=mt.call(this._shortMonthsParse,o))?i:null:-1!==(i=mt.call(this._longMonthsParse,o))?i:null:"MMM"===e?-1!==(i=mt.call(this._shortMonthsParse,o))?i:-1!==(i=mt.call(this._longMonthsParse,o))?i:null:-1!==(i=mt.call(this._longMonthsParse,o))?i:-1!==(i=mt.call(this._shortMonthsParse,o))?i:null}.call(this,t,e,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),r=0;r<12;r++){if(i=d([2e3,r]),n&&!this._longMonthsParse[r]&&(this._longMonthsParse[r]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[r]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[r]||(a="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[r]=new RegExp(a.replace(".",""),"i")),n&&"MMMM"===e&&this._longMonthsParse[r].test(t))return r;if(n&&"MMM"===e&&this._shortMonthsParse[r].test(t))return r;if(!n&&this._monthsParse[r].test(t))return r}},cn.monthsRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsStrictRegex:this._monthsRegex):(h(this,"_monthsRegex")||(this._monthsRegex=Ot),this._monthsStrictRegex&&t?this._monthsStrictRegex:this._monthsRegex)},cn.monthsShortRegex=function(t){return this._monthsParseExact?(h(this,"_monthsRegex")||Dt.call(this),t?this._monthsShortStrictRegex:this._monthsShortRegex):(h(this,"_monthsShortRegex")||(this._monthsShortRegex=Mt),this._monthsShortStrictRegex&&t?this._monthsShortStrictRegex:this._monthsShortRegex)},cn.week=function(t){return Pt(t,this._week.dow,this._week.doy).week},cn.firstDayOfYear=function(){return this._week.doy},cn.firstDayOfWeek=function(){return this._week.dow},cn.weekdays=function(t,e){var n=a(this._weekdays)?this._weekdays:this._weekdays[t&&!0!==t&&this._weekdays.isFormat.test(e)?"format":"standalone"];return!0===t?Ft(n,this._week.dow):t?n[t.day()]:n},cn.weekdaysMin=function(t){return!0===t?Ft(this._weekdaysMin,this._week.dow):t?this._weekdaysMin[t.day()]:this._weekdaysMin},cn.weekdaysShort=function(t){return!0===t?Ft(this._weekdaysShort,this._week.dow):t?this._weekdaysShort[t.day()]:this._weekdaysShort},cn.weekdaysParse=function(t,e,n){var r,i,a;if(this._weekdaysParseExact)return function(t,e,n){var r,i,a,o=t.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],r=0;r<7;++r)a=d([2e3,1]).day(r),this._minWeekdaysParse[r]=this.weekdaysMin(a,"").toLocaleLowerCase(),this._shortWeekdaysParse[r]=this.weekdaysShort(a,"").toLocaleLowerCase(),this._weekdaysParse[r]=this.weekdays(a,"").toLocaleLowerCase();return n?"dddd"===e?-1!==(i=mt.call(this._weekdaysParse,o))?i:null:"ddd"===e?-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:null:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:null:"dddd"===e?-1!==(i=mt.call(this._weekdaysParse,o))?i:-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:null:"ddd"===e?-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:-1!==(i=mt.call(this._weekdaysParse,o))?i:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:null:-1!==(i=mt.call(this._minWeekdaysParse,o))?i:-1!==(i=mt.call(this._weekdaysParse,o))?i:-1!==(i=mt.call(this._shortWeekdaysParse,o))?i:null}.call(this,t,e,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),r=0;r<7;r++){if(i=d([2e3,1]).day(r),n&&!this._fullWeekdaysParse[r]&&(this._fullWeekdaysParse[r]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[r]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[r]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[r]||(a="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[r]=new RegExp(a.replace(".",""),"i")),n&&"dddd"===e&&this._fullWeekdaysParse[r].test(t))return r;if(n&&"ddd"===e&&this._shortWeekdaysParse[r].test(t))return r;if(n&&"dd"===e&&this._minWeekdaysParse[r].test(t))return r;if(!n&&this._weekdaysParse[r].test(t))return r}},cn.weekdaysRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Wt.call(this),t?this._weekdaysStrictRegex:this._weekdaysRegex):(h(this,"_weekdaysRegex")||(this._weekdaysRegex=zt),this._weekdaysStrictRegex&&t?this._weekdaysStrictRegex:this._weekdaysRegex)},cn.weekdaysShortRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Wt.call(this),t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(h(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=Ut),this._weekdaysShortStrictRegex&&t?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},cn.weekdaysMinRegex=function(t){return this._weekdaysParseExact?(h(this,"_weekdaysRegex")||Wt.call(this),t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(h(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=$t),this._weekdaysMinStrictRegex&&t?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},cn.isPM=function(t){return"p"===(t+"").toLowerCase().charAt(0)},cn.meridiem=function(t,e,n){return 11<t?n?"pm":"PM":n?"am":"AM"},ee("en",{dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10;return t+(1===w(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th")}}),i.lang=C("moment.lang is deprecated. Use moment.locale instead.",ee),i.langData=C("moment.langData is deprecated. Use moment.localeData instead.",re);var fn=Math.abs;function dn(t,e,n,r){var i=Ie(e,n);return t._milliseconds+=r*i._milliseconds,t._days+=r*i._days,t._months+=r*i._months,t._bubble()}function pn(t){return t<0?Math.floor(t):Math.ceil(t)}function gn(t){return 4800*t/146097}function yn(t){return 146097*t/4800}function vn(t){return function(){return this.as(t)}}var mn=vn("ms"),bn=vn("s"),xn=vn("m"),_n=vn("h"),kn=vn("d"),wn=vn("w"),En=vn("M"),Tn=vn("Q"),Cn=vn("y");function An(t){return function(){return this.isValid()?this._data[t]:NaN}}var Sn=An("milliseconds"),Mn=An("seconds"),On=An("minutes"),Dn=An("hours"),Nn=An("days"),Bn=An("months"),Ln=An("years"),Pn=Math.round,In={ss:44,s:45,m:45,h:22,d:26,M:11},Fn=Math.abs;function jn(t){return(0<t)-(t<0)||+t}function Rn(){if(!this.isValid())return this.localeData().invalidDate();var t,e,n=Fn(this._milliseconds)/1e3,r=Fn(this._days),i=Fn(this._months);e=k((t=k(n/60))/60),n%=60,t%=60;var a=k(i/12),o=i%=12,s=r,c=e,u=t,l=n?n.toFixed(3).replace(/\.?0+$/,""):"",h=this.asSeconds();if(!h)return"P0D";var f=h<0?"-":"",d=jn(this._months)!==jn(h)?"-":"",p=jn(this._days)!==jn(h)?"-":"",g=jn(this._milliseconds)!==jn(h)?"-":"";return f+"P"+(a?d+a+"Y":"")+(o?d+o+"M":"")+(s?p+s+"D":"")+(c||u||l?"T":"")+(c?g+c+"H":"")+(u?g+u+"M":"")+(l?g+l+"S":"")}var Yn=Te.prototype;return Yn.isValid=function(){return this._isValid},Yn.abs=function(){var t=this._data;return this._milliseconds=fn(this._milliseconds),this._days=fn(this._days),this._months=fn(this._months),t.milliseconds=fn(t.milliseconds),t.seconds=fn(t.seconds),t.minutes=fn(t.minutes),t.hours=fn(t.hours),t.months=fn(t.months),t.years=fn(t.years),this},Yn.add=function(t,e){return dn(this,t,e,1)},Yn.subtract=function(t,e){return dn(this,t,e,-1)},Yn.as=function(t){if(!this.isValid())return NaN;var e,n,r=this._milliseconds;if("month"===(t=P(t))||"quarter"===t||"year"===t)switch(e=this._days+r/864e5,n=this._months+gn(e),t){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(e=this._days+Math.round(yn(this._months)),t){case"week":return e/7+r/6048e5;case"day":return e+r/864e5;case"hour":return 24*e+r/36e5;case"minute":return 1440*e+r/6e4;case"second":return 86400*e+r/1e3;case"millisecond":return Math.floor(864e5*e)+r;default:throw new Error("Unknown unit "+t)}},Yn.asMilliseconds=mn,Yn.asSeconds=bn,Yn.asMinutes=xn,Yn.asHours=_n,Yn.asDays=kn,Yn.asWeeks=wn,Yn.asMonths=En,Yn.asQuarters=Tn,Yn.asYears=Cn,Yn.valueOf=function(){return this.isValid()?this._milliseconds+864e5*this._days+this._months%12*2592e6+31536e6*w(this._months/12):NaN},Yn._bubble=function(){var t,e,n,r,i,a=this._milliseconds,o=this._days,s=this._months,c=this._data;return 0<=a&&0<=o&&0<=s||a<=0&&o<=0&&s<=0||(a+=864e5*pn(yn(s)+o),s=o=0),c.milliseconds=a%1e3,t=k(a/1e3),c.seconds=t%60,e=k(t/60),c.minutes=e%60,n=k(e/60),c.hours=n%24,s+=i=k(gn(o+=k(n/24))),o-=pn(yn(i)),r=k(s/12),s%=12,c.days=o,c.months=s,c.years=r,this},Yn.clone=function(){return Ie(this)},Yn.get=function(t){return t=P(t),this.isValid()?this[t+"s"]():NaN},Yn.milliseconds=Sn,Yn.seconds=Mn,Yn.minutes=On,Yn.hours=Dn,Yn.days=Nn,Yn.weeks=function(){return k(this.days()/7)},Yn.months=Bn,Yn.years=Ln,Yn.humanize=function(t){if(!this.isValid())return this.localeData().invalidDate();var e,n,r,i,a,o,s,c,u,l,h=this.localeData(),f=(e=!t,n=h,r=Ie(this).abs(),i=Pn(r.as("s")),a=Pn(r.as("m")),o=Pn(r.as("h")),s=Pn(r.as("d")),c=Pn(r.as("M")),u=Pn(r.as("y")),(l=i<=In.ss&&["s",i]||i<In.s&&["ss",i]||a<=1&&["m"]||a<In.m&&["mm",a]||o<=1&&["h"]||o<In.h&&["hh",o]||s<=1&&["d"]||s<In.d&&["dd",s]||c<=1&&["M"]||c<In.M&&["MM",c]||u<=1&&["y"]||["yy",u])[2]=e,l[3]=0<+this,l[4]=n,function(t,e,n,r,i){return i.relativeTime(e||1,!!n,t,r)}.apply(null,l));return t&&(f=h.pastFuture(+this,f)),h.postformat(f)},Yn.toISOString=Rn,Yn.toString=Rn,Yn.toJSON=Rn,Yn.locale=We,Yn.localeData=Ve,Yn.toIsoString=C("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",Rn),Yn.lang=He,W("X",0,0,"unix"),W("x",0,0,"valueOf"),lt("x",at),lt("X",/[+-]?\d+(\.\d{1,3})?/),pt("X",(function(t,e,n){n._d=new Date(1e3*parseFloat(t,10))})),pt("x",(function(t,e,n){n._d=new Date(w(t))})),i.version="2.24.0",e=xe,i.fn=on,i.min=function(){return we("isBefore",[].slice.call(arguments,0))},i.max=function(){return we("isAfter",[].slice.call(arguments,0))},i.now=function(){return Date.now?Date.now():+new Date},i.utc=d,i.unix=function(t){return xe(1e3*t)},i.months=function(t,e){return ln(t,e,"months")},i.isDate=u,i.locale=ee,i.invalid=y,i.duration=Ie,i.isMoment=_,i.weekdays=function(t,e,n){return hn(t,e,n,"weekdays")},i.parseZone=function(){return xe.apply(null,arguments).parseZone()},i.localeData=re,i.isDuration=Ce,i.monthsShort=function(t,e){return ln(t,e,"monthsShort")},i.weekdaysMin=function(t,e,n){return hn(t,e,n,"weekdaysMin")},i.defineLocale=ne,i.updateLocale=function(t,e){if(null!=e){var n,r,i=Zt;null!=(r=te(t))&&(i=r._config),(n=new N(e=D(i,e))).parentLocale=Jt[t],Jt[t]=n,ee(t)}else null!=Jt[t]&&(null!=Jt[t].parentLocale?Jt[t]=Jt[t].parentLocale:null!=Jt[t]&&delete Jt[t]);return Jt[t]},i.locales=function(){return A(Jt)},i.weekdaysShort=function(t,e,n){return hn(t,e,n,"weekdaysShort")},i.normalizeUnits=P,i.relativeTimeRounding=function(t){return void 0===t?Pn:"function"==typeof t&&(Pn=t,!0)},i.relativeTimeThreshold=function(t,e){return void 0!==In[t]&&(void 0===e?In[t]:(In[t]=e,"s"===t&&(In.ss=e-1),!0))},i.calendarFormat=function(t,e){var n=t.diff(e,"days",!0);return n<-6?"sameElse":n<-1?"lastWeek":n<0?"lastDay":n<1?"sameDay":n<2?"nextDay":n<7?"nextWeek":"sameElse"},i.prototype=on,i.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},i}()}).call(this,n(7)(t))},function(t,e,n){var r=n(37),i=n(80);t.exports=function(t){return null!=t&&i(t.length)&&!r(t)}},function(t,e,n){var r=n(256),i=n(266),a=n(35),o=n(5),s=n(273);t.exports=function(t){return"function"==typeof t?t:null==t?a:"object"==typeof t?o(t)?i(t[0],t[1]):r(t):s(t)}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,9],n=[1,7],r=[1,6],i=[1,8],a=[1,20,21,22,23,38,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],o=[2,10],s=[1,20],c=[1,21],u=[1,22],l=[1,23],h=[1,30],f=[1,54],d=[1,32],p=[1,33],g=[1,34],y=[1,35],v=[1,36],m=[1,48],b=[1,43],x=[1,45],_=[1,40],k=[1,44],w=[1,47],E=[1,51],T=[1,52],C=[1,53],A=[1,42],S=[1,46],M=[1,49],O=[1,50],D=[1,41],N=[1,57],B=[1,62],L=[1,20,21,22,23,38,42,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],P=[1,66],I=[1,65],F=[1,67],j=[20,21,23,69,70],R=[1,88],Y=[1,93],z=[1,90],U=[1,95],$=[1,98],W=[1,96],H=[1,97],V=[1,91],G=[1,103],q=[1,102],X=[1,92],Z=[1,94],J=[1,99],K=[1,100],Q=[1,101],tt=[1,104],et=[20,21,22,23,69,70],nt=[20,21,22,23,47,69,70],rt=[20,21,22,23,40,46,47,49,51,53,55,57,59,61,62,64,69,70,80,94,95,98,99,100,102,103,109,110,111,112,113,114],it=[20,21,23],at=[20,21,23,46,69,70,80,94,95,98,99,100,102,103,109,110,111,112,113,114],ot=[1,12,20,21,22,23,24,38,42,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],st=[46,80,94,95,98,99,100,102,103,109,110,111,112,113,114],ct=[1,136],ut=[1,144],lt=[1,145],ht=[1,146],ft=[1,147],dt=[1,131],pt=[1,132],gt=[1,128],yt=[1,139],vt=[1,140],mt=[1,141],bt=[1,142],xt=[1,143],_t=[1,148],kt=[1,149],wt=[1,134],Et=[1,137],Tt=[1,133],Ct=[1,130],At=[20,21,22,23,38,42,46,75,76,77,78,79,80,94,95,98,99,100,102,103,109,110,111,112,113,114],St=[1,152],Mt=[20,21,22,23,26,46,80,94,95,98,99,100,102,103,109,110,111,112,113,114],Ot=[20,21,22,23,24,26,38,40,41,42,46,50,52,54,56,58,60,61,63,65,69,70,71,75,76,77,78,79,80,81,84,94,95,98,99,100,102,103,104,105,109,110,111,112,113,114],Dt=[12,21,22,24],Nt=[22,95],Bt=[1,233],Lt=[1,237],Pt=[1,234],It=[1,231],Ft=[1,228],jt=[1,229],Rt=[1,230],Yt=[1,232],zt=[1,235],Ut=[1,236],$t=[1,238],Wt=[1,255],Ht=[20,21,23,95],Vt=[20,21,22,23,75,91,94,95,98,99,100,101,102,103,104],Gt={trace:function(){},yy:{},symbols_:{error:2,start:3,mermaidDoc:4,directive:5,openDirective:6,typeDirective:7,closeDirective:8,separator:9,":":10,argDirective:11,open_directive:12,type_directive:13,arg_directive:14,close_directive:15,graphConfig:16,document:17,line:18,statement:19,SEMI:20,NEWLINE:21,SPACE:22,EOF:23,GRAPH:24,NODIR:25,DIR:26,FirstStmtSeperator:27,ending:28,endToken:29,spaceList:30,spaceListNewline:31,verticeStatement:32,styleStatement:33,linkStyleStatement:34,classDefStatement:35,classStatement:36,clickStatement:37,subgraph:38,text:39,SQS:40,SQE:41,end:42,link:43,node:44,vertex:45,AMP:46,STYLE_SEPARATOR:47,idString:48,PS:49,PE:50,"(-":51,"-)":52,STADIUMSTART:53,STADIUMEND:54,SUBROUTINESTART:55,SUBROUTINEEND:56,CYLINDERSTART:57,CYLINDEREND:58,DIAMOND_START:59,DIAMOND_STOP:60,TAGEND:61,TRAPSTART:62,TRAPEND:63,INVTRAPSTART:64,INVTRAPEND:65,linkStatement:66,arrowText:67,TESTSTR:68,START_LINK:69,LINK:70,PIPE:71,textToken:72,STR:73,keywords:74,STYLE:75,LINKSTYLE:76,CLASSDEF:77,CLASS:78,CLICK:79,DOWN:80,UP:81,textNoTags:82,textNoTagsToken:83,DEFAULT:84,stylesOpt:85,alphaNum:86,CALLBACKNAME:87,CALLBACKARGS:88,HREF:89,LINK_TARGET:90,HEX:91,numList:92,INTERPOLATE:93,NUM:94,COMMA:95,style:96,styleComponent:97,ALPHA:98,COLON:99,MINUS:100,UNIT:101,BRKT:102,DOT:103,PCT:104,TAGSTART:105,alphaNumToken:106,idStringToken:107,alphaNumStatement:108,PUNCTUATION:109,UNICODE_TEXT:110,PLUS:111,EQUALS:112,MULT:113,UNDERSCORE:114,graphCodeTokens:115,ARROW_CROSS:116,ARROW_POINT:117,ARROW_CIRCLE:118,ARROW_OPEN:119,QUOTE:120,$accept:0,$end:1},terminals_:{2:"error",10:":",12:"open_directive",13:"type_directive",14:"arg_directive",15:"close_directive",20:"SEMI",21:"NEWLINE",22:"SPACE",23:"EOF",24:"GRAPH",25:"NODIR",26:"DIR",38:"subgraph",40:"SQS",41:"SQE",42:"end",46:"AMP",47:"STYLE_SEPARATOR",49:"PS",50:"PE",51:"(-",52:"-)",53:"STADIUMSTART",54:"STADIUMEND",55:"SUBROUTINESTART",56:"SUBROUTINEEND",57:"CYLINDERSTART",58:"CYLINDEREND",59:"DIAMOND_START",60:"DIAMOND_STOP",61:"TAGEND",62:"TRAPSTART",63:"TRAPEND",64:"INVTRAPSTART",65:"INVTRAPEND",68:"TESTSTR",69:"START_LINK",70:"LINK",71:"PIPE",73:"STR",75:"STYLE",76:"LINKSTYLE",77:"CLASSDEF",78:"CLASS",79:"CLICK",80:"DOWN",81:"UP",84:"DEFAULT",87:"CALLBACKNAME",88:"CALLBACKARGS",89:"HREF",90:"LINK_TARGET",91:"HEX",93:"INTERPOLATE",94:"NUM",95:"COMMA",98:"ALPHA",99:"COLON",100:"MINUS",101:"UNIT",102:"BRKT",103:"DOT",104:"PCT",105:"TAGSTART",109:"PUNCTUATION",110:"UNICODE_TEXT",111:"PLUS",112:"EQUALS",113:"MULT",114:"UNDERSCORE",116:"ARROW_CROSS",117:"ARROW_POINT",118:"ARROW_CIRCLE",119:"ARROW_OPEN",120:"QUOTE"},productions_:[0,[3,1],[3,2],[5,4],[5,6],[6,1],[7,1],[11,1],[8,1],[4,2],[17,0],[17,2],[18,1],[18,1],[18,1],[18,1],[18,1],[16,2],[16,2],[16,2],[16,3],[28,2],[28,1],[29,1],[29,1],[29,1],[27,1],[27,1],[27,2],[31,2],[31,2],[31,1],[31,1],[30,2],[30,1],[19,2],[19,2],[19,2],[19,2],[19,2],[19,2],[19,9],[19,6],[19,4],[9,1],[9,1],[9,1],[32,3],[32,4],[32,2],[32,1],[44,1],[44,5],[44,3],[45,4],[45,6],[45,4],[45,4],[45,4],[45,4],[45,4],[45,4],[45,6],[45,4],[45,4],[45,4],[45,4],[45,4],[45,1],[43,2],[43,3],[43,3],[43,1],[43,3],[66,1],[67,3],[39,1],[39,2],[39,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[74,1],[82,1],[82,2],[35,5],[35,5],[36,5],[37,2],[37,4],[37,3],[37,5],[37,2],[37,4],[37,4],[37,6],[37,2],[37,4],[37,2],[37,4],[37,4],[37,6],[33,5],[33,5],[34,5],[34,5],[34,9],[34,9],[34,7],[34,7],[92,1],[92,3],[85,1],[85,3],[96,1],[96,2],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[97,1],[72,1],[72,1],[72,1],[72,1],[72,1],[72,1],[83,1],[83,1],[83,1],[83,1],[48,1],[48,2],[86,1],[86,2],[108,1],[108,1],[108,1],[108,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[106,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[107,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1],[115,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 5:r.parseDirective("%%{","open_directive");break;case 6:r.parseDirective(a[s],"type_directive");break;case 7:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 8:r.parseDirective("}%%","close_directive","flowchart");break;case 10:this.$=[];break;case 11:a[s]!==[]&&a[s-1].push(a[s]),this.$=a[s-1];break;case 12:case 76:case 78:case 90:case 146:case 148:case 149:this.$=a[s];break;case 19:r.setDirection("TB"),this.$="TB";break;case 20:r.setDirection(a[s-1]),this.$=a[s-1];break;case 35:this.$=a[s-1].nodes;break;case 36:case 37:case 38:case 39:case 40:this.$=[];break;case 41:this.$=r.addSubGraph(a[s-6],a[s-1],a[s-4]);break;case 42:this.$=r.addSubGraph(a[s-3],a[s-1],a[s-3]);break;case 43:this.$=r.addSubGraph(void 0,a[s-1],void 0);break;case 47:r.addLink(a[s-2].stmt,a[s],a[s-1]),this.$={stmt:a[s],nodes:a[s].concat(a[s-2].nodes)};break;case 48:r.addLink(a[s-3].stmt,a[s-1],a[s-2]),this.$={stmt:a[s-1],nodes:a[s-1].concat(a[s-3].nodes)};break;case 49:this.$={stmt:a[s-1],nodes:a[s-1]};break;case 50:this.$={stmt:a[s],nodes:a[s]};break;case 51:this.$=[a[s]];break;case 52:this.$=a[s-4].concat(a[s]);break;case 53:this.$=[a[s-2]],r.setClass(a[s-2],a[s]);break;case 54:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"square");break;case 55:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"circle");break;case 56:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"ellipse");break;case 57:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"stadium");break;case 58:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"subroutine");break;case 59:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"cylinder");break;case 60:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"round");break;case 61:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"diamond");break;case 62:this.$=a[s-5],r.addVertex(a[s-5],a[s-2],"hexagon");break;case 63:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"odd");break;case 64:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"trapezoid");break;case 65:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"inv_trapezoid");break;case 66:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_right");break;case 67:this.$=a[s-3],r.addVertex(a[s-3],a[s-1],"lean_left");break;case 68:this.$=a[s],r.addVertex(a[s]);break;case 69:a[s-1].text=a[s],this.$=a[s-1];break;case 70:case 71:a[s-2].text=a[s-1],this.$=a[s-2];break;case 72:this.$=a[s];break;case 73:var c=r.destructLink(a[s],a[s-2]);this.$={type:c.type,stroke:c.stroke,length:c.length,text:a[s-1]};break;case 74:c=r.destructLink(a[s]);this.$={type:c.type,stroke:c.stroke,length:c.length};break;case 75:this.$=a[s-1];break;case 77:case 91:case 147:this.$=a[s-1]+""+a[s];break;case 92:case 93:this.$=a[s-4],r.addClass(a[s-2],a[s]);break;case 94:this.$=a[s-4],r.setClass(a[s-2],a[s]);break;case 95:case 103:this.$=a[s-1],r.setClickEvent(a[s-1],a[s]);break;case 96:case 104:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 97:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 98:this.$=a[s-4],r.setClickEvent(a[s-4],a[s-3],a[s-2]),r.setTooltip(a[s-4],a[s]);break;case 99:case 105:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 100:case 106:this.$=a[s-3],r.setLink(a[s-3],a[s-2]),r.setTooltip(a[s-3],a[s]);break;case 101:case 107:this.$=a[s-3],r.setLink(a[s-3],a[s-2],a[s]);break;case 102:case 108:this.$=a[s-5],r.setLink(a[s-5],a[s-4],a[s]),r.setTooltip(a[s-5],a[s-2]);break;case 109:this.$=a[s-4],r.addVertex(a[s-2],void 0,void 0,a[s]);break;case 110:case 112:this.$=a[s-4],r.updateLink(a[s-2],a[s]);break;case 111:this.$=a[s-4],r.updateLink([a[s-2]],a[s]);break;case 113:this.$=a[s-8],r.updateLinkInterpolate([a[s-6]],a[s-2]),r.updateLink([a[s-6]],a[s]);break;case 114:this.$=a[s-8],r.updateLinkInterpolate(a[s-6],a[s-2]),r.updateLink(a[s-6],a[s]);break;case 115:this.$=a[s-6],r.updateLinkInterpolate([a[s-4]],a[s]);break;case 116:this.$=a[s-6],r.updateLinkInterpolate(a[s-4],a[s]);break;case 117:case 119:this.$=[a[s]];break;case 118:case 120:a[s-2].push(a[s]),this.$=a[s-2];break;case 122:this.$=a[s-1]+a[s];break;case 144:this.$=a[s];break;case 145:this.$=a[s-1]+""+a[s];break;case 150:this.$="v";break;case 151:this.$="-"}},table:[{3:1,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},{1:[3]},{1:[2,1]},{3:10,4:2,5:3,6:5,12:e,16:4,21:n,22:r,24:i},t(a,o,{17:11}),{7:12,13:[1,13]},{16:14,21:n,22:r,24:i},{16:15,21:n,22:r,24:i},{25:[1,16],26:[1,17]},{13:[2,5]},{1:[2,2]},{1:[2,9],18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{8:55,10:[1,56],15:N},t([10,15],[2,6]),t(a,[2,17]),t(a,[2,18]),t(a,[2,19]),{20:[1,59],21:[1,60],22:B,27:58,30:61},t(L,[2,11]),t(L,[2,12]),t(L,[2,13]),t(L,[2,14]),t(L,[2,15]),t(L,[2,16]),{9:63,20:P,21:I,23:F,43:64,66:68,69:[1,69],70:[1,70]},{9:71,20:P,21:I,23:F},{9:72,20:P,21:I,23:F},{9:73,20:P,21:I,23:F},{9:74,20:P,21:I,23:F},{9:75,20:P,21:I,23:F},{9:77,20:P,21:I,22:[1,76],23:F},t(j,[2,50],{30:78,22:B}),{22:[1,79]},{22:[1,80]},{22:[1,81]},{22:[1,82]},{26:R,46:Y,73:[1,86],80:z,86:85,87:[1,83],89:[1,84],94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(et,[2,51],{47:[1,105]}),t(nt,[2,68],{107:116,40:[1,106],46:f,49:[1,107],51:[1,108],53:[1,109],55:[1,110],57:[1,111],59:[1,112],61:[1,113],62:[1,114],64:[1,115],80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,109:C,110:A,111:S,112:M,113:O,114:D}),t(rt,[2,144]),t(rt,[2,165]),t(rt,[2,166]),t(rt,[2,167]),t(rt,[2,168]),t(rt,[2,169]),t(rt,[2,170]),t(rt,[2,171]),t(rt,[2,172]),t(rt,[2,173]),t(rt,[2,174]),t(rt,[2,175]),t(rt,[2,176]),t(rt,[2,177]),t(rt,[2,178]),t(rt,[2,179]),{9:117,20:P,21:I,23:F},{11:118,14:[1,119]},t(it,[2,8]),t(a,[2,20]),t(a,[2,26]),t(a,[2,27]),{21:[1,120]},t(at,[2,34],{30:121,22:B}),t(L,[2,35]),{44:122,45:37,46:f,48:38,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},t(ot,[2,44]),t(ot,[2,45]),t(ot,[2,46]),t(st,[2,72],{67:123,68:[1,124],71:[1,125]}),{22:ct,24:ut,26:lt,38:ht,39:126,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t([46,68,71,80,94,95,98,99,100,102,103,109,110,111,112,113,114],[2,74]),t(L,[2,36]),t(L,[2,37]),t(L,[2,38]),t(L,[2,39]),t(L,[2,40]),{22:ct,24:ut,26:lt,38:ht,39:150,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(At,o,{17:151}),t(j,[2,49],{46:St}),{26:R,46:Y,80:z,86:153,91:[1,154],94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},{84:[1,155],92:156,94:[1,157]},{26:R,46:Y,80:z,84:[1,158],86:159,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},{26:R,46:Y,80:z,86:160,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(it,[2,95],{22:[1,161],88:[1,162]}),t(it,[2,99],{22:[1,163]}),t(it,[2,103],{106:89,108:165,22:[1,164],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),t(it,[2,105],{22:[1,166]}),t(Mt,[2,146]),t(Mt,[2,148]),t(Mt,[2,149]),t(Mt,[2,150]),t(Mt,[2,151]),t(Ot,[2,152]),t(Ot,[2,153]),t(Ot,[2,154]),t(Ot,[2,155]),t(Ot,[2,156]),t(Ot,[2,157]),t(Ot,[2,158]),t(Ot,[2,159]),t(Ot,[2,160]),t(Ot,[2,161]),t(Ot,[2,162]),t(Ot,[2,163]),t(Ot,[2,164]),{46:f,48:167,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:ct,24:ut,26:lt,38:ht,39:168,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:170,42:ft,46:Y,49:[1,169],61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:171,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:172,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:173,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:174,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:175,42:ft,46:Y,59:[1,176],61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:177,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:178,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:179,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(rt,[2,145]),t(Dt,[2,3]),{8:180,15:N},{15:[2,7]},t(a,[2,28]),t(at,[2,33]),t(j,[2,47],{30:181,22:B}),t(st,[2,69],{22:[1,182]}),{22:[1,183]},{22:ct,24:ut,26:lt,38:ht,39:184,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,69:pt,70:[1,185],72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(Ot,[2,76]),t(Ot,[2,78]),t(Ot,[2,134]),t(Ot,[2,135]),t(Ot,[2,136]),t(Ot,[2,137]),t(Ot,[2,138]),t(Ot,[2,139]),t(Ot,[2,140]),t(Ot,[2,141]),t(Ot,[2,142]),t(Ot,[2,143]),t(Ot,[2,79]),t(Ot,[2,80]),t(Ot,[2,81]),t(Ot,[2,82]),t(Ot,[2,83]),t(Ot,[2,84]),t(Ot,[2,85]),t(Ot,[2,86]),t(Ot,[2,87]),t(Ot,[2,88]),t(Ot,[2,89]),{9:188,20:P,21:I,22:ct,23:F,24:ut,26:lt,38:ht,40:[1,187],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,189],44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:B,30:190},{22:[1,191],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:165,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:[1,192]},{22:[1,193]},{22:[1,194],95:[1,195]},t(Nt,[2,117]),{22:[1,196]},{22:[1,197],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:165,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:[1,198],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:165,109:X,110:Z,111:J,112:K,113:Q,114:tt},{73:[1,199]},t(it,[2,97],{22:[1,200]}),{73:[1,201],90:[1,202]},{73:[1,203]},t(Mt,[2,147]),{73:[1,204],90:[1,205]},t(et,[2,53],{107:116,46:f,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,109:C,110:A,111:S,112:M,113:O,114:D}),{22:ct,24:ut,26:lt,38:ht,41:[1,206],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:207,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,50:[1,208],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,52:[1,209],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,54:[1,210],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,56:[1,211],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,58:[1,212],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,60:[1,213],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,39:214,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,41:[1,215],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,63:[1,216],65:[1,217],69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,63:[1,219],65:[1,218],69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{9:220,20:P,21:I,23:F},t(j,[2,48],{46:St}),t(st,[2,71]),t(st,[2,70]),{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,61:dt,69:pt,71:[1,221],72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(st,[2,73]),t(Ot,[2,77]),{22:ct,24:ut,26:lt,38:ht,39:222,42:ft,46:Y,61:dt,69:pt,72:127,73:gt,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(At,o,{17:223}),t(L,[2,43]),{45:224,46:f,48:38,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:Bt,75:Lt,85:225,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:239,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:240,91:Pt,93:[1,241],94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:242,91:Pt,93:[1,243],94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{94:[1,244]},{22:Bt,75:Lt,85:245,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:246,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{26:R,46:Y,80:z,86:247,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(it,[2,96]),{73:[1,248]},t(it,[2,100],{22:[1,249]}),t(it,[2,101]),t(it,[2,104]),t(it,[2,106],{22:[1,250]}),t(it,[2,107]),t(nt,[2,54]),{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,50:[1,251],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(nt,[2,60]),t(nt,[2,56]),t(nt,[2,57]),t(nt,[2,58]),t(nt,[2,59]),t(nt,[2,61]),{22:ct,24:ut,26:lt,38:ht,42:ft,46:Y,60:[1,252],61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(nt,[2,63]),t(nt,[2,64]),t(nt,[2,66]),t(nt,[2,65]),t(nt,[2,67]),t(Dt,[2,4]),t([22,46,80,94,95,98,99,100,102,103,109,110,111,112,113,114],[2,75]),{22:ct,24:ut,26:lt,38:ht,41:[1,253],42:ft,46:Y,61:dt,69:pt,72:186,74:138,75:yt,76:vt,77:mt,78:bt,79:xt,80:_t,81:kt,83:129,84:wt,94:U,95:$,98:W,99:H,100:Et,102:G,103:q,104:Tt,105:Ct,106:135,109:X,110:Z,111:J,112:K,113:Q,114:tt},{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,254],44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},t(et,[2,52]),t(it,[2,109],{95:Wt}),t(Ht,[2,119],{97:256,22:Bt,75:Lt,91:Pt,94:It,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t}),t(Vt,[2,121]),t(Vt,[2,123]),t(Vt,[2,124]),t(Vt,[2,125]),t(Vt,[2,126]),t(Vt,[2,127]),t(Vt,[2,128]),t(Vt,[2,129]),t(Vt,[2,130]),t(Vt,[2,131]),t(Vt,[2,132]),t(Vt,[2,133]),t(it,[2,110],{95:Wt}),t(it,[2,111],{95:Wt}),{22:[1,257]},t(it,[2,112],{95:Wt}),{22:[1,258]},t(Nt,[2,118]),t(it,[2,92],{95:Wt}),t(it,[2,93],{95:Wt}),t(it,[2,94],{106:89,108:165,26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),t(it,[2,98]),{90:[1,259]},{90:[1,260]},{50:[1,261]},{60:[1,262]},{9:263,20:P,21:I,23:F},t(L,[2,42]),{22:Bt,75:Lt,91:Pt,94:It,96:264,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},t(Vt,[2,122]),{26:R,46:Y,80:z,86:265,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},{26:R,46:Y,80:z,86:266,94:U,95:$,98:W,99:H,100:V,102:G,103:q,106:89,108:87,109:X,110:Z,111:J,112:K,113:Q,114:tt},t(it,[2,102]),t(it,[2,108]),t(nt,[2,55]),t(nt,[2,62]),t(At,o,{17:267}),t(Ht,[2,120],{97:256,22:Bt,75:Lt,91:Pt,94:It,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t}),t(it,[2,115],{106:89,108:165,22:[1,268],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),t(it,[2,116],{106:89,108:165,22:[1,269],26:R,46:Y,80:z,94:U,95:$,98:W,99:H,100:V,102:G,103:q,109:X,110:Z,111:J,112:K,113:Q,114:tt}),{18:18,19:19,20:s,21:c,22:u,23:l,32:24,33:25,34:26,35:27,36:28,37:29,38:h,42:[1,270],44:31,45:37,46:f,48:38,75:d,76:p,77:g,78:y,79:v,80:m,94:b,95:x,98:_,99:k,100:w,102:E,103:T,107:39,109:C,110:A,111:S,112:M,113:O,114:D},{22:Bt,75:Lt,85:271,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},{22:Bt,75:Lt,85:272,91:Pt,94:It,96:226,97:227,98:Ft,99:jt,100:Rt,101:Yt,102:zt,103:Ut,104:$t},t(L,[2,41]),t(it,[2,113],{95:Wt}),t(it,[2,114],{95:Wt})],defaultActions:{2:[2,1],9:[2,5],10:[2,2],119:[2,7]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},qt={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),12;case 1:return this.begin("type_directive"),13;case 2:return this.popState(),this.begin("arg_directive"),10;case 3:return this.popState(),this.popState(),15;case 4:return 14;case 5:case 6:break;case 7:this.begin("string");break;case 8:this.popState();break;case 9:return"STR";case 10:return 75;case 11:return 84;case 12:return 76;case 13:return 93;case 14:return 77;case 15:return 78;case 16:this.begin("href");break;case 17:this.popState();break;case 18:return 89;case 19:this.begin("callbackname");break;case 20:this.popState();break;case 21:this.popState(),this.begin("callbackargs");break;case 22:return 87;case 23:this.popState();break;case 24:return 88;case 25:this.begin("click");break;case 26:this.popState();break;case 27:return 79;case 28:case 29:return t.lex.firstGraph()&&this.begin("dir"),24;case 30:return 38;case 31:return 42;case 32:case 33:case 34:case 35:return 90;case 36:return this.popState(),25;case 37:case 38:case 39:case 40:case 41:case 42:case 43:case 44:case 45:case 46:return this.popState(),26;case 47:return 94;case 48:return 102;case 49:return 47;case 50:return 99;case 51:return 46;case 52:return 20;case 53:return 95;case 54:return 113;case 55:case 56:case 57:return 70;case 58:case 59:case 60:return 69;case 61:return 51;case 62:return 52;case 63:return 53;case 64:return 54;case 65:return 55;case 66:return 56;case 67:return 57;case 68:return 58;case 69:return 100;case 70:return 103;case 71:return 114;case 72:return 111;case 73:return 104;case 74:case 75:return 112;case 76:return 105;case 77:return 61;case 78:return 81;case 79:return"SEP";case 80:return 80;case 81:return 98;case 82:return 63;case 83:return 62;case 84:return 65;case 85:return 64;case 86:return 109;case 87:return 110;case 88:return 71;case 89:return 49;case 90:return 50;case 91:return 40;case 92:return 41;case 93:return 59;case 94:return 60;case 95:return 120;case 96:return 21;case 97:return 22;case 98:return 23}},rules:[/^(?:%%\{)/,/^(?:((?:(?!\}%%)[^:.])*))/,/^(?::)/,/^(?:\}%%)/,/^(?:((?:(?!\}%%).|\n)*))/,/^(?:%%(?!\{)[^\n]*)/,/^(?:[^\}]%%[^\n]*)/,/^(?:["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:style\b)/,/^(?:default\b)/,/^(?:linkStyle\b)/,/^(?:interpolate\b)/,/^(?:classDef\b)/,/^(?:class\b)/,/^(?:href[\s]+["])/,/^(?:["])/,/^(?:[^"]*)/,/^(?:call[\s]+)/,/^(?:\([\s]*\))/,/^(?:\()/,/^(?:[^(]*)/,/^(?:\))/,/^(?:[^)]*)/,/^(?:click[\s]+)/,/^(?:[\s\n])/,/^(?:[^\s\n]*)/,/^(?:graph\b)/,/^(?:flowchart\b)/,/^(?:subgraph\b)/,/^(?:end\b\s*)/,/^(?:_self\b)/,/^(?:_blank\b)/,/^(?:_parent\b)/,/^(?:_top\b)/,/^(?:(\r?\n)*\s*\n)/,/^(?:\s*LR\b)/,/^(?:\s*RL\b)/,/^(?:\s*TB\b)/,/^(?:\s*BT\b)/,/^(?:\s*TD\b)/,/^(?:\s*BR\b)/,/^(?:\s*<)/,/^(?:\s*>)/,/^(?:\s*\^)/,/^(?:\s*v\b)/,/^(?:[0-9]+)/,/^(?:#)/,/^(?::::)/,/^(?::)/,/^(?:&)/,/^(?:;)/,/^(?:,)/,/^(?:\*)/,/^(?:\s*[xo<]?--+[-xo>]\s*)/,/^(?:\s*[xo<]?==+[=xo>]\s*)/,/^(?:\s*[xo<]?-?\.+-[xo>]?\s*)/,/^(?:\s*[xo<]?--\s*)/,/^(?:\s*[xo<]?==\s*)/,/^(?:\s*[xo<]?-\.\s*)/,/^(?:\(-)/,/^(?:-\))/,/^(?:\(\[)/,/^(?:\]\))/,/^(?:\[\[)/,/^(?:\]\])/,/^(?:\[\()/,/^(?:\)\])/,/^(?:-)/,/^(?:\.)/,/^(?:[\_])/,/^(?:\+)/,/^(?:%)/,/^(?:=)/,/^(?:=)/,/^(?:<)/,/^(?:>)/,/^(?:\^)/,/^(?:\\\|)/,/^(?:v\b)/,/^(?:[A-Za-z]+)/,/^(?:\\\])/,/^(?:\[\/)/,/^(?:\/\])/,/^(?:\[\\)/,/^(?:[!"#$%&'*+,-.`?\\_/])/,/^(?:[\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6]|[\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377]|[\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5]|[\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA]|[\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE]|[\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA]|[\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0]|[\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977]|[\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2]|[\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A]|[\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39]|[\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8]|[\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C]|[\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C]|[\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99]|[\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0]|[\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D]|[\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3]|[\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10]|[\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1]|[\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81]|[\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3]|[\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6]|[\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A]|[\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081]|[\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D]|[\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0]|[\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310]|[\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C]|[\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711]|[\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7]|[\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C]|[\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16]|[\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF]|[\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC]|[\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D]|[\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D]|[\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3]|[\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F]|[\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128]|[\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184]|[\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3]|[\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6]|[\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE]|[\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C]|[\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D]|[\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC]|[\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B]|[\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788]|[\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805]|[\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB]|[\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28]|[\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5]|[\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4]|[\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E]|[\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D]|[\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36]|[\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D]|[\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC]|[\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF]|[\uFFD2-\uFFD7\uFFDA-\uFFDC])/,/^(?:\|)/,/^(?:\()/,/^(?:\))/,/^(?:\[)/,/^(?:\])/,/^(?:\{)/,/^(?:\})/,/^(?:")/,/^(?:(\r?\n)+)/,/^(?:\s)/,/^(?:$)/],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[23,24],inclusive:!1},callbackname:{rules:[20,21,22],inclusive:!1},href:{rules:[17,18],inclusive:!1},click:{rules:[26,27],inclusive:!1},vertex:{rules:[],inclusive:!1},dir:{rules:[36,37,38,39,40,41,42,43,44,45,46],inclusive:!1},string:{rules:[8,9],inclusive:!1},INITIAL:{rules:[0,5,6,7,10,11,12,13,14,15,16,19,25,28,29,30,31,32,33,34,35,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98],inclusive:!0}}};function Xt(){this.yy={}}return Gt.lexer=qt,Xt.prototype=Gt,Gt.Parser=Xt,new Xt}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,3],n=[1,5],r=[7,9,11,12,13,14,15,16,17,18,20,27,32],i=[1,15],a=[1,16],o=[1,17],s=[1,18],c=[1,19],u=[1,20],l=[1,21],h=[1,23],f=[1,25],d=[1,28],p=[5,7,9,11,12,13,14,15,16,17,18,20,27,32],g={trace:function(){},yy:{},symbols_:{error:2,start:3,directive:4,gantt:5,document:6,EOF:7,line:8,SPACE:9,statement:10,NL:11,dateFormat:12,inclusiveEndDates:13,axisFormat:14,excludes:15,todayMarker:16,title:17,section:18,clickStatement:19,taskTxt:20,taskData:21,openDirective:22,typeDirective:23,closeDirective:24,":":25,argDirective:26,click:27,callbackname:28,callbackargs:29,href:30,clickStatementDebug:31,open_directive:32,type_directive:33,arg_directive:34,close_directive:35,$accept:0,$end:1},terminals_:{2:"error",5:"gantt",7:"EOF",9:"SPACE",11:"NL",12:"dateFormat",13:"inclusiveEndDates",14:"axisFormat",15:"excludes",16:"todayMarker",17:"title",18:"section",20:"taskTxt",21:"taskData",25:":",27:"click",28:"callbackname",29:"callbackargs",30:"href",32:"open_directive",33:"type_directive",34:"arg_directive",35:"close_directive"},productions_:[0,[3,2],[3,3],[6,0],[6,2],[8,2],[8,1],[8,1],[8,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,1],[10,2],[10,1],[4,4],[4,6],[19,2],[19,3],[19,3],[19,4],[19,3],[19,4],[19,2],[31,2],[31,3],[31,3],[31,4],[31,3],[31,4],[31,2],[22,1],[23,1],[26,1],[24,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 2:return a[s-1];case 3:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 7:case 8:this.$=[];break;case 9:r.setDateFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 10:r.enableInclusiveEndDates(),this.$=a[s].substr(18);break;case 11:r.setAxisFormat(a[s].substr(11)),this.$=a[s].substr(11);break;case 12:r.setExcludes(a[s].substr(9)),this.$=a[s].substr(9);break;case 13:r.setTodayMarker(a[s].substr(12)),this.$=a[s].substr(12);break;case 14:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 15:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 17:r.addTask(a[s-1],a[s]),this.$="task";break;case 21:this.$=a[s-1],r.setClickEvent(a[s-1],a[s],null);break;case 22:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],a[s]);break;case 23:this.$=a[s-2],r.setClickEvent(a[s-2],a[s-1],null),r.setLink(a[s-2],a[s]);break;case 24:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-2],a[s-1]),r.setLink(a[s-3],a[s]);break;case 25:this.$=a[s-2],r.setClickEvent(a[s-2],a[s],null),r.setLink(a[s-2],a[s-1]);break;case 26:this.$=a[s-3],r.setClickEvent(a[s-3],a[s-1],a[s]),r.setLink(a[s-3],a[s-2]);break;case 27:this.$=a[s-1],r.setLink(a[s-1],a[s]);break;case 28:case 34:this.$=a[s-1]+" "+a[s];break;case 29:case 30:case 32:this.$=a[s-2]+" "+a[s-1]+" "+a[s];break;case 31:case 33:this.$=a[s-3]+" "+a[s-2]+" "+a[s-1]+" "+a[s];break;case 35:r.parseDirective("%%{","open_directive");break;case 36:r.parseDirective(a[s],"type_directive");break;case 37:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 38:r.parseDirective("}%%","close_directive","gantt")}},table:[{3:1,4:2,5:e,22:4,32:n},{1:[3]},{3:6,4:2,5:e,22:4,32:n},t(r,[2,3],{6:7}),{23:8,33:[1,9]},{33:[2,35]},{1:[2,1]},{4:24,7:[1,10],8:11,9:[1,12],10:13,11:[1,14],12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:22,20:h,22:4,27:f,32:n},{24:26,25:[1,27],35:d},t([25,35],[2,36]),t(r,[2,8],{1:[2,2]}),t(r,[2,4]),{4:24,10:29,12:i,13:a,14:o,15:s,16:c,17:u,18:l,19:22,20:h,22:4,27:f,32:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,9]),t(r,[2,10]),t(r,[2,11]),t(r,[2,12]),t(r,[2,13]),t(r,[2,14]),t(r,[2,15]),t(r,[2,16]),{21:[1,30]},t(r,[2,18]),{28:[1,31],30:[1,32]},{11:[1,33]},{26:34,34:[1,35]},{11:[2,38]},t(r,[2,5]),t(r,[2,17]),t(r,[2,21],{29:[1,36],30:[1,37]}),t(r,[2,27],{28:[1,38]}),t(p,[2,19]),{24:39,35:d},{35:[2,37]},t(r,[2,22],{30:[1,40]}),t(r,[2,23]),t(r,[2,25],{29:[1,41]}),{11:[1,42]},t(r,[2,24]),t(r,[2,26]),t(p,[2,20])],defaultActions:{5:[2,35],6:[2,1],28:[2,38],35:[2,37]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},y={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),32;case 1:return this.begin("type_directive"),33;case 2:return this.popState(),this.begin("arg_directive"),25;case 3:return this.popState(),this.popState(),35;case 4:return 34;case 5:case 6:case 7:break;case 8:return 11;case 9:case 10:case 11:break;case 12:this.begin("href");break;case 13:this.popState();break;case 14:return 30;case 15:this.begin("callbackname");break;case 16:this.popState();break;case 17:this.popState(),this.begin("callbackargs");break;case 18:return 28;case 19:this.popState();break;case 20:return 29;case 21:this.begin("click");break;case 22:this.popState();break;case 23:return 27;case 24:return 5;case 25:return 12;case 26:return 13;case 27:return 14;case 28:return 15;case 29:return 16;case 30:return"date";case 31:return 17;case 32:return 18;case 33:return 20;case 34:return 21;case 35:return 25;case 36:return 7;case 37:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)*[^\n]*)/i,/^(?:[^\}]%%*[^\n]*)/i,/^(?:%%*[^\n]*[\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:href[\s]+["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:call[\s]+)/i,/^(?:\([\s]*\))/i,/^(?:\()/i,/^(?:[^(]*)/i,/^(?:\))/i,/^(?:[^)]*)/i,/^(?:click[\s]+)/i,/^(?:[\s\n])/i,/^(?:[^\s\n]*)/i,/^(?:gantt\b)/i,/^(?:dateFormat\s[^#\n;]+)/i,/^(?:inclusiveEndDates\b)/i,/^(?:axisFormat\s[^#\n;]+)/i,/^(?:excludes\s[^#\n;]+)/i,/^(?:todayMarker\s[^\n;]+)/i,/^(?:\d\d\d\d-\d\d-\d\d\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},callbackargs:{rules:[19,20],inclusive:!1},callbackname:{rules:[16,17,18],inclusive:!1},href:{rules:[13,14],inclusive:!1},click:{rules:[22,23],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,15,21,24,25,26,27,28,29,30,31,32,33,34,35,36,37],inclusive:!0}}};function v(){this.yy={}}return g.lexer=y,v.prototype=g,g.Parser=v,new v}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,17,18,19,21],i=[1,15],a=[1,16],o=[1,17],s=[1,21],c=[4,6,9,11,17,18,19,21],u={trace:function(){},yy:{},symbols_:{error:2,start:3,journey:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,title:17,section:18,taskName:19,taskData:20,open_directive:21,type_directive:22,arg_directive:23,close_directive:24,$accept:0,$end:1},terminals_:{2:"error",4:"journey",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",17:"title",18:"section",19:"taskName",20:"taskData",21:"open_directive",22:"type_directive",23:"arg_directive",24:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,1],[10,2],[10,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 3:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 7:case 8:this.$=[];break;case 11:r.setTitle(a[s].substr(6)),this.$=a[s].substr(6);break;case 12:r.addSection(a[s].substr(8)),this.$=a[s].substr(8);break;case 13:r.addTask(a[s-1],a[s]),this.$="task";break;case 15:r.parseDirective("%%{","open_directive");break;case 16:r.parseDirective(a[s],"type_directive");break;case 17:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 18:r.parseDirective("}%%","close_directive","journey")}},table:[{3:1,4:e,7:3,12:4,21:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,21:n},{13:8,22:[1,9]},{22:[2,15]},{6:[1,10],7:18,8:11,9:[1,12],10:13,11:[1,14],12:4,17:i,18:a,19:o,21:n},{1:[2,2]},{14:19,15:[1,20],24:s},t([15,24],[2,16]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:18,10:22,12:4,17:i,18:a,19:o,21:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,12]),{20:[1,23]},t(r,[2,14]),{11:[1,24]},{16:25,23:[1,26]},{11:[2,18]},t(r,[2,5]),t(r,[2,13]),t(c,[2,9]),{14:27,24:s},{24:[2,17]},{11:[1,28]},t(c,[2,10])],defaultActions:{5:[2,15],7:[2,2],21:[2,18],26:[2,17]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},l={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),21;case 1:return this.begin("type_directive"),22;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),24;case 4:return 23;case 5:case 6:break;case 7:return 11;case 8:case 9:break;case 10:return 4;case 11:return 17;case 12:return 18;case 13:return 19;case 14:return 20;case 15:return 15;case 16:return 6;case 17:return"INVALID"}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:journey\b)/i,/^(?:title\s[^#\n;]+)/i,/^(?:section\s[^#:\n;]+)/i,/^(?:[^#:\n;]+)/i,/^(?::[^#\n;]+)/i,/^(?::)/i,/^(?:$)/i,/^(?:.)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,13,14,15,16,17],inclusive:!0}}};function h(){this.yy={}}return u.lexer=l,h.prototype=u,u.Parser=h,new h}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t,e){return r.default.lang.round(i.default.parse(t)[e])}},function(t,e,n){var r=n(112),i=n(82),a=n(24);t.exports=function(t){return a(t)?r(t):i(t)}},function(t,e,n){var r;if(!r)try{r=n(0)}catch(t){}r||(r=window.d3),t.exports=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t,e,n){var a=i.default.parse(t),o=a[e],s=r.default.channel.clamp[e](o+n);return o!==s&&(a[e]=s),i.default.stringify(a)}},function(t,e,n){var r=n(210),i=n(216);t.exports=function(t,e){var n=i(t,e);return r(n)?n:void 0}},function(t,e,n){var r=n(38),i=n(212),a=n(213),o=r?r.toStringTag:void 0;t.exports=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":o&&o in Object(t)?i(t):a(t)}},function(t,e){t.exports=function(t){return t}},function(t,e){t.exports=function(t,e){return t===e||t!=t&&e!=e}},function(t,e,n){var r=n(34),i=n(11);t.exports=function(t){if(!i(t))return!1;var e=r(t);return"[object Function]"==e||"[object GeneratorFunction]"==e||"[object AsyncFunction]"==e||"[object Proxy]"==e}},function(t,e,n){var r=n(16).Symbol;t.exports=r},function(t,e,n){(function(t){var r=n(16),i=n(232),a=e&&!e.nodeType&&e,o=a&&"object"==typeof t&&t&&!t.nodeType&&t,s=o&&o.exports===a?r.Buffer:void 0,c=(s?s.isBuffer:void 0)||i;t.exports=c}).call(this,n(7)(t))},function(t,e,n){var r=n(112),i=n(236),a=n(24);t.exports=function(t){return a(t)?r(t,!0):i(t)}},function(t,e,n){var r=n(241),i=n(77),a=n(242),o=n(121),s=n(243),c=n(34),u=n(110),l=u(r),h=u(i),f=u(a),d=u(o),p=u(s),g=c;(r&&"[object DataView]"!=g(new r(new ArrayBuffer(1)))||i&&"[object Map]"!=g(new i)||a&&"[object Promise]"!=g(a.resolve())||o&&"[object Set]"!=g(new o)||s&&"[object WeakMap]"!=g(new s))&&(g=function(t){var e=c(t),n="[object Object]"==e?t.constructor:void 0,r=n?u(n):"";if(r)switch(r){case l:return"[object DataView]";case h:return"[object Map]";case f:return"[object Promise]";case d:return"[object Set]";case p:return"[object WeakMap]"}return e}),t.exports=g},function(t,e,n){var r=n(34),i=n(21);t.exports=function(t){return"symbol"==typeof t||i(t)&&"[object Symbol]"==r(t)}},function(t,e,n){var r;try{r={defaults:n(154),each:n(87),isFunction:n(37),isPlainObject:n(158),pick:n(161),has:n(93),range:n(162),uniqueId:n(163)}}catch(t){}r||(r=window._),t.exports=r},function(t){t.exports=JSON.parse('{"name":"mermaid","version":"8.9.0","description":"Markdownish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.","main":"dist/mermaid.core.js","keywords":["diagram","markdown","flowchart","sequence diagram","gantt","class diagram","git graph"],"scripts":{"build:development":"webpack --progress --colors","build:production":"yarn build:development -p --config webpack.config.prod.babel.js","build":"yarn build:development && yarn build:production","postbuild":"documentation build src/mermaidAPI.js src/config.js --shallow -f md --markdown-toc false > docs/Setup.md","build:watch":"yarn build --watch","minify":"minify ./dist/mermaid.js > ./dist/mermaid.min.js","release":"yarn build","lint":"eslint src","e2e:depr":"yarn lint && jest e2e --config e2e/jest.config.js","cypress":"percy exec -- cypress run","e2e":"start-server-and-test dev http://localhost:9000/ cypress","e2e-upd":"yarn lint && jest e2e -u --config e2e/jest.config.js","dev":"webpack-dev-server --config webpack.config.e2e.js","test":"yarn lint && jest src/.*","test:watch":"jest --watch src","prepublishOnly":"yarn build && yarn test","prepare":"yarn build"},"repository":{"type":"git","url":"https://github.com/knsv/mermaid"},"author":"Knut Sveidqvist","license":"MIT","standard":{"ignore":["**/parser/*.js","dist/**/*.js","cypress/**/*.js"],"globals":["page"]},"dependencies":{"@braintree/sanitize-url":"^3.1.0","d3":"^5.7.0","dagre":"^0.8.4","dagre-d3":"^0.6.4","entity-decode":"^2.0.2","graphlib":"^2.1.7","he":"^1.2.0","khroma":"^1.1.0","minify":"^4.1.1","moment-mini":"^2.22.1","stylis":"^3.5.2"},"devDependencies":{"@babel/core":"^7.2.2","@babel/preset-env":"^7.8.4","@babel/register":"^7.0.0","@percy/cypress":"*","babel-core":"7.0.0-bridge.0","babel-eslint":"^10.1.0","babel-jest":"^24.9.0","babel-loader":"^8.0.4","coveralls":"^3.0.2","css-loader":"^2.0.1","css-to-string-loader":"^0.1.3","cypress":"4.0.1","documentation":"^12.0.1","eslint":"^6.3.0","eslint-config-prettier":"^6.3.0","eslint-plugin-prettier":"^3.1.0","husky":"^1.2.1","identity-obj-proxy":"^3.0.0","jest":"^24.9.0","jison":"^0.4.18","moment":"^2.23.0","node-sass":"^4.12.0","prettier":"^1.18.2","puppeteer":"^1.17.0","sass-loader":"^7.1.0","start-server-and-test":"^1.10.6","terser-webpack-plugin":"^2.2.2","webpack":"^4.41.2","webpack-bundle-analyzer":"^3.7.0","webpack-cli":"^3.1.2","webpack-dev-server":"^3.4.1","webpack-node-externals":"^1.7.2","yarn-upgrade-all":"^0.5.0"},"files":["dist"],"yarn-upgrade-all":{"ignore":["babel-core"]},"sideEffects":["**/*.css","**/*.scss"],"husky":{"hooks":{"pre-push":"yarn test"}}}')},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=new(n(176).default)({r:0,g:0,b:0,a:0},"transparent");e.default=r},function(t,e,n){var r=n(58),i=n(59);t.exports=function(t,e,n,a){var o=!n;n||(n={});for(var s=-1,c=e.length;++s<c;){var u=e[s],l=a?a(n[u],t[u],u,n,t):void 0;void 0===l&&(l=t[u]),o?i(n,u,l):r(n,u,l)}return n}},function(t,e,n){var r=n(231),i=n(21),a=Object.prototype,o=a.hasOwnProperty,s=a.propertyIsEnumerable,c=r(function(){return arguments}())?r:function(t){return i(t)&&o.call(t,"callee")&&!s.call(t,"callee")};t.exports=c},function(t,e,n){var r=n(233),i=n(61),a=n(81),o=a&&a.isTypedArray,s=o?i(o):r;t.exports=s},function(t,e,n){var r=n(42);t.exports=function(t){if("string"==typeof t||r(t))return t;var e=t+"";return"0"==e&&1/t==-1/0?"-0":e}},function(t,e,n){var r=n(12);t.exports=function(t,e){var n=t.append("foreignObject").attr("width","100000"),i=n.append("xhtml:div");i.attr("xmlns","http://www.w3.org/1999/xhtml");var a=e.label;switch(typeof a){case"function":i.insert(a);break;case"object":i.insert((function(){return a}));break;default:i.html(a)}r.applyStyle(i,e.labelStyle),i.style("display","inline-block"),i.style("white-space","nowrap");var o=i.node().getBoundingClientRect();return n.attr("width",o.width).attr("height",o.height),n}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a=n(15),o=n(52);e.default=function(t,e,n,s){if(void 0===n&&(n=0),void 0===s&&(s=1),"number"!=typeof t)return o.default(t,{a:e});var c=i.default.set({r:r.default.channel.clamp.r(t),g:r.default.channel.clamp.g(e),b:r.default.channel.clamp.b(n),a:r.default.channel.clamp.a(s)});return a.default.stringify(c)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t,e){var n=i.default.parse(t);for(var a in e)n[a]=r.default.channel.clamp[a](e[a]);return i.default.stringify(n)}},function(t,e,n){var r=n(54),i=n(205),a=n(206),o=n(207),s=n(208),c=n(209);function u(t){var e=this.__data__=new r(t);this.size=e.size}u.prototype.clear=i,u.prototype.delete=a,u.prototype.get=o,u.prototype.has=s,u.prototype.set=c,t.exports=u},function(t,e,n){var r=n(200),i=n(201),a=n(202),o=n(203),s=n(204);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},function(t,e,n){var r=n(36);t.exports=function(t,e){for(var n=t.length;n--;)if(r(t[n][0],e))return n;return-1}},function(t,e,n){var r=n(33)(Object,"create");t.exports=r},function(t,e,n){var r=n(225);t.exports=function(t,e){var n=t.__data__;return r(e)?n["string"==typeof e?"string":"hash"]:n.map}},function(t,e,n){var r=n(59),i=n(36),a=Object.prototype.hasOwnProperty;t.exports=function(t,e,n){var o=t[e];a.call(t,e)&&i(o,n)&&(void 0!==n||e in t)||r(t,e,n)}},function(t,e,n){var r=n(111);t.exports=function(t,e,n){"__proto__"==e&&r?r(t,e,{configurable:!0,enumerable:!0,value:n,writable:!0}):t[e]=n}},function(t,e){var n=/^(?:0|[1-9]\d*)$/;t.exports=function(t,e){var r=typeof t;return!!(e=null==e?9007199254740991:e)&&("number"==r||"symbol"!=r&&n.test(t))&&t>-1&&t%1==0&&t<e}},function(t,e){t.exports=function(t){return function(e){return t(e)}}},function(t,e){var n=Object.prototype;t.exports=function(t){var e=t&&t.constructor;return t===("function"==typeof e&&e.prototype||n)}},function(t,e,n){var r=n(113)(Object.getPrototypeOf,Object);t.exports=r},function(t,e,n){var r=n(88),i=n(254)(r);t.exports=i},function(t,e,n){var r=n(5),i=n(92),a=n(268),o=n(135);t.exports=function(t,e){return r(t)?t:i(t,e)?[t]:a(o(t))}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=Array(r);++n<r;)i[n]=e(t[n],n,t);return i}},function(t,e,n){var r=n(35),i=n(143),a=n(144);t.exports=function(t,e){return a(i(t,e,r),t+"")}},function(t,e,n){var r=n(36),i=n(24),a=n(60),o=n(11);t.exports=function(t,e,n){if(!o(n))return!1;var s=typeof e;return!!("number"==s?i(n)&&a(e,n.length):"string"==s&&e in n)&&r(n[e],t)}},function(t,e,n){"use strict";var r=n(4);t.exports={longestPath:function(t){var e={};r.forEach(t.sources(),(function n(i){var a=t.node(i);if(r.has(e,i))return a.rank;e[i]=!0;var o=r.min(r.map(t.outEdges(i),(function(e){return n(e.w)-t.edge(e).minlen})));return o!==Number.POSITIVE_INFINITY&&null!=o||(o=0),a.rank=o}))},slack:function(t,e){return t.node(e.w).rank-t.node(e.v).rank-t.edge(e).minlen}}},function(t,e,n){"use strict";var r=/^(%20|\s)*(javascript|data)/im,i=/[^\x20-\x7E]/gim,a=/^([^:]+):/gm,o=[".","/"];t.exports={sanitizeUrl:function(t){if(!t)return"about:blank";var e,n,s=t.replace(i,"").trim();return function(t){return o.indexOf(t[0])>-1}(s)?s:(n=s.match(a))?(e=n[0],r.test(e)?"about:blank":s):"about:blank"}}},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[2,3],n=[1,7],r=[7,12,15,17,19,20,21],i=[7,11,12,15,17,19,20,21],a=[2,20],o=[1,32],s={trace:function(){},yy:{},symbols_:{error:2,start:3,GG:4,":":5,document:6,EOF:7,DIR:8,options:9,body:10,OPT:11,NL:12,line:13,statement:14,COMMIT:15,commit_arg:16,BRANCH:17,ID:18,CHECKOUT:19,MERGE:20,RESET:21,reset_arg:22,STR:23,HEAD:24,reset_parents:25,CARET:26,$accept:0,$end:1},terminals_:{2:"error",4:"GG",5:":",7:"EOF",8:"DIR",11:"OPT",12:"NL",15:"COMMIT",17:"BRANCH",18:"ID",19:"CHECKOUT",20:"MERGE",21:"RESET",23:"STR",24:"HEAD",26:"CARET"},productions_:[0,[3,4],[3,5],[6,0],[6,2],[9,2],[9,1],[10,0],[10,2],[13,2],[13,1],[14,2],[14,2],[14,2],[14,2],[14,2],[16,0],[16,1],[22,2],[22,2],[25,0],[25,2]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:return a[s-1];case 2:return r.setDirection(a[s-3]),a[s-1];case 4:r.setOptions(a[s-1]),this.$=a[s];break;case 5:a[s-1]+=a[s],this.$=a[s-1];break;case 7:this.$=[];break;case 8:a[s-1].push(a[s]),this.$=a[s-1];break;case 9:this.$=a[s-1];break;case 11:r.commit(a[s]);break;case 12:r.branch(a[s]);break;case 13:r.checkout(a[s]);break;case 14:r.merge(a[s]);break;case 15:r.reset(a[s]);break;case 16:this.$="";break;case 17:this.$=a[s];break;case 18:this.$=a[s-1]+":"+a[s];break;case 19:this.$=a[s-1]+":"+r.count,r.count=0;break;case 20:r.count=0;break;case 21:r.count+=1}},table:[{3:1,4:[1,2]},{1:[3]},{5:[1,3],8:[1,4]},{6:5,7:e,9:6,12:n},{5:[1,8]},{7:[1,9]},t(r,[2,7],{10:10,11:[1,11]}),t(i,[2,6]),{6:12,7:e,9:6,12:n},{1:[2,1]},{7:[2,4],12:[1,15],13:13,14:14,15:[1,16],17:[1,17],19:[1,18],20:[1,19],21:[1,20]},t(i,[2,5]),{7:[1,21]},t(r,[2,8]),{12:[1,22]},t(r,[2,10]),{12:[2,16],16:23,23:[1,24]},{18:[1,25]},{18:[1,26]},{18:[1,27]},{18:[1,30],22:28,24:[1,29]},{1:[2,2]},t(r,[2,9]),{12:[2,11]},{12:[2,17]},{12:[2,12]},{12:[2,13]},{12:[2,14]},{12:[2,15]},{12:a,25:31,26:o},{12:a,25:33,26:o},{12:[2,18]},{12:a,25:34,26:o},{12:[2,19]},{12:[2,21]}],defaultActions:{9:[2,1],21:[2,2],23:[2,11],24:[2,17],25:[2,12],26:[2,13],27:[2,14],28:[2,15],31:[2,18],33:[2,19],34:[2,21]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},c={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 12;case 1:case 2:case 3:break;case 4:return 4;case 5:return 15;case 6:return 17;case 7:return 20;case 8:return 21;case 9:return 19;case 10:case 11:return 8;case 12:return 5;case 13:return 26;case 14:this.begin("options");break;case 15:this.popState();break;case 16:return 11;case 17:this.begin("string");break;case 18:this.popState();break;case 19:return 23;case 20:return 18;case 21:return 7}},rules:[/^(?:(\r?\n)+)/i,/^(?:\s+)/i,/^(?:#[^\n]*)/i,/^(?:%[^\n]*)/i,/^(?:gitGraph\b)/i,/^(?:commit\b)/i,/^(?:branch\b)/i,/^(?:merge\b)/i,/^(?:reset\b)/i,/^(?:checkout\b)/i,/^(?:LR\b)/i,/^(?:BT\b)/i,/^(?::)/i,/^(?:\^)/i,/^(?:options\r?\n)/i,/^(?:end\r?\n)/i,/^(?:[^\n]+\r?\n)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:[a-zA-Z][-_\.a-zA-Z0-9]*[-_a-zA-Z0-9])/i,/^(?:$)/i],conditions:{options:{rules:[15,16],inclusive:!1},string:{rules:[18,19],inclusive:!1},INITIAL:{rules:[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,17,20,21],inclusive:!0}}};function u(){this.yy={}}return s.lexer=c,u.prototype=s,s.Parser=u,new u}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[6,9,10],n={trace:function(){},yy:{},symbols_:{error:2,start:3,info:4,document:5,EOF:6,line:7,statement:8,NL:9,showInfo:10,$accept:0,$end:1},terminals_:{2:"error",4:"info",6:"EOF",9:"NL",10:"showInfo"},productions_:[0,[3,3],[5,0],[5,2],[7,1],[7,1],[8,1]],performAction:function(t,e,n,r,i,a,o){a.length;switch(i){case 1:return r;case 4:break;case 6:r.setInfo(!0)}},table:[{3:1,4:[1,2]},{1:[3]},t(e,[2,2],{5:3}),{6:[1,4],7:5,8:6,9:[1,7],10:[1,8]},{1:[2,1]},t(e,[2,3]),t(e,[2,4]),t(e,[2,5]),t(e,[2,6])],defaultActions:{4:[2,1]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},r={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return 4;case 1:return 9;case 2:return"space";case 3:return 10;case 4:return 6;case 5:return"TXT"}},rules:[/^(?:info\b)/i,/^(?:[\s\n\r]+)/i,/^(?:[\s]+)/i,/^(?:showInfo\b)/i,/^(?:$)/i,/^(?:.)/i],conditions:{INITIAL:{rules:[0,1,2,3,4,5],inclusive:!0}}};function i(){this.yy={}}return n.lexer=r,i.prototype=n,n.Parser=i,new i}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,4],n=[1,5],r=[1,6],i=[1,7],a=[1,9],o=[1,10,12,19,20,21,22],s=[1,6,10,12,19,20,21,22],c=[19,20,21],u=[1,22],l=[6,19,20,21,22],h={trace:function(){},yy:{},symbols_:{error:2,start:3,eol:4,directive:5,PIE:6,document:7,line:8,statement:9,txt:10,value:11,title:12,title_value:13,openDirective:14,typeDirective:15,closeDirective:16,":":17,argDirective:18,NEWLINE:19,";":20,EOF:21,open_directive:22,type_directive:23,arg_directive:24,close_directive:25,$accept:0,$end:1},terminals_:{2:"error",6:"PIE",10:"txt",11:"value",12:"title",13:"title_value",17:":",19:"NEWLINE",20:";",21:"EOF",22:"open_directive",23:"type_directive",24:"arg_directive",25:"close_directive"},productions_:[0,[3,2],[3,2],[3,2],[7,0],[7,2],[8,2],[9,0],[9,2],[9,2],[9,1],[5,3],[5,5],[4,1],[4,1],[4,1],[14,1],[15,1],[18,1],[16,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 6:this.$=a[s-1];break;case 8:r.addSection(a[s-1],r.cleanupValue(a[s]));break;case 9:this.$=a[s].trim(),r.setTitle(this.$);break;case 16:r.parseDirective("%%{","open_directive");break;case 17:r.parseDirective(a[s],"type_directive");break;case 18:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 19:r.parseDirective("}%%","close_directive","pie")}},table:[{3:1,4:2,5:3,6:e,14:8,19:n,20:r,21:i,22:a},{1:[3]},{3:10,4:2,5:3,6:e,14:8,19:n,20:r,21:i,22:a},{3:11,4:2,5:3,6:e,14:8,19:n,20:r,21:i,22:a},t(o,[2,4],{7:12}),t(s,[2,13]),t(s,[2,14]),t(s,[2,15]),{15:13,23:[1,14]},{23:[2,16]},{1:[2,1]},{1:[2,2]},t(c,[2,7],{14:8,8:15,9:16,5:19,1:[2,3],10:[1,17],12:[1,18],22:a}),{16:20,17:[1,21],25:u},t([17,25],[2,17]),t(o,[2,5]),{4:23,19:n,20:r,21:i},{11:[1,24]},{13:[1,25]},t(c,[2,10]),t(l,[2,11]),{18:26,24:[1,27]},t(l,[2,19]),t(o,[2,6]),t(c,[2,8]),t(c,[2,9]),{16:28,25:u},{25:[2,18]},t(l,[2,12])],defaultActions:{9:[2,16],10:[2,1],11:[2,2],27:[2,18]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},f={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),22;case 1:return this.begin("type_directive"),23;case 2:return this.popState(),this.begin("arg_directive"),17;case 3:return this.popState(),this.popState(),25;case 4:return 24;case 5:case 6:break;case 7:return 19;case 8:case 9:break;case 10:return this.begin("title"),12;case 11:return this.popState(),"title_value";case 12:this.begin("string");break;case 13:this.popState();break;case 14:return"txt";case 15:return 6;case 16:return"value";case 17:return 21}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n\r]+)/i,/^(?:%%[^\n]*)/i,/^(?:[\s]+)/i,/^(?:title\b)/i,/^(?:(?!\n||)*[^\n]*)/i,/^(?:["])/i,/^(?:["])/i,/^(?:[^"]*)/i,/^(?:pie\b)/i,/^(?::[\s]*[\d]+(?:\.[\d]+)?)/i,/^(?:$)/i],conditions:{close_directive:{rules:[],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},open_directive:{rules:[1],inclusive:!1},title:{rules:[11],inclusive:!1},string:{rules:[13,14],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,12,15,16,17],inclusive:!0}}};function d(){this.yy={}}return h.lexer=f,d.prototype=h,h.Parser=d,new d}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){(function(t,r){var i=function(){var t=function(t,e,n,r){for(n=n||{},r=t.length;r--;n[t[r]]=e);return n},e=[1,2],n=[1,5],r=[6,9,11,23,37],i=[1,17],a=[1,20],o=[1,25],s=[1,26],c=[1,27],u=[1,28],l=[1,37],h=[23,34,35],f=[4,6,9,11,23,37],d=[30,31,32,33],p=[22,27],g={trace:function(){},yy:{},symbols_:{error:2,start:3,ER_DIAGRAM:4,document:5,EOF:6,directive:7,line:8,SPACE:9,statement:10,NEWLINE:11,openDirective:12,typeDirective:13,closeDirective:14,":":15,argDirective:16,entityName:17,relSpec:18,role:19,BLOCK_START:20,attributes:21,BLOCK_STOP:22,ALPHANUM:23,attribute:24,attributeType:25,attributeName:26,ATTRIBUTE_WORD:27,cardinality:28,relType:29,ZERO_OR_ONE:30,ZERO_OR_MORE:31,ONE_OR_MORE:32,ONLY_ONE:33,NON_IDENTIFYING:34,IDENTIFYING:35,WORD:36,open_directive:37,type_directive:38,arg_directive:39,close_directive:40,$accept:0,$end:1},terminals_:{2:"error",4:"ER_DIAGRAM",6:"EOF",9:"SPACE",11:"NEWLINE",15:":",20:"BLOCK_START",22:"BLOCK_STOP",23:"ALPHANUM",27:"ATTRIBUTE_WORD",30:"ZERO_OR_ONE",31:"ZERO_OR_MORE",32:"ONE_OR_MORE",33:"ONLY_ONE",34:"NON_IDENTIFYING",35:"IDENTIFYING",36:"WORD",37:"open_directive",38:"type_directive",39:"arg_directive",40:"close_directive"},productions_:[0,[3,3],[3,2],[5,0],[5,2],[8,2],[8,1],[8,1],[8,1],[7,4],[7,6],[10,1],[10,5],[10,4],[10,3],[10,1],[17,1],[21,1],[21,2],[24,2],[25,1],[26,1],[18,3],[28,1],[28,1],[28,1],[28,1],[29,1],[29,1],[19,1],[19,1],[12,1],[13,1],[16,1],[14,1]],performAction:function(t,e,n,r,i,a,o){var s=a.length-1;switch(i){case 1:break;case 3:this.$=[];break;case 4:a[s-1].push(a[s]),this.$=a[s-1];break;case 5:case 6:this.$=a[s];break;case 7:case 8:this.$=[];break;case 12:r.addEntity(a[s-4]),r.addEntity(a[s-2]),r.addRelationship(a[s-4],a[s],a[s-2],a[s-3]);break;case 13:r.addEntity(a[s-3]),r.addAttributes(a[s-3],a[s-1]);break;case 14:r.addEntity(a[s-2]);break;case 15:r.addEntity(a[s]);break;case 16:this.$=a[s];break;case 17:this.$=[a[s]];break;case 18:a[s].push(a[s-1]),this.$=a[s];break;case 19:this.$={attributeType:a[s-1],attributeName:a[s]};break;case 20:case 21:this.$=a[s];break;case 22:this.$={cardA:a[s],relType:a[s-1],cardB:a[s-2]};break;case 23:this.$=r.Cardinality.ZERO_OR_ONE;break;case 24:this.$=r.Cardinality.ZERO_OR_MORE;break;case 25:this.$=r.Cardinality.ONE_OR_MORE;break;case 26:this.$=r.Cardinality.ONLY_ONE;break;case 27:this.$=r.Identification.NON_IDENTIFYING;break;case 28:this.$=r.Identification.IDENTIFYING;break;case 29:this.$=a[s].replace(/"/g,"");break;case 30:this.$=a[s];break;case 31:r.parseDirective("%%{","open_directive");break;case 32:r.parseDirective(a[s],"type_directive");break;case 33:a[s]=a[s].trim().replace(/'/g,'"'),r.parseDirective(a[s],"arg_directive");break;case 34:r.parseDirective("}%%","close_directive","er")}},table:[{3:1,4:e,7:3,12:4,37:n},{1:[3]},t(r,[2,3],{5:6}),{3:7,4:e,7:3,12:4,37:n},{13:8,38:[1,9]},{38:[2,31]},{6:[1,10],7:15,8:11,9:[1,12],10:13,11:[1,14],12:4,17:16,23:i,37:n},{1:[2,2]},{14:18,15:[1,19],40:a},t([15,40],[2,32]),t(r,[2,8],{1:[2,1]}),t(r,[2,4]),{7:15,10:21,12:4,17:16,23:i,37:n},t(r,[2,6]),t(r,[2,7]),t(r,[2,11]),t(r,[2,15],{18:22,28:24,20:[1,23],30:o,31:s,32:c,33:u}),t([6,9,11,15,20,23,30,31,32,33,37],[2,16]),{11:[1,29]},{16:30,39:[1,31]},{11:[2,34]},t(r,[2,5]),{17:32,23:i},{21:33,22:[1,34],24:35,25:36,27:l},{29:38,34:[1,39],35:[1,40]},t(h,[2,23]),t(h,[2,24]),t(h,[2,25]),t(h,[2,26]),t(f,[2,9]),{14:41,40:a},{40:[2,33]},{15:[1,42]},{22:[1,43]},t(r,[2,14]),{21:44,22:[2,17],24:35,25:36,27:l},{26:45,27:[1,46]},{27:[2,20]},{28:47,30:o,31:s,32:c,33:u},t(d,[2,27]),t(d,[2,28]),{11:[1,48]},{19:49,23:[1,51],36:[1,50]},t(r,[2,13]),{22:[2,18]},t(p,[2,19]),t(p,[2,21]),{23:[2,22]},t(f,[2,10]),t(r,[2,12]),t(r,[2,29]),t(r,[2,30])],defaultActions:{5:[2,31],7:[2,2],20:[2,34],31:[2,33],37:[2,20],44:[2,18],47:[2,22]},parseError:function(t,e){if(!e.recoverable){var n=new Error(t);throw n.hash=e,n}this.trace(t)},parse:function(t){var e=this,n=[0],r=[],i=[null],a=[],o=this.table,s="",c=0,u=0,l=0,h=2,f=1,d=a.slice.call(arguments,1),p=Object.create(this.lexer),g={yy:{}};for(var y in this.yy)Object.prototype.hasOwnProperty.call(this.yy,y)&&(g.yy[y]=this.yy[y]);p.setInput(t,g.yy),g.yy.lexer=p,g.yy.parser=this,void 0===p.yylloc&&(p.yylloc={});var v=p.yylloc;a.push(v);var m=p.options&&p.options.ranges;function b(){var t;return"number"!=typeof(t=r.pop()||p.lex()||f)&&(t instanceof Array&&(t=(r=t).pop()),t=e.symbols_[t]||t),t}"function"==typeof g.yy.parseError?this.parseError=g.yy.parseError:this.parseError=Object.getPrototypeOf(this).parseError;for(var x,_,k,w,E,T,C,A,S,M={};;){if(k=n[n.length-1],this.defaultActions[k]?w=this.defaultActions[k]:(null==x&&(x=b()),w=o[k]&&o[k][x]),void 0===w||!w.length||!w[0]){var O="";for(T in S=[],o[k])this.terminals_[T]&&T>h&&S.push("'"+this.terminals_[T]+"'");O=p.showPosition?"Parse error on line "+(c+1)+":\n"+p.showPosition()+"\nExpecting "+S.join(", ")+", got '"+(this.terminals_[x]||x)+"'":"Parse error on line "+(c+1)+": Unexpected "+(x==f?"end of input":"'"+(this.terminals_[x]||x)+"'"),this.parseError(O,{text:p.match,token:this.terminals_[x]||x,line:p.yylineno,loc:v,expected:S})}if(w[0]instanceof Array&&w.length>1)throw new Error("Parse Error: multiple actions possible at state: "+k+", token: "+x);switch(w[0]){case 1:n.push(x),i.push(p.yytext),a.push(p.yylloc),n.push(w[1]),x=null,_?(x=_,_=null):(u=p.yyleng,s=p.yytext,c=p.yylineno,v=p.yylloc,l>0&&l--);break;case 2:if(C=this.productions_[w[1]][1],M.$=i[i.length-C],M._$={first_line:a[a.length-(C||1)].first_line,last_line:a[a.length-1].last_line,first_column:a[a.length-(C||1)].first_column,last_column:a[a.length-1].last_column},m&&(M._$.range=[a[a.length-(C||1)].range[0],a[a.length-1].range[1]]),void 0!==(E=this.performAction.apply(M,[s,u,c,g.yy,w[1],i,a].concat(d))))return E;C&&(n=n.slice(0,-1*C*2),i=i.slice(0,-1*C),a=a.slice(0,-1*C)),n.push(this.productions_[w[1]][0]),i.push(M.$),a.push(M._$),A=o[n[n.length-2]][n[n.length-1]],n.push(A);break;case 3:return!0}}return!0}},y={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t,e){return this.yy=e||this.yy||{},this._input=t,this._more=this._backtrack=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,n=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e),this.offset-=e;var r=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),n.length-1&&(this.yylineno-=n.length-1);var i=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:n?(n.length===r.length?this.yylloc.first_column:0)+r[r.length-n.length].length-n[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[i[0],i[0]+this.yyleng-e]),this.yyleng=this.yytext.length,this},more:function(){return this._more=!0,this},reject:function(){return this.options.backtrack_lexer?(this._backtrack=!0,this):this.parseError("Lexical error on line "+(this.yylineno+1)+". You can only invoke reject() in the lexer when the lexer is of the backtracking persuasion (options.backtrack_lexer = true).\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},test_match:function(t,e){var n,r,i;if(this.options.backtrack_lexer&&(i={yylineno:this.yylineno,yylloc:{first_line:this.yylloc.first_line,last_line:this.last_line,first_column:this.yylloc.first_column,last_column:this.yylloc.last_column},yytext:this.yytext,match:this.match,matches:this.matches,matched:this.matched,yyleng:this.yyleng,offset:this.offset,_more:this._more,_input:this._input,yy:this.yy,conditionStack:this.conditionStack.slice(0),done:this.done},this.options.ranges&&(i.yylloc.range=this.yylloc.range.slice(0))),(r=t[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=r.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:r?r[r.length-1].length-r[r.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+t[0].length},this.yytext+=t[0],this.match+=t[0],this.matches=t,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._backtrack=!1,this._input=this._input.slice(t[0].length),this.matched+=t[0],n=this.performAction.call(this,this.yy,this,e,this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),n)return n;if(this._backtrack){for(var a in i)this[a]=i[a];return!1}return!1},next:function(){if(this.done)return this.EOF;var t,e,n,r;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var i=this._currentRules(),a=0;a<i.length;a++)if((n=this._input.match(this.rules[i[a]]))&&(!e||n[0].length>e[0].length)){if(e=n,r=a,this.options.backtrack_lexer){if(!1!==(t=this.test_match(n,i[a])))return t;if(this._backtrack){e=!1;continue}return!1}if(!this.options.flex)break}return e?!1!==(t=this.test_match(e,i[r]))&&t:""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return t||this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.length-1>0?this.conditionStack.pop():this.conditionStack[0]},_currentRules:function(){return this.conditionStack.length&&this.conditionStack[this.conditionStack.length-1]?this.conditions[this.conditionStack[this.conditionStack.length-1]].rules:this.conditions.INITIAL.rules},topState:function(t){return(t=this.conditionStack.length-1-Math.abs(t||0))>=0?this.conditionStack[t]:"INITIAL"},pushState:function(t){this.begin(t)},stateStackSize:function(){return this.conditionStack.length},options:{"case-insensitive":!0},performAction:function(t,e,n,r){switch(n){case 0:return this.begin("open_directive"),37;case 1:return this.begin("type_directive"),38;case 2:return this.popState(),this.begin("arg_directive"),15;case 3:return this.popState(),this.popState(),40;case 4:return 39;case 5:case 6:break;case 7:return 11;case 8:break;case 9:return 9;case 10:return 36;case 11:return 4;case 12:return this.begin("block"),20;case 13:break;case 14:return 27;case 15:break;case 16:return this.popState(),22;case 17:return e.yytext[0];case 18:return 30;case 19:return 31;case 20:return 32;case 21:return 33;case 22:return 30;case 23:return 31;case 24:return 32;case 25:return 34;case 26:return 35;case 27:case 28:return 34;case 29:return 23;case 30:return e.yytext[0];case 31:return 6}},rules:[/^(?:%%\{)/i,/^(?:((?:(?!\}%%)[^:.])*))/i,/^(?::)/i,/^(?:\}%%)/i,/^(?:((?:(?!\}%%).|\n)*))/i,/^(?:%(?!\{)[^\n]*)/i,/^(?:[^\}]%%[^\n]*)/i,/^(?:[\n]+)/i,/^(?:\s+)/i,/^(?:[\s]+)/i,/^(?:"[^"]*")/i,/^(?:erDiagram\b)/i,/^(?:\{)/i,/^(?:\s+)/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:[\n]+)/i,/^(?:\})/i,/^(?:.)/i,/^(?:\|o\b)/i,/^(?:\}o\b)/i,/^(?:\}\|)/i,/^(?:\|\|)/i,/^(?:o\|)/i,/^(?:o\{)/i,/^(?:\|\{)/i,/^(?:\.\.)/i,/^(?:--)/i,/^(?:\.-)/i,/^(?:-\.)/i,/^(?:[A-Za-z][A-Za-z0-9\-_]*)/i,/^(?:.)/i,/^(?:$)/i],conditions:{open_directive:{rules:[1],inclusive:!1},type_directive:{rules:[2,3],inclusive:!1},arg_directive:{rules:[3,4],inclusive:!1},block:{rules:[13,14,15,16,17],inclusive:!1},INITIAL:{rules:[0,5,6,7,8,9,10,11,12,18,19,20,21,22,23,24,25,26,27,28,29,30,31],inclusive:!0}}};function v(){this.yy={}}return g.lexer=y,v.prototype=g,g.Parser=v,new v}();e.parser=i,e.Parser=i.Parser,e.parse=function(){return i.parse.apply(i,arguments)},e.main=function(r){r[1]||(console.log("Usage: "+r[0]+" FILE"),t.exit(1));var i=n(19).readFileSync(n(20).normalize(r[1]),"utf8");return e.parser.parse(i)},n.c[n.s]===r&&e.main(t.argv.slice(1))}).call(this,n(14),n(7)(t))},function(t,e,n){"use strict";var r;Object.defineProperty(e,"__esModule",{value:!0}),function(t){t[t.ALL=0]="ALL",t[t.RGB=1]="RGB",t[t.HSL=2]="HSL"}(r||(r={})),e.TYPE=r},function(t,e,n){"use strict";var r=n(10);t.exports=i;function i(t){this._isDirected=!r.has(t,"directed")||t.directed,this._isMultigraph=!!r.has(t,"multigraph")&&t.multigraph,this._isCompound=!!r.has(t,"compound")&&t.compound,this._label=void 0,this._defaultNodeLabelFn=r.constant(void 0),this._defaultEdgeLabelFn=r.constant(void 0),this._nodes={},this._isCompound&&(this._parent={},this._children={},this._children["\0"]={}),this._in={},this._preds={},this._out={},this._sucs={},this._edgeObjs={},this._edgeLabels={}}function a(t,e){t[e]?t[e]++:t[e]=1}function o(t,e){--t[e]||delete t[e]}function s(t,e,n,i){var a=""+e,o=""+n;if(!t&&a>o){var s=a;a=o,o=s}return a+""+o+""+(r.isUndefined(i)?"\0":i)}function c(t,e,n,r){var i=""+e,a=""+n;if(!t&&i>a){var o=i;i=a,a=o}var s={v:i,w:a};return r&&(s.name=r),s}function u(t,e){return s(t,e.v,e.w,e.name)}i.prototype._nodeCount=0,i.prototype._edgeCount=0,i.prototype.isDirected=function(){return this._isDirected},i.prototype.isMultigraph=function(){return this._isMultigraph},i.prototype.isCompound=function(){return this._isCompound},i.prototype.setGraph=function(t){return this._label=t,this},i.prototype.graph=function(){return this._label},i.prototype.setDefaultNodeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultNodeLabelFn=t,this},i.prototype.nodeCount=function(){return this._nodeCount},i.prototype.nodes=function(){return r.keys(this._nodes)},i.prototype.sources=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._in[e])}))},i.prototype.sinks=function(){var t=this;return r.filter(this.nodes(),(function(e){return r.isEmpty(t._out[e])}))},i.prototype.setNodes=function(t,e){var n=arguments,i=this;return r.each(t,(function(t){n.length>1?i.setNode(t,e):i.setNode(t)})),this},i.prototype.setNode=function(t,e){return r.has(this._nodes,t)?(arguments.length>1&&(this._nodes[t]=e),this):(this._nodes[t]=arguments.length>1?e:this._defaultNodeLabelFn(t),this._isCompound&&(this._parent[t]="\0",this._children[t]={},this._children["\0"][t]=!0),this._in[t]={},this._preds[t]={},this._out[t]={},this._sucs[t]={},++this._nodeCount,this)},i.prototype.node=function(t){return this._nodes[t]},i.prototype.hasNode=function(t){return r.has(this._nodes,t)},i.prototype.removeNode=function(t){var e=this;if(r.has(this._nodes,t)){var n=function(t){e.removeEdge(e._edgeObjs[t])};delete this._nodes[t],this._isCompound&&(this._removeFromParentsChildList(t),delete this._parent[t],r.each(this.children(t),(function(t){e.setParent(t)})),delete this._children[t]),r.each(r.keys(this._in[t]),n),delete this._in[t],delete this._preds[t],r.each(r.keys(this._out[t]),n),delete this._out[t],delete this._sucs[t],--this._nodeCount}return this},i.prototype.setParent=function(t,e){if(!this._isCompound)throw new Error("Cannot set parent in a non-compound graph");if(r.isUndefined(e))e="\0";else{for(var n=e+="";!r.isUndefined(n);n=this.parent(n))if(n===t)throw new Error("Setting "+e+" as parent of "+t+" would create a cycle");this.setNode(e)}return this.setNode(t),this._removeFromParentsChildList(t),this._parent[t]=e,this._children[e][t]=!0,this},i.prototype._removeFromParentsChildList=function(t){delete this._children[this._parent[t]][t]},i.prototype.parent=function(t){if(this._isCompound){var e=this._parent[t];if("\0"!==e)return e}},i.prototype.children=function(t){if(r.isUndefined(t)&&(t="\0"),this._isCompound){var e=this._children[t];if(e)return r.keys(e)}else{if("\0"===t)return this.nodes();if(this.hasNode(t))return[]}},i.prototype.predecessors=function(t){var e=this._preds[t];if(e)return r.keys(e)},i.prototype.successors=function(t){var e=this._sucs[t];if(e)return r.keys(e)},i.prototype.neighbors=function(t){var e=this.predecessors(t);if(e)return r.union(e,this.successors(t))},i.prototype.isLeaf=function(t){return 0===(this.isDirected()?this.successors(t):this.neighbors(t)).length},i.prototype.filterNodes=function(t){var e=new this.constructor({directed:this._isDirected,multigraph:this._isMultigraph,compound:this._isCompound});e.setGraph(this.graph());var n=this;r.each(this._nodes,(function(n,r){t(r)&&e.setNode(r,n)})),r.each(this._edgeObjs,(function(t){e.hasNode(t.v)&&e.hasNode(t.w)&&e.setEdge(t,n.edge(t))}));var i={};return this._isCompound&&r.each(e.nodes(),(function(t){e.setParent(t,function t(r){var a=n.parent(r);return void 0===a||e.hasNode(a)?(i[r]=a,a):a in i?i[a]:t(a)}(t))})),e},i.prototype.setDefaultEdgeLabel=function(t){return r.isFunction(t)||(t=r.constant(t)),this._defaultEdgeLabelFn=t,this},i.prototype.edgeCount=function(){return this._edgeCount},i.prototype.edges=function(){return r.values(this._edgeObjs)},i.prototype.setPath=function(t,e){var n=this,i=arguments;return r.reduce(t,(function(t,r){return i.length>1?n.setEdge(t,r,e):n.setEdge(t,r),r})),this},i.prototype.setEdge=function(){var t,e,n,i,o=!1,u=arguments[0];"object"==typeof u&&null!==u&&"v"in u?(t=u.v,e=u.w,n=u.name,2===arguments.length&&(i=arguments[1],o=!0)):(t=u,e=arguments[1],n=arguments[3],arguments.length>2&&(i=arguments[2],o=!0)),t=""+t,e=""+e,r.isUndefined(n)||(n=""+n);var l=s(this._isDirected,t,e,n);if(r.has(this._edgeLabels,l))return o&&(this._edgeLabels[l]=i),this;if(!r.isUndefined(n)&&!this._isMultigraph)throw new Error("Cannot set a named edge when isMultigraph = false");this.setNode(t),this.setNode(e),this._edgeLabels[l]=o?i:this._defaultEdgeLabelFn(t,e,n);var h=c(this._isDirected,t,e,n);return t=h.v,e=h.w,Object.freeze(h),this._edgeObjs[l]=h,a(this._preds[e],t),a(this._sucs[t],e),this._in[e][l]=h,this._out[t][l]=h,this._edgeCount++,this},i.prototype.edge=function(t,e,n){var r=1===arguments.length?u(this._isDirected,arguments[0]):s(this._isDirected,t,e,n);return this._edgeLabels[r]},i.prototype.hasEdge=function(t,e,n){var i=1===arguments.length?u(this._isDirected,arguments[0]):s(this._isDirected,t,e,n);return r.has(this._edgeLabels,i)},i.prototype.removeEdge=function(t,e,n){var r=1===arguments.length?u(this._isDirected,arguments[0]):s(this._isDirected,t,e,n),i=this._edgeObjs[r];return i&&(t=i.v,e=i.w,delete this._edgeLabels[r],delete this._edgeObjs[r],o(this._preds[e],t),o(this._sucs[t],e),delete this._in[e][r],delete this._out[t][r],this._edgeCount--),this},i.prototype.inEdges=function(t,e){var n=this._in[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.v===e})):i}},i.prototype.outEdges=function(t,e){var n=this._out[t];if(n){var i=r.values(n);return e?r.filter(i,(function(t){return t.w===e})):i}},i.prototype.nodeEdges=function(t,e){var n=this.inEdges(t,e);if(n)return n.concat(this.outEdges(t,e))}},function(t,e,n){var r=n(33)(n(16),"Map");t.exports=r},function(t,e,n){var r=n(217),i=n(224),a=n(226),o=n(227),s=n(228);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r&&!1!==e(t[n],n,t););return t}},function(t,e){t.exports=function(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=9007199254740991}},function(t,e,n){(function(t){var r=n(109),i=e&&!e.nodeType&&e,a=i&&"object"==typeof t&&t&&!t.nodeType&&t,o=a&&a.exports===i&&r.process,s=function(){try{var t=a&&a.require&&a.require("util").types;return t||o&&o.binding&&o.binding("util")}catch(t){}}();t.exports=s}).call(this,n(7)(t))},function(t,e,n){var r=n(62),i=n(234),a=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return i(t);var e=[];for(var n in Object(t))a.call(t,n)&&"constructor"!=n&&e.push(n);return e}},function(t,e,n){var r=n(116),i=n(117),a=Object.prototype.propertyIsEnumerable,o=Object.getOwnPropertySymbols,s=o?function(t){return null==t?[]:(t=Object(t),r(o(t),(function(e){return a.call(t,e)})))}:i;t.exports=s},function(t,e){t.exports=function(t,e){for(var n=-1,r=e.length,i=t.length;++n<r;)t[i+n]=e[n];return t}},function(t,e,n){var r=n(122);t.exports=function(t){var e=new t.constructor(t.byteLength);return new r(e).set(new r(t)),e}},function(t,e){t.exports=function(t){return function(){return t}}},function(t,e,n){t.exports=n(126)},function(t,e,n){var r=n(89),i=n(30);t.exports=function(t,e){return t&&r(t,e,i)}},function(t,e,n){var r=n(253)();t.exports=r},function(t,e){t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t){n[++e]=t})),n}},function(t,e,n){var r=n(65),i=n(49);t.exports=function(t,e){for(var n=0,a=(e=r(e,t)).length;null!=t&&n<a;)t=t[i(e[n++])];return n&&n==a?t:void 0}},function(t,e,n){var r=n(5),i=n(42),a=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,o=/^\w*$/;t.exports=function(t,e){if(r(t))return!1;var n=typeof t;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=t&&!i(t))||(o.test(t)||!a.test(t)||null!=e&&t in Object(e))}},function(t,e,n){var r=n(275),i=n(137);t.exports=function(t,e){return null!=t&&i(t,e,r)}},function(t,e,n){var r=n(84),i=n(287);t.exports=function t(e,n,a,o,s){var c=-1,u=e.length;for(a||(a=i),s||(s=[]);++c<u;){var l=e[c];n>0&&a(l)?n>1?t(l,n-1,a,o,s):r(s,l):o||(s[s.length]=l)}return s}},function(t,e,n){var r=n(42);t.exports=function(t,e,n){for(var i=-1,a=t.length;++i<a;){var o=t[i],s=e(o);if(null!=s&&(void 0===c?s==s&&!r(s):n(s,c)))var c=s,u=o}return u}},function(t,e){t.exports=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);r.y<a&&(l=-l);return{x:i+u,y:a+l}}},function(t,e,n){var r=n(372),i=n(50),a=n(373);t.exports=function(t,e,n){var o=e.label,s=t.append("g");"svg"===e.labelType?a(s,e):"string"!=typeof o||"html"===e.labelType?i(s,e):r(s,e);var c,u=s.node().getBBox();switch(n){case"top":c=-e.height/2;break;case"bottom":c=e.height/2-u.height;break;default:c=-u.height/2}return s.attr("transform","translate("+-u.width/2+","+c+")"),s}},function(t,e){},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a=n(178),o={re:/^#((?:[a-f0-9]{2}){2,4}|[a-f0-9]{3})$/i,parse:function(t){if(35===t.charCodeAt(0)){var e=t.match(o.re);if(e){var n=e[1],r=parseInt(n,16),a=n.length,s=a%4==0,c=a>4,u=c?1:17,l=c?8:4,h=s?0:-1,f=c?255:15;return i.default.set({r:(r>>l*(h+3)&f)*u,g:(r>>l*(h+2)&f)*u,b:(r>>l*(h+1)&f)*u,a:s?(r&f)*u/255:1},t)}}},stringify:function(t){return t.a<1?"#"+a.DEC2HEX[Math.round(t.r)]+a.DEC2HEX[Math.round(t.g)]+a.DEC2HEX[Math.round(t.b)]+r.default.unit.frac2hex(t.a):"#"+a.DEC2HEX[Math.round(t.r)]+a.DEC2HEX[Math.round(t.g)]+a.DEC2HEX[Math.round(t.b)]}};e.default=o},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a=n(15);e.default=function(t,e,n,o){void 0===o&&(o=1);var s=i.default.set({h:r.default.channel.clamp.h(t),s:r.default.channel.clamp.s(e),l:r.default.channel.clamp.l(n),a:r.default.channel.clamp.a(o)});return a.default.stringify(s)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"a")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15);e.default=function(t){var e=i.default.parse(t),n=e.r,a=e.g,o=e.b,s=.2126*r.default.channel.toLinear(n)+.7152*r.default.channel.toLinear(a)+.0722*r.default.channel.toLinear(o);return r.default.lang.round(s)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(102);e.default=function(t){return r.default(t)>=.5}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"a",e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"a",-e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15),i=n(52);e.default=function(t,e){var n=r.default.parse(t),a={};for(var o in e)e[o]&&(a[o]=n[o]+e[o]);return i.default(t,a)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15),i=n(51);e.default=function(t,e,n){void 0===n&&(n=50);var a=r.default.parse(t),o=a.r,s=a.g,c=a.b,u=a.a,l=r.default.parse(e),h=l.r,f=l.g,d=l.b,p=l.a,g=n/100,y=2*g-1,v=u-p,m=((y*v==-1?y:(y+v)/(1+y*v))+1)/2,b=1-m,x=o*m+h*b,_=s*m+f*b,k=c*m+d*b,w=u*g+p*(1-g);return i.default(x,_,k,w)}},function(t,e,n){var r=n(53),i=n(79),a=n(58),o=n(229),s=n(235),c=n(114),u=n(115),l=n(238),h=n(239),f=n(119),d=n(240),p=n(41),g=n(244),y=n(245),v=n(124),m=n(5),b=n(39),x=n(249),_=n(11),k=n(251),w=n(30),E={};E["[object Arguments]"]=E["[object Array]"]=E["[object ArrayBuffer]"]=E["[object DataView]"]=E["[object Boolean]"]=E["[object Date]"]=E["[object Float32Array]"]=E["[object Float64Array]"]=E["[object Int8Array]"]=E["[object Int16Array]"]=E["[object Int32Array]"]=E["[object Map]"]=E["[object Number]"]=E["[object Object]"]=E["[object RegExp]"]=E["[object Set]"]=E["[object String]"]=E["[object Symbol]"]=E["[object Uint8Array]"]=E["[object Uint8ClampedArray]"]=E["[object Uint16Array]"]=E["[object Uint32Array]"]=!0,E["[object Error]"]=E["[object Function]"]=E["[object WeakMap]"]=!1,t.exports=function t(e,n,T,C,A,S){var M,O=1&n,D=2&n,N=4&n;if(T&&(M=A?T(e,C,A,S):T(e)),void 0!==M)return M;if(!_(e))return e;var B=m(e);if(B){if(M=g(e),!O)return u(e,M)}else{var L=p(e),P="[object Function]"==L||"[object GeneratorFunction]"==L;if(b(e))return c(e,O);if("[object Object]"==L||"[object Arguments]"==L||P&&!A){if(M=D||P?{}:v(e),!O)return D?h(e,s(M,e)):l(e,o(M,e))}else{if(!E[L])return A?e:{};M=y(e,L,O)}}S||(S=new r);var I=S.get(e);if(I)return I;S.set(e,M),k(e)?e.forEach((function(r){M.add(t(r,n,T,r,e,S))})):x(e)&&e.forEach((function(r,i){M.set(i,t(r,n,T,i,e,S))}));var F=N?D?d:f:D?keysIn:w,j=B?void 0:F(e);return i(j||e,(function(r,i){j&&(r=e[i=r]),a(M,i,t(r,n,T,i,e,S))})),M}},function(t,e,n){(function(e){var n="object"==typeof e&&e&&e.Object===Object&&e;t.exports=n}).call(this,n(211))},function(t,e){var n=Function.prototype.toString;t.exports=function(t){if(null!=t){try{return n.call(t)}catch(t){}try{return t+""}catch(t){}}return""}},function(t,e,n){var r=n(33),i=function(){try{var t=r(Object,"defineProperty");return t({},"",{}),t}catch(t){}}();t.exports=i},function(t,e,n){var r=n(230),i=n(47),a=n(5),o=n(39),s=n(60),c=n(48),u=Object.prototype.hasOwnProperty;t.exports=function(t,e){var n=a(t),l=!n&&i(t),h=!n&&!l&&o(t),f=!n&&!l&&!h&&c(t),d=n||l||h||f,p=d?r(t.length,String):[],g=p.length;for(var y in t)!e&&!u.call(t,y)||d&&("length"==y||h&&("offset"==y||"parent"==y)||f&&("buffer"==y||"byteLength"==y||"byteOffset"==y)||s(y,g))||p.push(y);return p}},function(t,e){t.exports=function(t,e){return function(n){return t(e(n))}}},function(t,e,n){(function(t){var r=n(16),i=e&&!e.nodeType&&e,a=i&&"object"==typeof t&&t&&!t.nodeType&&t,o=a&&a.exports===i?r.Buffer:void 0,s=o?o.allocUnsafe:void 0;t.exports=function(t,e){if(e)return t.slice();var n=t.length,r=s?s(n):new t.constructor(n);return t.copy(r),r}}).call(this,n(7)(t))},function(t,e){t.exports=function(t,e){var n=-1,r=t.length;for(e||(e=Array(r));++n<r;)e[n]=t[n];return e}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length,i=0,a=[];++n<r;){var o=t[n];e(o,n,t)&&(a[i++]=o)}return a}},function(t,e){t.exports=function(){return[]}},function(t,e,n){var r=n(84),i=n(63),a=n(83),o=n(117),s=Object.getOwnPropertySymbols?function(t){for(var e=[];t;)r(e,a(t)),t=i(t);return e}:o;t.exports=s},function(t,e,n){var r=n(120),i=n(83),a=n(30);t.exports=function(t){return r(t,a,i)}},function(t,e,n){var r=n(84),i=n(5);t.exports=function(t,e,n){var a=e(t);return i(t)?a:r(a,n(t))}},function(t,e,n){var r=n(33)(n(16),"Set");t.exports=r},function(t,e,n){var r=n(16).Uint8Array;t.exports=r},function(t,e,n){var r=n(85);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.length)}},function(t,e,n){var r=n(125),i=n(63),a=n(62);t.exports=function(t){return"function"!=typeof t.constructor||a(t)?{}:r(i(t))}},function(t,e,n){var r=n(11),i=Object.create,a=function(){function t(){}return function(e){if(!r(e))return{};if(i)return i(e);t.prototype=e;var n=new t;return t.prototype=void 0,n}}();t.exports=a},function(t,e,n){var r=n(79),i=n(64),a=n(127),o=n(5);t.exports=function(t,e){return(o(t)?r:i)(t,a(e))}},function(t,e,n){var r=n(35);t.exports=function(t){return"function"==typeof t?t:r}},function(t,e,n){var r=n(116),i=n(255),a=n(25),o=n(5);t.exports=function(t,e){return(o(t)?r:i)(t,a(e,3))}},function(t,e,n){var r=n(258),i=n(21);t.exports=function t(e,n,a,o,s){return e===n||(null==e||null==n||!i(e)&&!i(n)?e!=e&&n!=n:r(e,n,a,o,t,s))}},function(t,e,n){var r=n(131),i=n(261),a=n(132);t.exports=function(t,e,n,o,s,c){var u=1&n,l=t.length,h=e.length;if(l!=h&&!(u&&h>l))return!1;var f=c.get(t);if(f&&c.get(e))return f==e;var d=-1,p=!0,g=2&n?new r:void 0;for(c.set(t,e),c.set(e,t);++d<l;){var y=t[d],v=e[d];if(o)var m=u?o(v,y,d,e,t,c):o(y,v,d,t,e,c);if(void 0!==m){if(m)continue;p=!1;break}if(g){if(!i(e,(function(t,e){if(!a(g,e)&&(y===t||s(y,t,n,o,c)))return g.push(e)}))){p=!1;break}}else if(y!==v&&!s(y,v,n,o,c)){p=!1;break}}return c.delete(t),c.delete(e),p}},function(t,e,n){var r=n(78),i=n(259),a=n(260);function o(t){var e=-1,n=null==t?0:t.length;for(this.__data__=new r;++e<n;)this.add(t[e])}o.prototype.add=o.prototype.push=i,o.prototype.has=a,t.exports=o},function(t,e){t.exports=function(t,e){return t.has(e)}},function(t,e,n){var r=n(11);t.exports=function(t){return t==t&&!r(t)}},function(t,e){t.exports=function(t,e){return function(n){return null!=n&&(n[t]===e&&(void 0!==e||t in Object(n)))}}},function(t,e,n){var r=n(271);t.exports=function(t){return null==t?"":r(t)}},function(t,e,n){var r=n(272),i=n(137);t.exports=function(t,e){return null!=t&&i(t,e,r)}},function(t,e,n){var r=n(65),i=n(47),a=n(5),o=n(60),s=n(80),c=n(49);t.exports=function(t,e,n){for(var u=-1,l=(e=r(e,t)).length,h=!1;++u<l;){var f=c(e[u]);if(!(h=null!=t&&n(t,f)))break;t=t[f]}return h||++u!=l?h:!!(l=null==t?0:t.length)&&s(l)&&o(f,l)&&(a(t)||i(t))}},function(t,e){t.exports=function(t){return function(e){return null==e?void 0:e[t]}}},function(t,e){t.exports=function(t){return void 0===t}},function(t,e,n){var r=n(66),i=n(25),a=n(141),o=n(5);t.exports=function(t,e){return(o(t)?r:a)(t,i(e,3))}},function(t,e,n){var r=n(64),i=n(24);t.exports=function(t,e){var n=-1,a=i(t)?Array(t.length):[];return r(t,(function(t,r,i){a[++n]=e(t,r,i)})),a}},function(t,e,n){var r=n(277),i=n(64),a=n(25),o=n(278),s=n(5);t.exports=function(t,e,n){var c=s(t)?r:o,u=arguments.length<3;return c(t,a(e,4),n,u,i)}},function(t,e,n){var r=n(288),i=Math.max;t.exports=function(t,e,n){return e=i(void 0===e?t.length-1:e,0),function(){for(var a=arguments,o=-1,s=i(a.length-e,0),c=Array(s);++o<s;)c[o]=a[e+o];o=-1;for(var u=Array(e+1);++o<e;)u[o]=a[o];return u[e]=n(c),r(t,this,u)}}},function(t,e,n){var r=n(289),i=n(290)(r);t.exports=i},function(t,e){t.exports=function(t,e,n,r){for(var i=t.length,a=n+(r?1:-1);r?a--:++a<i;)if(e(t[a],a,t))return a;return-1}},function(t,e,n){var r=n(24),i=n(21);t.exports=function(t){return i(t)&&r(t)}},function(t,e,n){var r=n(299),i=n(30);t.exports=function(t){return null==t?[]:r(t,i(t))}},function(t,e,n){var r=n(10),i=n(149);t.exports=function(t,e,n,r){return function(t,e,n,r){var a,o,s={},c=new i,u=function(t){var e=t.v!==a?t.v:t.w,r=s[e],i=n(t),u=o.distance+i;if(i<0)throw new Error("dijkstra does not allow negative edge weights. Bad edge: "+t+" Weight: "+i);u<r.distance&&(r.distance=u,r.predecessor=a,c.decrease(e,u))};t.nodes().forEach((function(t){var n=t===e?0:Number.POSITIVE_INFINITY;s[t]={distance:n},c.add(t,n)}));for(;c.size()>0&&(a=c.removeMin(),(o=s[a]).distance!==Number.POSITIVE_INFINITY);)r(a).forEach(u);return s}(t,String(e),n||a,r||function(e){return t.outEdges(e)})};var a=r.constant(1)},function(t,e,n){var r=n(10);function i(){this._arr=[],this._keyIndices={}}t.exports=i,i.prototype.size=function(){return this._arr.length},i.prototype.keys=function(){return this._arr.map((function(t){return t.key}))},i.prototype.has=function(t){return r.has(this._keyIndices,t)},i.prototype.priority=function(t){var e=this._keyIndices[t];if(void 0!==e)return this._arr[e].priority},i.prototype.min=function(){if(0===this.size())throw new Error("Queue underflow");return this._arr[0].key},i.prototype.add=function(t,e){var n=this._keyIndices;if(t=String(t),!r.has(n,t)){var i=this._arr,a=i.length;return n[t]=a,i.push({key:t,priority:e}),this._decrease(a),!0}return!1},i.prototype.removeMin=function(){this._swap(0,this._arr.length-1);var t=this._arr.pop();return delete this._keyIndices[t.key],this._heapify(0),t.key},i.prototype.decrease=function(t,e){var n=this._keyIndices[t];if(e>this._arr[n].priority)throw new Error("New priority is greater than current priority. Key: "+t+" Old: "+this._arr[n].priority+" New: "+e);this._arr[n].priority=e,this._decrease(n)},i.prototype._heapify=function(t){var e=this._arr,n=2*t,r=n+1,i=t;n<e.length&&(i=e[n].priority<e[i].priority?n:i,r<e.length&&(i=e[r].priority<e[i].priority?r:i),i!==t&&(this._swap(t,i),this._heapify(i)))},i.prototype._decrease=function(t){for(var e,n=this._arr,r=n[t].priority;0!==t&&!(n[e=t>>1].priority<r);)this._swap(t,e),t=e},i.prototype._swap=function(t,e){var n=this._arr,r=this._keyIndices,i=n[t],a=n[e];n[t]=a,n[e]=i,r[a.key]=t,r[i.key]=e}},function(t,e,n){var r=n(10);t.exports=function(t){var e=0,n=[],i={},a=[];return t.nodes().forEach((function(o){r.has(i,o)||function o(s){var c=i[s]={onStack:!0,lowlink:e,index:e++};if(n.push(s),t.successors(s).forEach((function(t){r.has(i,t)?i[t].onStack&&(c.lowlink=Math.min(c.lowlink,i[t].index)):(o(t),c.lowlink=Math.min(c.lowlink,i[t].lowlink))})),c.lowlink===c.index){var u,l=[];do{u=n.pop(),i[u].onStack=!1,l.push(u)}while(s!==u);a.push(l)}}(o)})),a}},function(t,e,n){var r=n(10);function i(t){var e={},n={},i=[];if(r.each(t.sinks(),(function o(s){if(r.has(n,s))throw new a;r.has(e,s)||(n[s]=!0,e[s]=!0,r.each(t.predecessors(s),o),delete n[s],i.push(s))})),r.size(e)!==t.nodeCount())throw new a;return i}function a(){}t.exports=i,i.CycleException=a,a.prototype=new Error},function(t,e,n){var r=n(10);t.exports=function(t,e,n){r.isArray(e)||(e=[e]);var i=(t.isDirected()?t.successors:t.neighbors).bind(t),a=[],o={};return r.each(e,(function(e){if(!t.hasNode(e))throw new Error("Graph does not have node: "+e);!function t(e,n,i,a,o,s){r.has(a,n)||(a[n]=!0,i||s.push(n),r.each(o(n),(function(n){t(e,n,i,a,o,s)})),i&&s.push(n))}(t,e,"post"===n,o,i,a)})),a}},function(t,e,n){var r;try{r=n(18)}catch(t){}r||(r=window.dagre),t.exports=r},function(t,e,n){var r=n(67),i=n(36),a=n(68),o=n(40),s=Object.prototype,c=s.hasOwnProperty,u=r((function(t,e){t=Object(t);var n=-1,r=e.length,u=r>2?e[2]:void 0;for(u&&a(e[0],e[1],u)&&(r=1);++n<r;)for(var l=e[n],h=o(l),f=-1,d=h.length;++f<d;){var p=h[f],g=t[p];(void 0===g||i(g,s[p])&&!c.call(t,p))&&(t[p]=l[p])}return t}));t.exports=u},function(t,e,n){var r=n(318);t.exports=function(t){return t?(t=r(t))===1/0||t===-1/0?17976931348623157e292*(t<0?-1:1):t==t?t:0:0===t?t:0}},function(t,e,n){var r=n(94);t.exports=function(t){return(null==t?0:t.length)?r(t,1):[]}},function(t,e,n){var r=n(59),i=n(36);t.exports=function(t,e,n){(void 0===n||i(t[e],n))&&(void 0!==n||e in t)||r(t,e,n)}},function(t,e,n){var r=n(34),i=n(63),a=n(21),o=Function.prototype,s=Object.prototype,c=o.toString,u=s.hasOwnProperty,l=c.call(Object);t.exports=function(t){if(!a(t)||"[object Object]"!=r(t))return!1;var e=i(t);if(null===e)return!0;var n=u.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&c.call(n)==l}},function(t,e){t.exports=function(t,e){if(("constructor"!==e||"function"!=typeof t[e])&&"__proto__"!=e)return t[e]}},function(t,e){t.exports=function(t,e){return t<e}},function(t,e,n){var r=n(332),i=n(335)((function(t,e){return null==t?{}:r(t,e)}));t.exports=i},function(t,e,n){var r=n(336)();t.exports=r},function(t,e,n){var r=n(135),i=0;t.exports=function(t){var e=++i;return r(t)+e}},function(t,e,n){"use strict";var r=n(4),i=n(17).Graph,a=n(69).slack;function o(t,e){return r.forEach(t.nodes(),(function n(i){r.forEach(e.nodeEdges(i),(function(r){var o=r.v,s=i===o?r.w:o;t.hasNode(s)||a(e,r)||(t.setNode(s,{}),t.setEdge(i,s,{}),n(s))}))})),t.nodeCount()}function s(t,e){return r.minBy(e.edges(),(function(n){if(t.hasNode(n.v)!==t.hasNode(n.w))return a(e,n)}))}function c(t,e,n){r.forEach(t.nodes(),(function(t){e.node(t).rank+=n}))}t.exports=function(t){var e,n,r=new i({directed:!1}),u=t.nodes()[0],l=t.nodeCount();r.setNode(u,{});for(;o(r,t)<l;)e=s(r,t),n=r.hasNode(e.v)?a(t,e):-a(t,e),c(r,t,n);return r}},function(t,e){t.exports=function(t,e){return t.intersect(e)}},function(t,e,n){var r=n(96);t.exports=function(t,e,n){return r(t,e,e,n)}},function(t,e,n){var r=n(369);t.exports=function(t,e,n){var i=t.x,a=t.y,o=[],s=Number.POSITIVE_INFINITY,c=Number.POSITIVE_INFINITY;e.forEach((function(t){s=Math.min(s,t.x),c=Math.min(c,t.y)}));for(var u=i-t.width/2-s,l=a-t.height/2-c,h=0;h<e.length;h++){var f=e[h],d=e[h<e.length-1?h+1:0],p=r(t,n,{x:u+f.x,y:l+f.y},{x:u+d.x,y:l+d.y});p&&o.push(p)}if(!o.length)return console.log("NO INTERSECTION FOUND, RETURN NODE CENTER",t),t;o.length>1&&o.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1}));return o[0]}},function(t,e){t.exports=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o);return{x:i+n,y:a+r}}},function(t,e,n){t.exports=function t(e){"use strict";var n=/^\0+/g,r=/[\0\r\f]/g,i=/: */g,a=/zoo|gra/,o=/([,: ])(transform)/g,s=/,+\s*(?![^(]*[)])/g,c=/ +\s*(?![^(]*[)])/g,u=/ *[\0] */g,l=/,\r+?/g,h=/([\t\r\n ])*\f?&/g,f=/:global\(((?:[^\(\)\[\]]*|\[.*\]|\([^\(\)]*\))*)\)/g,d=/\W+/g,p=/@(k\w+)\s*(\S*)\s*/,g=/::(place)/g,y=/:(read-only)/g,v=/\s+(?=[{\];=:>])/g,m=/([[}=:>])\s+/g,b=/(\{[^{]+?);(?=\})/g,x=/\s{2,}/g,_=/([^\(])(:+) */g,k=/[svh]\w+-[tblr]{2}/,w=/\(\s*(.*)\s*\)/g,E=/([\s\S]*?);/g,T=/-self|flex-/g,C=/[^]*?(:[rp][el]a[\w-]+)[^]*/,A=/stretch|:\s*\w+\-(?:conte|avail)/,S=/([^-])(image-set\()/,M="-webkit-",O="-moz-",D="-ms-",N=1,B=1,L=0,P=1,I=1,F=1,j=0,R=0,Y=0,z=[],U=[],$=0,W=null,H=0,V=1,G="",q="",X="";function Z(t,e,i,a,o){for(var s,c,l=0,h=0,f=0,d=0,v=0,m=0,b=0,x=0,k=0,E=0,T=0,C=0,A=0,S=0,O=0,D=0,j=0,U=0,W=0,K=i.length,it=K-1,at="",ot="",st="",ct="",ut="",lt="";O<K;){if(b=i.charCodeAt(O),O===it&&h+d+f+l!==0&&(0!==h&&(b=47===h?10:47),d=f=l=0,K++,it++),h+d+f+l===0){if(O===it&&(D>0&&(ot=ot.replace(r,"")),ot.trim().length>0)){switch(b){case 32:case 9:case 59:case 13:case 10:break;default:ot+=i.charAt(O)}b=59}if(1===j)switch(b){case 123:case 125:case 59:case 34:case 39:case 40:case 41:case 44:j=0;case 9:case 13:case 10:case 32:break;default:for(j=0,W=O,v=b,O--,b=59;W<K;)switch(i.charCodeAt(W++)){case 10:case 13:case 59:++O,b=v,W=K;break;case 58:D>0&&(++O,b=v);case 123:W=K}}switch(b){case 123:for(v=(ot=ot.trim()).charCodeAt(0),T=1,W=++O;O<K;){switch(b=i.charCodeAt(O)){case 123:T++;break;case 125:T--;break;case 47:switch(m=i.charCodeAt(O+1)){case 42:case 47:O=rt(m,O,it,i)}break;case 91:b++;case 40:b++;case 34:case 39:for(;O++<it&&i.charCodeAt(O)!==b;);}if(0===T)break;O++}switch(st=i.substring(W,O),0===v&&(v=(ot=ot.replace(n,"").trim()).charCodeAt(0)),v){case 64:switch(D>0&&(ot=ot.replace(r,"")),m=ot.charCodeAt(1)){case 100:case 109:case 115:case 45:s=e;break;default:s=z}if(W=(st=Z(e,s,st,m,o+1)).length,Y>0&&0===W&&(W=ot.length),$>0&&(c=nt(3,st,s=J(z,ot,U),e,B,N,W,m,o,a),ot=s.join(""),void 0!==c&&0===(W=(st=c.trim()).length)&&(m=0,st="")),W>0)switch(m){case 115:ot=ot.replace(w,et);case 100:case 109:case 45:st=ot+"{"+st+"}";break;case 107:st=(ot=ot.replace(p,"$1 $2"+(V>0?G:"")))+"{"+st+"}",st=1===I||2===I&&tt("@"+st,3)?"@"+M+st+"@"+st:"@"+st;break;default:st=ot+st,112===a&&(ct+=st,st="")}else st="";break;default:st=Z(e,J(e,ot,U),st,a,o+1)}ut+=st,C=0,j=0,S=0,D=0,U=0,A=0,ot="",st="",b=i.charCodeAt(++O);break;case 125:case 59:if((W=(ot=(D>0?ot.replace(r,""):ot).trim()).length)>1)switch(0===S&&(45===(v=ot.charCodeAt(0))||v>96&&v<123)&&(W=(ot=ot.replace(" ",":")).length),$>0&&void 0!==(c=nt(1,ot,e,t,B,N,ct.length,a,o,a))&&0===(W=(ot=c.trim()).length)&&(ot="\0\0"),v=ot.charCodeAt(0),m=ot.charCodeAt(1),v){case 0:break;case 64:if(105===m||99===m){lt+=ot+i.charAt(O);break}default:if(58===ot.charCodeAt(W-1))break;ct+=Q(ot,v,m,ot.charCodeAt(2))}C=0,j=0,S=0,D=0,U=0,ot="",b=i.charCodeAt(++O)}}switch(b){case 13:case 10:if(h+d+f+l+R===0)switch(E){case 41:case 39:case 34:case 64:case 126:case 62:case 42:case 43:case 47:case 45:case 58:case 44:case 59:case 123:case 125:break;default:S>0&&(j=1)}47===h?h=0:P+C===0&&107!==a&&ot.length>0&&(D=1,ot+="\0"),$*H>0&&nt(0,ot,e,t,B,N,ct.length,a,o,a),N=1,B++;break;case 59:case 125:if(h+d+f+l===0){N++;break}default:switch(N++,at=i.charAt(O),b){case 9:case 32:if(d+l+h===0)switch(x){case 44:case 58:case 9:case 32:at="";break;default:32!==b&&(at=" ")}break;case 0:at="\\0";break;case 12:at="\\f";break;case 11:at="\\v";break;case 38:d+h+l===0&&P>0&&(U=1,D=1,at="\f"+at);break;case 108:if(d+h+l+L===0&&S>0)switch(O-S){case 2:112===x&&58===i.charCodeAt(O-3)&&(L=x);case 8:111===k&&(L=k)}break;case 58:d+h+l===0&&(S=O);break;case 44:h+f+d+l===0&&(D=1,at+="\r");break;case 34:case 39:0===h&&(d=d===b?0:0===d?b:d);break;case 91:d+h+f===0&&l++;break;case 93:d+h+f===0&&l--;break;case 41:d+h+l===0&&f--;break;case 40:if(d+h+l===0){if(0===C)switch(2*x+3*k){case 533:break;default:T=0,C=1}f++}break;case 64:h+f+d+l+S+A===0&&(A=1);break;case 42:case 47:if(d+l+f>0)break;switch(h){case 0:switch(2*b+3*i.charCodeAt(O+1)){case 235:h=47;break;case 220:W=O,h=42}break;case 42:47===b&&42===x&&W+2!==O&&(33===i.charCodeAt(W+2)&&(ct+=i.substring(W,O+1)),at="",h=0)}}if(0===h){if(P+d+l+A===0&&107!==a&&59!==b)switch(b){case 44:case 126:case 62:case 43:case 41:case 40:if(0===C){switch(x){case 9:case 32:case 10:case 13:at+="\0";break;default:at="\0"+at+(44===b?"":"\0")}D=1}else switch(b){case 40:S+7===O&&108===x&&(S=0),C=++T;break;case 41:0==(C=--T)&&(D=1,at+="\0")}break;case 9:case 32:switch(x){case 0:case 123:case 125:case 59:case 44:case 12:case 9:case 32:case 10:case 13:break;default:0===C&&(D=1,at+="\0")}}ot+=at,32!==b&&9!==b&&(E=b)}}k=x,x=b,O++}if(W=ct.length,Y>0&&0===W&&0===ut.length&&0===e[0].length==0&&(109!==a||1===e.length&&(P>0?q:X)===e[0])&&(W=e.join(",").length+2),W>0){if(s=0===P&&107!==a?function(t){for(var e,n,i=0,a=t.length,o=Array(a);i<a;++i){for(var s=t[i].split(u),c="",l=0,h=0,f=0,d=0,p=s.length;l<p;++l)if(!(0===(h=(n=s[l]).length)&&p>1)){if(f=c.charCodeAt(c.length-1),d=n.charCodeAt(0),e="",0!==l)switch(f){case 42:case 126:case 62:case 43:case 32:case 40:break;default:e=" "}switch(d){case 38:n=e+q;case 126:case 62:case 43:case 32:case 41:case 40:break;case 91:n=e+n+q;break;case 58:switch(2*n.charCodeAt(1)+3*n.charCodeAt(2)){case 530:if(F>0){n=e+n.substring(8,h-1);break}default:(l<1||s[l-1].length<1)&&(n=e+q+n)}break;case 44:e="";default:n=h>1&&n.indexOf(":")>0?e+n.replace(_,"$1"+q+"$2"):e+n+q}c+=n}o[i]=c.replace(r,"").trim()}return o}(e):e,$>0&&void 0!==(c=nt(2,ct,s,t,B,N,W,a,o,a))&&0===(ct=c).length)return lt+ct+ut;if(ct=s.join(",")+"{"+ct+"}",I*L!=0){switch(2!==I||tt(ct,2)||(L=0),L){case 111:ct=ct.replace(y,":-moz-$1")+ct;break;case 112:ct=ct.replace(g,"::-webkit-input-$1")+ct.replace(g,"::-moz-$1")+ct.replace(g,":-ms-input-$1")+ct}L=0}}return lt+ct+ut}function J(t,e,n){var r=e.trim().split(l),i=r,a=r.length,o=t.length;switch(o){case 0:case 1:for(var s=0,c=0===o?"":t[0]+" ";s<a;++s)i[s]=K(c,i[s],n,o).trim();break;default:s=0;var u=0;for(i=[];s<a;++s)for(var h=0;h<o;++h)i[u++]=K(t[h]+" ",r[s],n,o).trim()}return i}function K(t,e,n,r){var i=e,a=i.charCodeAt(0);switch(a<33&&(a=(i=i.trim()).charCodeAt(0)),a){case 38:switch(P+r){case 0:case 1:if(0===t.trim().length)break;default:return i.replace(h,"$1"+t.trim())}break;case 58:switch(i.charCodeAt(1)){case 103:if(F>0&&P>0)return i.replace(f,"$1").replace(h,"$1"+X);break;default:return t.trim()+i.replace(h,"$1"+t.trim())}default:if(n*P>0&&i.indexOf("\f")>0)return i.replace(h,(58===t.charCodeAt(0)?"":"$1")+t.trim())}return t+i}function Q(t,e,n,r){var u,l=0,h=t+";",f=2*e+3*n+4*r;if(944===f)return function(t){var e=t.length,n=t.indexOf(":",9)+1,r=t.substring(0,n).trim(),i=t.substring(n,e-1).trim();switch(t.charCodeAt(9)*V){case 0:break;case 45:if(110!==t.charCodeAt(10))break;default:var a=i.split((i="",s)),o=0;for(n=0,e=a.length;o<e;n=0,++o){for(var u=a[o],l=u.split(c);u=l[n];){var h=u.charCodeAt(0);if(1===V&&(h>64&&h<90||h>96&&h<123||95===h||45===h&&45!==u.charCodeAt(1)))switch(isNaN(parseFloat(u))+(-1!==u.indexOf("("))){case 1:switch(u){case"infinite":case"alternate":case"backwards":case"running":case"normal":case"forwards":case"both":case"none":case"linear":case"ease":case"ease-in":case"ease-out":case"ease-in-out":case"paused":case"reverse":case"alternate-reverse":case"inherit":case"initial":case"unset":case"step-start":case"step-end":break;default:u+=G}}l[n++]=u}i+=(0===o?"":",")+l.join(" ")}}return i=r+i+";",1===I||2===I&&tt(i,1)?M+i+i:i}(h);if(0===I||2===I&&!tt(h,1))return h;switch(f){case 1015:return 97===h.charCodeAt(10)?M+h+h:h;case 951:return 116===h.charCodeAt(3)?M+h+h:h;case 963:return 110===h.charCodeAt(5)?M+h+h:h;case 1009:if(100!==h.charCodeAt(4))break;case 969:case 942:return M+h+h;case 978:return M+h+O+h+h;case 1019:case 983:return M+h+O+h+D+h+h;case 883:return 45===h.charCodeAt(8)?M+h+h:h.indexOf("image-set(",11)>0?h.replace(S,"$1-webkit-$2")+h:h;case 932:if(45===h.charCodeAt(4))switch(h.charCodeAt(5)){case 103:return M+"box-"+h.replace("-grow","")+M+h+D+h.replace("grow","positive")+h;case 115:return M+h+D+h.replace("shrink","negative")+h;case 98:return M+h+D+h.replace("basis","preferred-size")+h}return M+h+D+h+h;case 964:return M+h+D+"flex-"+h+h;case 1023:if(99!==h.charCodeAt(8))break;return u=h.substring(h.indexOf(":",15)).replace("flex-","").replace("space-between","justify"),M+"box-pack"+u+M+h+D+"flex-pack"+u+h;case 1005:return a.test(h)?h.replace(i,":"+M)+h.replace(i,":"+O)+h:h;case 1e3:switch(l=(u=h.substring(13).trim()).indexOf("-")+1,u.charCodeAt(0)+u.charCodeAt(l)){case 226:u=h.replace(k,"tb");break;case 232:u=h.replace(k,"tb-rl");break;case 220:u=h.replace(k,"lr");break;default:return h}return M+h+D+u+h;case 1017:if(-1===h.indexOf("sticky",9))return h;case 975:switch(l=(h=t).length-10,f=(u=(33===h.charCodeAt(l)?h.substring(0,l):h).substring(t.indexOf(":",7)+1).trim()).charCodeAt(0)+(0|u.charCodeAt(7))){case 203:if(u.charCodeAt(8)<111)break;case 115:h=h.replace(u,M+u)+";"+h;break;case 207:case 102:h=h.replace(u,M+(f>102?"inline-":"")+"box")+";"+h.replace(u,M+u)+";"+h.replace(u,D+u+"box")+";"+h}return h+";";case 938:if(45===h.charCodeAt(5))switch(h.charCodeAt(6)){case 105:return u=h.replace("-items",""),M+h+M+"box-"+u+D+"flex-"+u+h;case 115:return M+h+D+"flex-item-"+h.replace(T,"")+h;default:return M+h+D+"flex-line-pack"+h.replace("align-content","").replace(T,"")+h}break;case 973:case 989:if(45!==h.charCodeAt(3)||122===h.charCodeAt(4))break;case 931:case 953:if(!0===A.test(t))return 115===(u=t.substring(t.indexOf(":")+1)).charCodeAt(0)?Q(t.replace("stretch","fill-available"),e,n,r).replace(":fill-available",":stretch"):h.replace(u,M+u)+h.replace(u,O+u.replace("fill-",""))+h;break;case 962:if(h=M+h+(102===h.charCodeAt(5)?D+h:"")+h,n+r===211&&105===h.charCodeAt(13)&&h.indexOf("transform",10)>0)return h.substring(0,h.indexOf(";",27)+1).replace(o,"$1-webkit-$2")+h}return h}function tt(t,e){var n=t.indexOf(1===e?":":"{"),r=t.substring(0,3!==e?n:10),i=t.substring(n+1,t.length-1);return W(2!==e?r:r.replace(C,"$1"),i,e)}function et(t,e){var n=Q(e,e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2));return n!==e+";"?n.replace(E," or ($1)").substring(4):"("+e+")"}function nt(t,e,n,r,i,a,o,s,c,u){for(var l,h=0,f=e;h<$;++h)switch(l=U[h].call(at,t,f,n,r,i,a,o,s,c,u)){case void 0:case!1:case!0:case null:break;default:f=l}if(f!==e)return f}function rt(t,e,n,r){for(var i=e+1;i<n;++i)switch(r.charCodeAt(i)){case 47:if(42===t&&42===r.charCodeAt(i-1)&&e+2!==i)return i+1;break;case 10:if(47===t)return i+1}return i}function it(t){for(var e in t){var n=t[e];switch(e){case"keyframe":V=0|n;break;case"global":F=0|n;break;case"cascade":P=0|n;break;case"compress":j=0|n;break;case"semicolon":R=0|n;break;case"preserve":Y=0|n;break;case"prefix":W=null,n?"function"!=typeof n?I=1:(I=2,W=n):I=0}}return it}function at(e,n){if(void 0!==this&&this.constructor===at)return t(e);var i=e,a=i.charCodeAt(0);a<33&&(a=(i=i.trim()).charCodeAt(0)),V>0&&(G=i.replace(d,91===a?"":"-")),a=1,1===P?X=i:q=i;var o,s=[X];$>0&&void 0!==(o=nt(-1,n,s,s,B,N,0,0,0,0))&&"string"==typeof o&&(n=o);var c=Z(z,s,n,0,0);return $>0&&void 0!==(o=nt(-2,c,s,s,B,N,c.length,0,0,0))&&"string"!=typeof(c=o)&&(a=0),G="",X="",q="",L=0,B=1,N=1,j*a==0?c:function(t){return t.replace(r,"").replace(v,"").replace(m,"$1").replace(b,"$1").replace(x," ")}(c)}return at.use=function t(e){switch(e){case void 0:case null:$=U.length=0;break;default:if("function"==typeof e)U[$++]=e;else if("object"==typeof e)for(var n=0,r=e.length;n<r;++n)t(e[n]);else H=0|!!e}return t},at.set=it,void 0!==e&&it(e),at}(null)},function(t,e){t.exports=function(t,e){return t.intersect(e)}},function(t,e,n){var r={"./locale":98,"./locale.js":98};function i(t){var e=a(t);return n(e)}function a(t){if(!n.o(r,t)){var e=new Error("Cannot find module '"+t+"'");throw e.code="MODULE_NOT_FOUND",e}return r[t]}i.keys=function(){return Object.keys(r)},i.resolve=a,t.exports=i,i.id=171},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(51);e.hex=r.default;var i=n(51);e.rgb=i.default;var a=n(51);e.rgba=a.default;var o=n(100);e.hsl=o.default;var s=n(100);e.hsla=s.default;var c=n(29);e.channel=c.default;var u=n(182);e.red=u.default;var l=n(183);e.green=l.default;var h=n(184);e.blue=h.default;var f=n(185);e.hue=f.default;var d=n(186);e.saturation=d.default;var p=n(187);e.lightness=p.default;var g=n(101);e.alpha=g.default;var y=n(101);e.opacity=y.default;var v=n(102);e.luminance=v.default;var m=n(188);e.isDark=m.default;var b=n(103);e.isLight=b.default;var x=n(189);e.isValid=x.default;var _=n(190);e.saturate=_.default;var k=n(191);e.desaturate=k.default;var w=n(192);e.lighten=w.default;var E=n(193);e.darken=E.default;var T=n(104);e.opacify=T.default;var C=n(104);e.fadeIn=C.default;var A=n(105);e.transparentize=A.default;var S=n(105);e.fadeOut=S.default;var M=n(194);e.complement=M.default;var O=n(195);e.grayscale=O.default;var D=n(106);e.adjust=D.default;var N=n(52);e.change=N.default;var B=n(196);e.invert=B.default;var L=n(107);e.mix=L.default;var P=n(197);e.scale=P.default},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r={min:{r:0,g:0,b:0,s:0,l:0,a:0},max:{r:255,g:255,b:255,h:360,s:100,l:100,a:1},clamp:{r:function(t){return t>=255?255:t<0?0:t},g:function(t){return t>=255?255:t<0?0:t},b:function(t){return t>=255?255:t<0?0:t},h:function(t){return t%360},s:function(t){return t>=100?100:t<0?0:t},l:function(t){return t>=100?100:t<0?0:t},a:function(t){return t>=1?1:t<0?0:t}},toLinear:function(t){var e=t/255;return t>.03928?Math.pow((e+.055)/1.055,2.4):e/12.92},hue2rgb:function(t,e,n){return n<0&&(n+=1),n>1&&(n-=1),n<1/6?t+6*(e-t)*n:n<.5?e:n<2/3?t+(e-t)*(2/3-n)*6:t},hsl2rgb:function(t,e){var n=t.h,i=t.s,a=t.l;if(100===i)return 2.55*a;n/=360,i/=100;var o=(a/=100)<.5?a*(1+i):a+i-a*i,s=2*a-o;switch(e){case"r":return 255*r.hue2rgb(s,o,n+1/3);case"g":return 255*r.hue2rgb(s,o,n);case"b":return 255*r.hue2rgb(s,o,n-1/3)}},rgb2hsl:function(t,e){var n=t.r,r=t.g,i=t.b;n/=255,r/=255,i/=255;var a=Math.max(n,r,i),o=Math.min(n,r,i),s=(a+o)/2;if("l"===e)return 100*s;if(a===o)return 0;var c=a-o;if("s"===e)return 100*(s>.5?c/(2-a-o):c/(a+o));switch(a){case n:return 60*((r-i)/c+(r<i?6:0));case r:return 60*((i-n)/c+2);case i:return 60*((n-r)/c+4);default:return-1}}};e.default=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r={round:function(t){return Math.round(1e10*t)/1e10}};e.default=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r={frac2hex:function(t){var e=Math.round(255*t).toString(16);return e.length>1?e:"0"+e},dec2hex:function(t){var e=Math.round(t).toString(16);return e.length>1?e:"0"+e}};e.default=r},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(75),a=n(177),o=function(){function t(t,e){this.color=e,this.changed=!1,this.data=t,this.type=new a.default}return t.prototype.set=function(t,e){return this.color=e,this.changed=!1,this.data=t,this.type.type=i.TYPE.ALL,this},t.prototype._ensureHSL=function(){void 0===this.data.h&&(this.data.h=r.default.channel.rgb2hsl(this.data,"h")),void 0===this.data.s&&(this.data.s=r.default.channel.rgb2hsl(this.data,"s")),void 0===this.data.l&&(this.data.l=r.default.channel.rgb2hsl(this.data,"l"))},t.prototype._ensureRGB=function(){void 0===this.data.r&&(this.data.r=r.default.channel.hsl2rgb(this.data,"r")),void 0===this.data.g&&(this.data.g=r.default.channel.hsl2rgb(this.data,"g")),void 0===this.data.b&&(this.data.b=r.default.channel.hsl2rgb(this.data,"b"))},Object.defineProperty(t.prototype,"r",{get:function(){return this.type.is(i.TYPE.HSL)||void 0===this.data.r?(this._ensureHSL(),r.default.channel.hsl2rgb(this.data,"r")):this.data.r},set:function(t){this.type.set(i.TYPE.RGB),this.changed=!0,this.data.r=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"g",{get:function(){return this.type.is(i.TYPE.HSL)||void 0===this.data.g?(this._ensureHSL(),r.default.channel.hsl2rgb(this.data,"g")):this.data.g},set:function(t){this.type.set(i.TYPE.RGB),this.changed=!0,this.data.g=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"b",{get:function(){return this.type.is(i.TYPE.HSL)||void 0===this.data.b?(this._ensureHSL(),r.default.channel.hsl2rgb(this.data,"b")):this.data.b},set:function(t){this.type.set(i.TYPE.RGB),this.changed=!0,this.data.b=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"h",{get:function(){return this.type.is(i.TYPE.RGB)||void 0===this.data.h?(this._ensureRGB(),r.default.channel.rgb2hsl(this.data,"h")):this.data.h},set:function(t){this.type.set(i.TYPE.HSL),this.changed=!0,this.data.h=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"s",{get:function(){return this.type.is(i.TYPE.RGB)||void 0===this.data.s?(this._ensureRGB(),r.default.channel.rgb2hsl(this.data,"s")):this.data.s},set:function(t){this.type.set(i.TYPE.HSL),this.changed=!0,this.data.s=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"l",{get:function(){return this.type.is(i.TYPE.RGB)||void 0===this.data.l?(this._ensureRGB(),r.default.channel.rgb2hsl(this.data,"l")):this.data.l},set:function(t){this.type.set(i.TYPE.HSL),this.changed=!0,this.data.l=t},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"a",{get:function(){return this.data.a},set:function(t){this.changed=!0,this.data.a=t},enumerable:!0,configurable:!0}),t}();e.default=o},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(75),i=function(){function t(){this.type=r.TYPE.ALL}return t.prototype.get=function(){return this.type},t.prototype.set=function(t){if(this.type&&this.type!==t)throw new Error("Cannot change both RGB and HSL channels at the same time");this.type=t},t.prototype.reset=function(){this.type=r.TYPE.ALL},t.prototype.is=function(t){return this.type===t},t}();e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i={};e.DEC2HEX=i;for(var a=0;a<=255;a++)i[a]=r.default.unit.dec2hex(a)},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(99),i={colors:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyanaqua:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",rebeccapurple:"#663399",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",transparent:"#00000000",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},parse:function(t){t=t.toLowerCase();var e=i.colors[t];if(e)return r.default.parse(e)},stringify:function(t){var e=r.default.stringify(t);for(var n in i.colors)if(i.colors[n]===e)return n}};e.default=i},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a={re:/^rgba?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?))(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e\d+)?(%?)))?\s*?\)$/i,parse:function(t){var e=t.charCodeAt(0);if(114===e||82===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5],h=n[6],f=n[7],d=n[8];return i.default.set({r:r.default.channel.clamp.r(s?2.55*parseFloat(o):parseFloat(o)),g:r.default.channel.clamp.g(u?2.55*parseFloat(c):parseFloat(c)),b:r.default.channel.clamp.b(h?2.55*parseFloat(l):parseFloat(l)),a:f?r.default.channel.clamp.a(d?parseFloat(f)/100:parseFloat(f)):1},t)}}},stringify:function(t){return t.a<1?"rgba("+r.default.lang.round(t.r)+", "+r.default.lang.round(t.g)+", "+r.default.lang.round(t.b)+", "+r.default.lang.round(t.a)+")":"rgb("+r.default.lang.round(t.r)+", "+r.default.lang.round(t.g)+", "+r.default.lang.round(t.b)+")"}};e.default=a},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(45),a={re:/^hsla?\(\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(?:deg|grad|rad|turn)?)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)\s*?(?:,|\s)\s*?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?%)(?:\s*?(?:,|\/)\s*?\+?(-?(?:\d+(?:\.\d+)?|(?:\.\d+))(?:e-?\d+)?(%)?))?\s*?\)$/i,hueRe:/^(.+?)(deg|grad|rad|turn)$/i,_hue2deg:function(t){var e=t.match(a.hueRe);if(e){var n=e[1];switch(e[2]){case"grad":return r.default.channel.clamp.h(.9*parseFloat(n));case"rad":return r.default.channel.clamp.h(180*parseFloat(n)/Math.PI);case"turn":return r.default.channel.clamp.h(360*parseFloat(n))}}return r.default.channel.clamp.h(parseFloat(t))},parse:function(t){var e=t.charCodeAt(0);if(104===e||72===e){var n=t.match(a.re);if(n){var o=n[1],s=n[2],c=n[3],u=n[4],l=n[5];return i.default.set({h:a._hue2deg(o),s:r.default.channel.clamp.s(parseFloat(s)),l:r.default.channel.clamp.l(parseFloat(c)),a:u?r.default.channel.clamp.a(l?parseFloat(u)/100:parseFloat(u)):1},t)}}},stringify:function(t){return t.a<1?"hsla("+r.default.lang.round(t.h)+", "+r.default.lang.round(t.s)+"%, "+r.default.lang.round(t.l)+"%, "+t.a+")":"hsl("+r.default.lang.round(t.h)+", "+r.default.lang.round(t.s)+"%, "+r.default.lang.round(t.l)+"%)"}};e.default=a},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"r")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"g")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"b")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"h")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"s")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(29);e.default=function(t){return r.default(t,"l")}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(103);e.default=function(t){return!r.default(t)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15);e.default=function(t){try{return r.default.parse(t),!0}catch(t){return!1}}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"s",e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"s",-e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"l",e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t,e){return r.default(t,"l",-e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(32);e.default=function(t){return r.default(t,"h",180)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(52);e.default=function(t){return r.default(t,{s:0})}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(15),i=n(107);e.default=function(t,e){void 0===e&&(e=100);var n=r.default.parse(t);return n.r=255-n.r,n.g=255-n.g,n.b=255-n.b,i.default(n,t,e)}},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var r=n(9),i=n(15),a=n(106);e.default=function(t,e){var n,o,s,c=i.default.parse(t),u={};for(var l in e)u[l]=(n=c[l],o=e[l],s=r.default.channel.max[l],o>0?(s-n)*o/100:n*o/100);return a.default(t,u)}},function(t,e,n){t.exports={Graph:n(76),version:n(300)}},function(t,e,n){var r=n(108);t.exports=function(t){return r(t,4)}},function(t,e){t.exports=function(){this.__data__=[],this.size=0}},function(t,e,n){var r=n(55),i=Array.prototype.splice;t.exports=function(t){var e=this.__data__,n=r(e,t);return!(n<0)&&(n==e.length-1?e.pop():i.call(e,n,1),--this.size,!0)}},function(t,e,n){var r=n(55);t.exports=function(t){var e=this.__data__,n=r(e,t);return n<0?void 0:e[n][1]}},function(t,e,n){var r=n(55);t.exports=function(t){return r(this.__data__,t)>-1}},function(t,e,n){var r=n(55);t.exports=function(t,e){var n=this.__data__,i=r(n,t);return i<0?(++this.size,n.push([t,e])):n[i][1]=e,this}},function(t,e,n){var r=n(54);t.exports=function(){this.__data__=new r,this.size=0}},function(t,e){t.exports=function(t){var e=this.__data__,n=e.delete(t);return this.size=e.size,n}},function(t,e){t.exports=function(t){return this.__data__.get(t)}},function(t,e){t.exports=function(t){return this.__data__.has(t)}},function(t,e,n){var r=n(54),i=n(77),a=n(78);t.exports=function(t,e){var n=this.__data__;if(n instanceof r){var o=n.__data__;if(!i||o.length<199)return o.push([t,e]),this.size=++n.size,this;n=this.__data__=new a(o)}return n.set(t,e),this.size=n.size,this}},function(t,e,n){var r=n(37),i=n(214),a=n(11),o=n(110),s=/^\[object .+?Constructor\]$/,c=Function.prototype,u=Object.prototype,l=c.toString,h=u.hasOwnProperty,f=RegExp("^"+l.call(h).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");t.exports=function(t){return!(!a(t)||i(t))&&(r(t)?f:s).test(o(t))}},function(t,e){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e,n){var r=n(38),i=Object.prototype,a=i.hasOwnProperty,o=i.toString,s=r?r.toStringTag:void 0;t.exports=function(t){var e=a.call(t,s),n=t[s];try{t[s]=void 0;var r=!0}catch(t){}var i=o.call(t);return r&&(e?t[s]=n:delete t[s]),i}},function(t,e){var n=Object.prototype.toString;t.exports=function(t){return n.call(t)}},function(t,e,n){var r,i=n(215),a=(r=/[^.]+$/.exec(i&&i.keys&&i.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";t.exports=function(t){return!!a&&a in t}},function(t,e,n){var r=n(16)["__core-js_shared__"];t.exports=r},function(t,e){t.exports=function(t,e){return null==t?void 0:t[e]}},function(t,e,n){var r=n(218),i=n(54),a=n(77);t.exports=function(){this.size=0,this.__data__={hash:new r,map:new(a||i),string:new r}}},function(t,e,n){var r=n(219),i=n(220),a=n(221),o=n(222),s=n(223);function c(t){var e=-1,n=null==t?0:t.length;for(this.clear();++e<n;){var r=t[e];this.set(r[0],r[1])}}c.prototype.clear=r,c.prototype.delete=i,c.prototype.get=a,c.prototype.has=o,c.prototype.set=s,t.exports=c},function(t,e,n){var r=n(56);t.exports=function(){this.__data__=r?r(null):{},this.size=0}},function(t,e){t.exports=function(t){var e=this.has(t)&&delete this.__data__[t];return this.size-=e?1:0,e}},function(t,e,n){var r=n(56),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;if(r){var n=e[t];return"__lodash_hash_undefined__"===n?void 0:n}return i.call(e,t)?e[t]:void 0}},function(t,e,n){var r=n(56),i=Object.prototype.hasOwnProperty;t.exports=function(t){var e=this.__data__;return r?void 0!==e[t]:i.call(e,t)}},function(t,e,n){var r=n(56);t.exports=function(t,e){var n=this.__data__;return this.size+=this.has(t)?0:1,n[t]=r&&void 0===e?"__lodash_hash_undefined__":e,this}},function(t,e,n){var r=n(57);t.exports=function(t){var e=r(this,t).delete(t);return this.size-=e?1:0,e}},function(t,e){t.exports=function(t){var e=typeof t;return"string"==e||"number"==e||"symbol"==e||"boolean"==e?"__proto__"!==t:null===t}},function(t,e,n){var r=n(57);t.exports=function(t){return r(this,t).get(t)}},function(t,e,n){var r=n(57);t.exports=function(t){return r(this,t).has(t)}},function(t,e,n){var r=n(57);t.exports=function(t,e){var n=r(this,t),i=n.size;return n.set(t,e),this.size+=n.size==i?0:1,this}},function(t,e,n){var r=n(46),i=n(30);t.exports=function(t,e){return t&&r(e,i(e),t)}},function(t,e){t.exports=function(t,e){for(var n=-1,r=Array(t);++n<t;)r[n]=e(n);return r}},function(t,e,n){var r=n(34),i=n(21);t.exports=function(t){return i(t)&&"[object Arguments]"==r(t)}},function(t,e){t.exports=function(){return!1}},function(t,e,n){var r=n(34),i=n(80),a=n(21),o={};o["[object Float32Array]"]=o["[object Float64Array]"]=o["[object Int8Array]"]=o["[object Int16Array]"]=o["[object Int32Array]"]=o["[object Uint8Array]"]=o["[object Uint8ClampedArray]"]=o["[object Uint16Array]"]=o["[object Uint32Array]"]=!0,o["[object Arguments]"]=o["[object Array]"]=o["[object ArrayBuffer]"]=o["[object Boolean]"]=o["[object DataView]"]=o["[object Date]"]=o["[object Error]"]=o["[object Function]"]=o["[object Map]"]=o["[object Number]"]=o["[object Object]"]=o["[object RegExp]"]=o["[object Set]"]=o["[object String]"]=o["[object WeakMap]"]=!1,t.exports=function(t){return a(t)&&i(t.length)&&!!o[r(t)]}},function(t,e,n){var r=n(113)(Object.keys,Object);t.exports=r},function(t,e,n){var r=n(46),i=n(40);t.exports=function(t,e){return t&&r(e,i(e),t)}},function(t,e,n){var r=n(11),i=n(62),a=n(237),o=Object.prototype.hasOwnProperty;t.exports=function(t){if(!r(t))return a(t);var e=i(t),n=[];for(var s in t)("constructor"!=s||!e&&o.call(t,s))&&n.push(s);return n}},function(t,e){t.exports=function(t){var e=[];if(null!=t)for(var n in Object(t))e.push(n);return e}},function(t,e,n){var r=n(46),i=n(83);t.exports=function(t,e){return r(t,i(t),e)}},function(t,e,n){var r=n(46),i=n(118);t.exports=function(t,e){return r(t,i(t),e)}},function(t,e,n){var r=n(120),i=n(118),a=n(40);t.exports=function(t){return r(t,a,i)}},function(t,e,n){var r=n(33)(n(16),"DataView");t.exports=r},function(t,e,n){var r=n(33)(n(16),"Promise");t.exports=r},function(t,e,n){var r=n(33)(n(16),"WeakMap");t.exports=r},function(t,e){var n=Object.prototype.hasOwnProperty;t.exports=function(t){var e=t.length,r=new t.constructor(e);return e&&"string"==typeof t[0]&&n.call(t,"index")&&(r.index=t.index,r.input=t.input),r}},function(t,e,n){var r=n(85),i=n(246),a=n(247),o=n(248),s=n(123);t.exports=function(t,e,n){var c=t.constructor;switch(e){case"[object ArrayBuffer]":return r(t);case"[object Boolean]":case"[object Date]":return new c(+t);case"[object DataView]":return i(t,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return s(t,n);case"[object Map]":return new c;case"[object Number]":case"[object String]":return new c(t);case"[object RegExp]":return a(t);case"[object Set]":return new c;case"[object Symbol]":return o(t)}}},function(t,e,n){var r=n(85);t.exports=function(t,e){var n=e?r(t.buffer):t.buffer;return new t.constructor(n,t.byteOffset,t.byteLength)}},function(t,e){var n=/\w*$/;t.exports=function(t){var e=new t.constructor(t.source,n.exec(t));return e.lastIndex=t.lastIndex,e}},function(t,e,n){var r=n(38),i=r?r.prototype:void 0,a=i?i.valueOf:void 0;t.exports=function(t){return a?Object(a.call(t)):{}}},function(t,e,n){var r=n(250),i=n(61),a=n(81),o=a&&a.isMap,s=o?i(o):r;t.exports=s},function(t,e,n){var r=n(41),i=n(21);t.exports=function(t){return i(t)&&"[object Map]"==r(t)}},function(t,e,n){var r=n(252),i=n(61),a=n(81),o=a&&a.isSet,s=o?i(o):r;t.exports=s},function(t,e,n){var r=n(41),i=n(21);t.exports=function(t){return i(t)&&"[object Set]"==r(t)}},function(t,e){t.exports=function(t){return function(e,n,r){for(var i=-1,a=Object(e),o=r(e),s=o.length;s--;){var c=o[t?s:++i];if(!1===n(a[c],c,a))break}return e}}},function(t,e,n){var r=n(24);t.exports=function(t,e){return function(n,i){if(null==n)return n;if(!r(n))return t(n,i);for(var a=n.length,o=e?a:-1,s=Object(n);(e?o--:++o<a)&&!1!==i(s[o],o,s););return n}}},function(t,e,n){var r=n(64);t.exports=function(t,e){var n=[];return r(t,(function(t,r,i){e(t,r,i)&&n.push(t)})),n}},function(t,e,n){var r=n(257),i=n(265),a=n(134);t.exports=function(t){var e=i(t);return 1==e.length&&e[0][2]?a(e[0][0],e[0][1]):function(n){return n===t||r(n,t,e)}}},function(t,e,n){var r=n(53),i=n(129);t.exports=function(t,e,n,a){var o=n.length,s=o,c=!a;if(null==t)return!s;for(t=Object(t);o--;){var u=n[o];if(c&&u[2]?u[1]!==t[u[0]]:!(u[0]in t))return!1}for(;++o<s;){var l=(u=n[o])[0],h=t[l],f=u[1];if(c&&u[2]){if(void 0===h&&!(l in t))return!1}else{var d=new r;if(a)var p=a(h,f,l,t,e,d);if(!(void 0===p?i(f,h,3,a,d):p))return!1}}return!0}},function(t,e,n){var r=n(53),i=n(130),a=n(262),o=n(264),s=n(41),c=n(5),u=n(39),l=n(48),h="[object Object]",f=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,d,p,g){var y=c(t),v=c(e),m=y?"[object Array]":s(t),b=v?"[object Array]":s(e),x=(m="[object Arguments]"==m?h:m)==h,_=(b="[object Arguments]"==b?h:b)==h,k=m==b;if(k&&u(t)){if(!u(e))return!1;y=!0,x=!1}if(k&&!x)return g||(g=new r),y||l(t)?i(t,e,n,d,p,g):a(t,e,m,n,d,p,g);if(!(1&n)){var w=x&&f.call(t,"__wrapped__"),E=_&&f.call(e,"__wrapped__");if(w||E){var T=w?t.value():t,C=E?e.value():e;return g||(g=new r),p(T,C,n,d,g)}}return!!k&&(g||(g=new r),o(t,e,n,d,p,g))}},function(t,e){t.exports=function(t){return this.__data__.set(t,"__lodash_hash_undefined__"),this}},function(t,e){t.exports=function(t){return this.__data__.has(t)}},function(t,e){t.exports=function(t,e){for(var n=-1,r=null==t?0:t.length;++n<r;)if(e(t[n],n,t))return!0;return!1}},function(t,e,n){var r=n(38),i=n(122),a=n(36),o=n(130),s=n(263),c=n(90),u=r?r.prototype:void 0,l=u?u.valueOf:void 0;t.exports=function(t,e,n,r,u,h,f){switch(n){case"[object DataView]":if(t.byteLength!=e.byteLength||t.byteOffset!=e.byteOffset)return!1;t=t.buffer,e=e.buffer;case"[object ArrayBuffer]":return!(t.byteLength!=e.byteLength||!h(new i(t),new i(e)));case"[object Boolean]":case"[object Date]":case"[object Number]":return a(+t,+e);case"[object Error]":return t.name==e.name&&t.message==e.message;case"[object RegExp]":case"[object String]":return t==e+"";case"[object Map]":var d=s;case"[object Set]":var p=1&r;if(d||(d=c),t.size!=e.size&&!p)return!1;var g=f.get(t);if(g)return g==e;r|=2,f.set(t,e);var y=o(d(t),d(e),r,u,h,f);return f.delete(t),y;case"[object Symbol]":if(l)return l.call(t)==l.call(e)}return!1}},function(t,e){t.exports=function(t){var e=-1,n=Array(t.size);return t.forEach((function(t,r){n[++e]=[r,t]})),n}},function(t,e,n){var r=n(119),i=Object.prototype.hasOwnProperty;t.exports=function(t,e,n,a,o,s){var c=1&n,u=r(t),l=u.length;if(l!=r(e).length&&!c)return!1;for(var h=l;h--;){var f=u[h];if(!(c?f in e:i.call(e,f)))return!1}var d=s.get(t);if(d&&s.get(e))return d==e;var p=!0;s.set(t,e),s.set(e,t);for(var g=c;++h<l;){var y=t[f=u[h]],v=e[f];if(a)var m=c?a(v,y,f,e,t,s):a(y,v,f,t,e,s);if(!(void 0===m?y===v||o(y,v,n,a,s):m)){p=!1;break}g||(g="constructor"==f)}if(p&&!g){var b=t.constructor,x=e.constructor;b!=x&&"constructor"in t&&"constructor"in e&&!("function"==typeof b&&b instanceof b&&"function"==typeof x&&x instanceof x)&&(p=!1)}return s.delete(t),s.delete(e),p}},function(t,e,n){var r=n(133),i=n(30);t.exports=function(t){for(var e=i(t),n=e.length;n--;){var a=e[n],o=t[a];e[n]=[a,o,r(o)]}return e}},function(t,e,n){var r=n(129),i=n(267),a=n(136),o=n(92),s=n(133),c=n(134),u=n(49);t.exports=function(t,e){return o(t)&&s(e)?c(u(t),e):function(n){var o=i(n,t);return void 0===o&&o===e?a(n,t):r(e,o,3)}}},function(t,e,n){var r=n(91);t.exports=function(t,e,n){var i=null==t?void 0:r(t,e);return void 0===i?n:i}},function(t,e,n){var r=n(269),i=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,a=/\\(\\)?/g,o=r((function(t){var e=[];return 46===t.charCodeAt(0)&&e.push(""),t.replace(i,(function(t,n,r,i){e.push(r?i.replace(a,"$1"):n||t)})),e}));t.exports=o},function(t,e,n){var r=n(270);t.exports=function(t){var e=r(t,(function(t){return 500===n.size&&n.clear(),t})),n=e.cache;return e}},function(t,e,n){var r=n(78);function i(t,e){if("function"!=typeof t||null!=e&&"function"!=typeof e)throw new TypeError("Expected a function");var n=function(){var r=arguments,i=e?e.apply(this,r):r[0],a=n.cache;if(a.has(i))return a.get(i);var o=t.apply(this,r);return n.cache=a.set(i,o)||a,o};return n.cache=new(i.Cache||r),n}i.Cache=r,t.exports=i},function(t,e,n){var r=n(38),i=n(66),a=n(5),o=n(42),s=r?r.prototype:void 0,c=s?s.toString:void 0;t.exports=function t(e){if("string"==typeof e)return e;if(a(e))return i(e,t)+"";if(o(e))return c?c.call(e):"";var n=e+"";return"0"==n&&1/e==-1/0?"-0":n}},function(t,e){t.exports=function(t,e){return null!=t&&e in Object(t)}},function(t,e,n){var r=n(138),i=n(274),a=n(92),o=n(49);t.exports=function(t){return a(t)?r(o(t)):i(t)}},function(t,e,n){var r=n(91);t.exports=function(t){return function(e){return r(e,t)}}},function(t,e){var n=Object.prototype.hasOwnProperty;t.exports=function(t,e){return null!=t&&n.call(t,e)}},function(t,e,n){var r=n(82),i=n(41),a=n(47),o=n(5),s=n(24),c=n(39),u=n(62),l=n(48),h=Object.prototype.hasOwnProperty;t.exports=function(t){if(null==t)return!0;if(s(t)&&(o(t)||"string"==typeof t||"function"==typeof t.splice||c(t)||l(t)||a(t)))return!t.length;var e=i(t);if("[object Map]"==e||"[object Set]"==e)return!t.size;if(u(t))return!r(t).length;for(var n in t)if(h.call(t,n))return!1;return!0}},function(t,e){t.exports=function(t,e,n,r){var i=-1,a=null==t?0:t.length;for(r&&a&&(n=t[++i]);++i<a;)n=e(n,t[i],i,t);return n}},function(t,e){t.exports=function(t,e,n,r,i){return i(t,(function(t,i,a){n=r?(r=!1,t):e(n,t,i,a)})),n}},function(t,e,n){var r=n(82),i=n(41),a=n(24),o=n(280),s=n(281);t.exports=function(t){if(null==t)return 0;if(a(t))return o(t)?s(t):t.length;var e=i(t);return"[object Map]"==e||"[object Set]"==e?t.size:r(t).length}},function(t,e,n){var r=n(34),i=n(5),a=n(21);t.exports=function(t){return"string"==typeof t||!i(t)&&a(t)&&"[object String]"==r(t)}},function(t,e,n){var r=n(282),i=n(283),a=n(284);t.exports=function(t){return i(t)?a(t):r(t)}},function(t,e,n){var r=n(138)("length");t.exports=r},function(t,e){var n=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");t.exports=function(t){return n.test(t)}},function(t,e){var n="[\\ud800-\\udfff]",r="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",i="\\ud83c[\\udffb-\\udfff]",a="[^\\ud800-\\udfff]",o="(?:\\ud83c[\\udde6-\\uddff]){2}",s="[\\ud800-\\udbff][\\udc00-\\udfff]",c="(?:"+r+"|"+i+")"+"?",u="[\\ufe0e\\ufe0f]?"+c+("(?:\\u200d(?:"+[a,o,s].join("|")+")[\\ufe0e\\ufe0f]?"+c+")*"),l="(?:"+[a+r+"?",r,o,s,n].join("|")+")",h=RegExp(i+"(?="+i+")|"+l+u,"g");t.exports=function(t){for(var e=h.lastIndex=0;h.test(t);)++e;return e}},function(t,e,n){var r=n(79),i=n(125),a=n(88),o=n(25),s=n(63),c=n(5),u=n(39),l=n(37),h=n(11),f=n(48);t.exports=function(t,e,n){var d=c(t),p=d||u(t)||f(t);if(e=o(e,4),null==n){var g=t&&t.constructor;n=p?d?new g:[]:h(t)&&l(g)?i(s(t)):{}}return(p?r:a)(t,(function(t,r,i){return e(n,t,r,i)})),n}},function(t,e,n){var r=n(94),i=n(67),a=n(291),o=n(146),s=i((function(t){return a(r(t,1,o,!0))}));t.exports=s},function(t,e,n){var r=n(38),i=n(47),a=n(5),o=r?r.isConcatSpreadable:void 0;t.exports=function(t){return a(t)||i(t)||!!(o&&t&&t[o])}},function(t,e){t.exports=function(t,e,n){switch(n.length){case 0:return t.call(e);case 1:return t.call(e,n[0]);case 2:return t.call(e,n[0],n[1]);case 3:return t.call(e,n[0],n[1],n[2])}return t.apply(e,n)}},function(t,e,n){var r=n(86),i=n(111),a=n(35),o=i?function(t,e){return i(t,"toString",{configurable:!0,enumerable:!1,value:r(e),writable:!0})}:a;t.exports=o},function(t,e){var n=Date.now;t.exports=function(t){var e=0,r=0;return function(){var i=n(),a=16-(i-r);if(r=i,a>0){if(++e>=800)return arguments[0]}else e=0;return t.apply(void 0,arguments)}}},function(t,e,n){var r=n(131),i=n(292),a=n(296),o=n(132),s=n(297),c=n(90);t.exports=function(t,e,n){var u=-1,l=i,h=t.length,f=!0,d=[],p=d;if(n)f=!1,l=a;else if(h>=200){var g=e?null:s(t);if(g)return c(g);f=!1,l=o,p=new r}else p=e?[]:d;t:for(;++u<h;){var y=t[u],v=e?e(y):y;if(y=n||0!==y?y:0,f&&v==v){for(var m=p.length;m--;)if(p[m]===v)continue t;e&&p.push(v),d.push(y)}else l(p,v,n)||(p!==d&&p.push(v),d.push(y))}return d}},function(t,e,n){var r=n(293);t.exports=function(t,e){return!!(null==t?0:t.length)&&r(t,e,0)>-1}},function(t,e,n){var r=n(145),i=n(294),a=n(295);t.exports=function(t,e,n){return e==e?a(t,e,n):r(t,i,n)}},function(t,e){t.exports=function(t){return t!=t}},function(t,e){t.exports=function(t,e,n){for(var r=n-1,i=t.length;++r<i;)if(t[r]===e)return r;return-1}},function(t,e){t.exports=function(t,e,n){for(var r=-1,i=null==t?0:t.length;++r<i;)if(n(e,t[r]))return!0;return!1}},function(t,e,n){var r=n(121),i=n(298),a=n(90),o=r&&1/a(new r([,-0]))[1]==1/0?function(t){return new r(t)}:i;t.exports=o},function(t,e){t.exports=function(){}},function(t,e,n){var r=n(66);t.exports=function(t,e){return r(e,(function(e){return t[e]}))}},function(t,e){t.exports="2.1.8"},function(t,e,n){var r=n(10),i=n(76);function a(t){return r.map(t.nodes(),(function(e){var n=t.node(e),i=t.parent(e),a={v:e};return r.isUndefined(n)||(a.value=n),r.isUndefined(i)||(a.parent=i),a}))}function o(t){return r.map(t.edges(),(function(e){var n=t.edge(e),i={v:e.v,w:e.w};return r.isUndefined(e.name)||(i.name=e.name),r.isUndefined(n)||(i.value=n),i}))}t.exports={write:function(t){var e={options:{directed:t.isDirected(),multigraph:t.isMultigraph(),compound:t.isCompound()},nodes:a(t),edges:o(t)};r.isUndefined(t.graph())||(e.value=r.clone(t.graph()));return e},read:function(t){var e=new i(t.options).setGraph(t.value);return r.each(t.nodes,(function(t){e.setNode(t.v,t.value),t.parent&&e.setParent(t.v,t.parent)})),r.each(t.edges,(function(t){e.setEdge({v:t.v,w:t.w,name:t.name},t.value)})),e}}},function(t,e,n){t.exports={components:n(303),dijkstra:n(148),dijkstraAll:n(304),findCycles:n(305),floydWarshall:n(306),isAcyclic:n(307),postorder:n(308),preorder:n(309),prim:n(310),tarjan:n(150),topsort:n(151)}},function(t,e,n){var r=n(10);t.exports=function(t){var e,n={},i=[];function a(i){r.has(n,i)||(n[i]=!0,e.push(i),r.each(t.successors(i),a),r.each(t.predecessors(i),a))}return r.each(t.nodes(),(function(t){e=[],a(t),e.length&&i.push(e)})),i}},function(t,e,n){var r=n(148),i=n(10);t.exports=function(t,e,n){return i.transform(t.nodes(),(function(i,a){i[a]=r(t,a,e,n)}),{})}},function(t,e,n){var r=n(10),i=n(150);t.exports=function(t){return r.filter(i(t),(function(e){return e.length>1||1===e.length&&t.hasEdge(e[0],e[0])}))}},function(t,e,n){var r=n(10);t.exports=function(t,e,n){return function(t,e,n){var r={},i=t.nodes();return i.forEach((function(t){r[t]={},r[t][t]={distance:0},i.forEach((function(e){t!==e&&(r[t][e]={distance:Number.POSITIVE_INFINITY})})),n(t).forEach((function(n){var i=n.v===t?n.w:n.v,a=e(n);r[t][i]={distance:a,predecessor:t}}))})),i.forEach((function(t){var e=r[t];i.forEach((function(n){var a=r[n];i.forEach((function(n){var r=a[t],i=e[n],o=a[n],s=r.distance+i.distance;s<o.distance&&(o.distance=s,o.predecessor=i.predecessor)}))}))})),r}(t,e||i,n||function(e){return t.outEdges(e)})};var i=r.constant(1)},function(t,e,n){var r=n(151);t.exports=function(t){try{r(t)}catch(t){if(t instanceof r.CycleException)return!1;throw t}return!0}},function(t,e,n){var r=n(152);t.exports=function(t,e){return r(t,e,"post")}},function(t,e,n){var r=n(152);t.exports=function(t,e){return r(t,e,"pre")}},function(t,e,n){var r=n(10),i=n(76),a=n(149);t.exports=function(t,e){var n,o=new i,s={},c=new a;function u(t){var r=t.v===n?t.w:t.v,i=c.priority(r);if(void 0!==i){var a=e(t);a<i&&(s[r]=n,c.decrease(r,a))}}if(0===t.nodeCount())return o;r.each(t.nodes(),(function(t){c.add(t,Number.POSITIVE_INFINITY),o.setNode(t)})),c.decrease(t.nodes()[0],0);var l=!1;for(;c.size()>0;){if(n=c.removeMin(),r.has(s,n))o.setEdge(n,s[n]);else{if(l)throw new Error("Input graph is not connected: "+t);l=!0}t.nodeEdges(n).forEach(u)}return o}},function(t,e,n){var r;try{r=n(3)}catch(t){}r||(r=window.graphlib),t.exports=r},function(t,e,n){"use strict";var r=n(4),i=n(345),a=n(348),o=n(349),s=n(8).normalizeRanks,c=n(351),u=n(8).removeEmptyRanks,l=n(352),h=n(353),f=n(354),d=n(355),p=n(364),g=n(8),y=n(17).Graph;t.exports=function(t,e){var n=e&&e.debugTiming?g.time:g.notime;n("layout",(function(){var e=n(" buildLayoutGraph",(function(){return function(t){var e=new y({multigraph:!0,compound:!0}),n=C(t.graph());return e.setGraph(r.merge({},m,T(n,v),r.pick(n,b))),r.forEach(t.nodes(),(function(n){var i=C(t.node(n));e.setNode(n,r.defaults(T(i,x),_)),e.setParent(n,t.parent(n))})),r.forEach(t.edges(),(function(n){var i=C(t.edge(n));e.setEdge(n,r.merge({},w,T(i,k),r.pick(i,E)))})),e}(t)}));n(" runLayout",(function(){!function(t,e){e(" makeSpaceForEdgeLabels",(function(){!function(t){var e=t.graph();e.ranksep/=2,r.forEach(t.edges(),(function(n){var r=t.edge(n);r.minlen*=2,"c"!==r.labelpos.toLowerCase()&&("TB"===e.rankdir||"BT"===e.rankdir?r.width+=r.labeloffset:r.height+=r.labeloffset)}))}(t)})),e(" removeSelfEdges",(function(){!function(t){r.forEach(t.edges(),(function(e){if(e.v===e.w){var n=t.node(e.v);n.selfEdges||(n.selfEdges=[]),n.selfEdges.push({e:e,label:t.edge(e)}),t.removeEdge(e)}}))}(t)})),e(" acyclic",(function(){i.run(t)})),e(" nestingGraph.run",(function(){l.run(t)})),e(" rank",(function(){o(g.asNonCompoundGraph(t))})),e(" injectEdgeLabelProxies",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.width&&n.height){var r=t.node(e.v),i={rank:(t.node(e.w).rank-r.rank)/2+r.rank,e:e};g.addDummyNode(t,"edge-proxy",i,"_ep")}}))}(t)})),e(" removeEmptyRanks",(function(){u(t)})),e(" nestingGraph.cleanup",(function(){l.cleanup(t)})),e(" normalizeRanks",(function(){s(t)})),e(" assignRankMinMax",(function(){!function(t){var e=0;r.forEach(t.nodes(),(function(n){var i=t.node(n);i.borderTop&&(i.minRank=t.node(i.borderTop).rank,i.maxRank=t.node(i.borderBottom).rank,e=r.max(e,i.maxRank))})),t.graph().maxRank=e}(t)})),e(" removeEdgeLabelProxies",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);"edge-proxy"===n.dummy&&(t.edge(n.e).labelRank=n.rank,t.removeNode(e))}))}(t)})),e(" normalize.run",(function(){a.run(t)})),e(" parentDummyChains",(function(){c(t)})),e(" addBorderSegments",(function(){h(t)})),e(" order",(function(){d(t)})),e(" insertSelfEdges",(function(){!function(t){var e=g.buildLayerMatrix(t);r.forEach(e,(function(e){var n=0;r.forEach(e,(function(e,i){var a=t.node(e);a.order=i+n,r.forEach(a.selfEdges,(function(e){g.addDummyNode(t,"selfedge",{width:e.label.width,height:e.label.height,rank:a.rank,order:i+ ++n,e:e.e,label:e.label},"_se")})),delete a.selfEdges}))}))}(t)})),e(" adjustCoordinateSystem",(function(){f.adjust(t)})),e(" position",(function(){p(t)})),e(" positionSelfEdges",(function(){!function(t){r.forEach(t.nodes(),(function(e){var n=t.node(e);if("selfedge"===n.dummy){var r=t.node(n.e.v),i=r.x+r.width/2,a=r.y,o=n.x-i,s=r.height/2;t.setEdge(n.e,n.label),t.removeNode(e),n.label.points=[{x:i+2*o/3,y:a-s},{x:i+5*o/6,y:a-s},{x:i+o,y:a},{x:i+5*o/6,y:a+s},{x:i+2*o/3,y:a+s}],n.label.x=n.x,n.label.y=n.y}}))}(t)})),e(" removeBorderNodes",(function(){!function(t){r.forEach(t.nodes(),(function(e){if(t.children(e).length){var n=t.node(e),i=t.node(n.borderTop),a=t.node(n.borderBottom),o=t.node(r.last(n.borderLeft)),s=t.node(r.last(n.borderRight));n.width=Math.abs(s.x-o.x),n.height=Math.abs(a.y-i.y),n.x=o.x+n.width/2,n.y=i.y+n.height/2}})),r.forEach(t.nodes(),(function(e){"border"===t.node(e).dummy&&t.removeNode(e)}))}(t)})),e(" normalize.undo",(function(){a.undo(t)})),e(" fixupEdgeLabelCoords",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(r.has(n,"x"))switch("l"!==n.labelpos&&"r"!==n.labelpos||(n.width-=n.labeloffset),n.labelpos){case"l":n.x-=n.width/2+n.labeloffset;break;case"r":n.x+=n.width/2+n.labeloffset}}))}(t)})),e(" undoCoordinateSystem",(function(){f.undo(t)})),e(" translateGraph",(function(){!function(t){var e=Number.POSITIVE_INFINITY,n=0,i=Number.POSITIVE_INFINITY,a=0,o=t.graph(),s=o.marginx||0,c=o.marginy||0;function u(t){var r=t.x,o=t.y,s=t.width,c=t.height;e=Math.min(e,r-s/2),n=Math.max(n,r+s/2),i=Math.min(i,o-c/2),a=Math.max(a,o+c/2)}r.forEach(t.nodes(),(function(e){u(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.has(n,"x")&&u(n)})),e-=s,i-=c,r.forEach(t.nodes(),(function(n){var r=t.node(n);r.x-=e,r.y-=i})),r.forEach(t.edges(),(function(n){var a=t.edge(n);r.forEach(a.points,(function(t){t.x-=e,t.y-=i})),r.has(a,"x")&&(a.x-=e),r.has(a,"y")&&(a.y-=i)})),o.width=n-e+s,o.height=a-i+c}(t)})),e(" assignNodeIntersects",(function(){!function(t){r.forEach(t.edges(),(function(e){var n,r,i=t.edge(e),a=t.node(e.v),o=t.node(e.w);i.points?(n=i.points[0],r=i.points[i.points.length-1]):(i.points=[],n=o,r=a),i.points.unshift(g.intersectRect(a,n)),i.points.push(g.intersectRect(o,r))}))}(t)})),e(" reversePoints",(function(){!function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);n.reversed&&n.points.reverse()}))}(t)})),e(" acyclic.undo",(function(){i.undo(t)}))}(e,n)})),n(" updateInputGraph",(function(){!function(t,e){r.forEach(t.nodes(),(function(n){var r=t.node(n),i=e.node(n);r&&(r.x=i.x,r.y=i.y,e.children(n).length&&(r.width=i.width,r.height=i.height))})),r.forEach(t.edges(),(function(n){var i=t.edge(n),a=e.edge(n);i.points=a.points,r.has(a,"x")&&(i.x=a.x,i.y=a.y)})),t.graph().width=e.graph().width,t.graph().height=e.graph().height}(t,e)}))}))};var v=["nodesep","edgesep","ranksep","marginx","marginy"],m={ranksep:50,edgesep:20,nodesep:50,rankdir:"tb"},b=["acyclicer","ranker","rankdir","align"],x=["width","height"],_={width:0,height:0},k=["minlen","weight","width","height","labeloffset"],w={minlen:1,weight:1,width:0,height:0,labeloffset:10,labelpos:"r"},E=["labelpos"];function T(t,e){return r.mapValues(r.pick(t,e),Number)}function C(t){var e={};return r.forEach(t,(function(t,n){e[n.toLowerCase()]=t})),e}},function(t,e,n){var r=n(108);t.exports=function(t){return r(t,5)}},function(t,e,n){var r=n(315)(n(316));t.exports=r},function(t,e,n){var r=n(25),i=n(24),a=n(30);t.exports=function(t){return function(e,n,o){var s=Object(e);if(!i(e)){var c=r(n,3);e=a(e),n=function(t){return c(s[t],t,s)}}var u=t(e,n,o);return u>-1?s[c?e[u]:u]:void 0}}},function(t,e,n){var r=n(145),i=n(25),a=n(317),o=Math.max;t.exports=function(t,e,n){var s=null==t?0:t.length;if(!s)return-1;var c=null==n?0:a(n);return c<0&&(c=o(s+c,0)),r(t,i(e,3),c)}},function(t,e,n){var r=n(155);t.exports=function(t){var e=r(t),n=e%1;return e==e?n?e-n:e:0}},function(t,e,n){var r=n(11),i=n(42),a=/^\s+|\s+$/g,o=/^[-+]0x[0-9a-f]+$/i,s=/^0b[01]+$/i,c=/^0o[0-7]+$/i,u=parseInt;t.exports=function(t){if("number"==typeof t)return t;if(i(t))return NaN;if(r(t)){var e="function"==typeof t.valueOf?t.valueOf():t;t=r(e)?e+"":e}if("string"!=typeof t)return 0===t?t:+t;t=t.replace(a,"");var n=s.test(t);return n||c.test(t)?u(t.slice(2),n?2:8):o.test(t)?NaN:+t}},function(t,e,n){var r=n(89),i=n(127),a=n(40);t.exports=function(t,e){return null==t?t:r(t,i(e),a)}},function(t,e){t.exports=function(t){var e=null==t?0:t.length;return e?t[e-1]:void 0}},function(t,e,n){var r=n(59),i=n(88),a=n(25);t.exports=function(t,e){var n={};return e=a(e,3),i(t,(function(t,i,a){r(n,i,e(t,i,a))})),n}},function(t,e,n){var r=n(95),i=n(323),a=n(35);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},function(t,e){t.exports=function(t,e){return t>e}},function(t,e,n){var r=n(325),i=n(328)((function(t,e,n){r(t,e,n)}));t.exports=i},function(t,e,n){var r=n(53),i=n(157),a=n(89),o=n(326),s=n(11),c=n(40),u=n(159);t.exports=function t(e,n,l,h,f){e!==n&&a(n,(function(a,c){if(f||(f=new r),s(a))o(e,n,c,l,t,h,f);else{var d=h?h(u(e,c),a,c+"",e,n,f):void 0;void 0===d&&(d=a),i(e,c,d)}}),c)}},function(t,e,n){var r=n(157),i=n(114),a=n(123),o=n(115),s=n(124),c=n(47),u=n(5),l=n(146),h=n(39),f=n(37),d=n(11),p=n(158),g=n(48),y=n(159),v=n(327);t.exports=function(t,e,n,m,b,x,_){var k=y(t,n),w=y(e,n),E=_.get(w);if(E)r(t,n,E);else{var T=x?x(k,w,n+"",t,e,_):void 0,C=void 0===T;if(C){var A=u(w),S=!A&&h(w),M=!A&&!S&&g(w);T=w,A||S||M?u(k)?T=k:l(k)?T=o(k):S?(C=!1,T=i(w,!0)):M?(C=!1,T=a(w,!0)):T=[]:p(w)||c(w)?(T=k,c(k)?T=v(k):d(k)&&!f(k)||(T=s(w))):C=!1}C&&(_.set(w,T),b(T,w,m,x,_),_.delete(w)),r(t,n,T)}}},function(t,e,n){var r=n(46),i=n(40);t.exports=function(t){return r(t,i(t))}},function(t,e,n){var r=n(67),i=n(68);t.exports=function(t){return r((function(e,n){var r=-1,a=n.length,o=a>1?n[a-1]:void 0,s=a>2?n[2]:void 0;for(o=t.length>3&&"function"==typeof o?(a--,o):void 0,s&&i(n[0],n[1],s)&&(o=a<3?void 0:o,a=1),e=Object(e);++r<a;){var c=n[r];c&&t(e,c,r,o)}return e}))}},function(t,e,n){var r=n(95),i=n(160),a=n(35);t.exports=function(t){return t&&t.length?r(t,a,i):void 0}},function(t,e,n){var r=n(95),i=n(25),a=n(160);t.exports=function(t,e){return t&&t.length?r(t,i(e,2),a):void 0}},function(t,e,n){var r=n(16);t.exports=function(){return r.Date.now()}},function(t,e,n){var r=n(333),i=n(136);t.exports=function(t,e){return r(t,e,(function(e,n){return i(t,n)}))}},function(t,e,n){var r=n(91),i=n(334),a=n(65);t.exports=function(t,e,n){for(var o=-1,s=e.length,c={};++o<s;){var u=e[o],l=r(t,u);n(l,u)&&i(c,a(u,t),l)}return c}},function(t,e,n){var r=n(58),i=n(65),a=n(60),o=n(11),s=n(49);t.exports=function(t,e,n,c){if(!o(t))return t;for(var u=-1,l=(e=i(e,t)).length,h=l-1,f=t;null!=f&&++u<l;){var d=s(e[u]),p=n;if(u!=h){var g=f[d];void 0===(p=c?c(g,d,f):void 0)&&(p=o(g)?g:a(e[u+1])?[]:{})}r(f,d,p),f=f[d]}return t}},function(t,e,n){var r=n(156),i=n(143),a=n(144);t.exports=function(t){return a(i(t,void 0,r),t+"")}},function(t,e,n){var r=n(337),i=n(68),a=n(155);t.exports=function(t){return function(e,n,o){return o&&"number"!=typeof o&&i(e,n,o)&&(n=o=void 0),e=a(e),void 0===n?(n=e,e=0):n=a(n),o=void 0===o?e<n?1:-1:a(o),r(e,n,o,t)}}},function(t,e){var n=Math.ceil,r=Math.max;t.exports=function(t,e,i,a){for(var o=-1,s=r(n((e-t)/(i||1)),0),c=Array(s);s--;)c[a?s:++o]=t,t+=i;return c}},function(t,e,n){var r=n(94),i=n(339),a=n(67),o=n(68),s=a((function(t,e){if(null==t)return[];var n=e.length;return n>1&&o(t,e[0],e[1])?e=[]:n>2&&o(e[0],e[1],e[2])&&(e=[e[0]]),i(t,r(e,1),[])}));t.exports=s},function(t,e,n){var r=n(66),i=n(25),a=n(141),o=n(340),s=n(61),c=n(341),u=n(35);t.exports=function(t,e,n){var l=-1;e=r(e.length?e:[u],s(i));var h=a(t,(function(t,n,i){return{criteria:r(e,(function(e){return e(t)})),index:++l,value:t}}));return o(h,(function(t,e){return c(t,e,n)}))}},function(t,e){t.exports=function(t,e){var n=t.length;for(t.sort(e);n--;)t[n]=t[n].value;return t}},function(t,e,n){var r=n(342);t.exports=function(t,e,n){for(var i=-1,a=t.criteria,o=e.criteria,s=a.length,c=n.length;++i<s;){var u=r(a[i],o[i]);if(u)return i>=c?u:u*("desc"==n[i]?-1:1)}return t.index-e.index}},function(t,e,n){var r=n(42);t.exports=function(t,e){if(t!==e){var n=void 0!==t,i=null===t,a=t==t,o=r(t),s=void 0!==e,c=null===e,u=e==e,l=r(e);if(!c&&!l&&!o&&t>e||o&&s&&u&&!c&&!l||i&&s&&u||!n&&u||!a)return 1;if(!i&&!o&&!l&&t<e||l&&n&&a&&!i&&!o||c&&n&&a||!s&&a||!u)return-1}return 0}},function(t,e,n){var r=n(58),i=n(344);t.exports=function(t,e){return i(t||[],e||[],r)}},function(t,e){t.exports=function(t,e,n){for(var r=-1,i=t.length,a=e.length,o={};++r<i;){var s=r<a?e[r]:void 0;n(o,t[r],s)}return o}},function(t,e,n){"use strict";var r=n(4),i=n(346);t.exports={run:function(t){var e="greedy"===t.graph().acyclicer?i(t,function(t){return function(e){return t.edge(e).weight}}(t)):function(t){var e=[],n={},i={};function a(o){r.has(i,o)||(i[o]=!0,n[o]=!0,r.forEach(t.outEdges(o),(function(t){r.has(n,t.w)?e.push(t):a(t.w)})),delete n[o])}return r.forEach(t.nodes(),a),e}(t);r.forEach(e,(function(e){var n=t.edge(e);t.removeEdge(e),n.forwardName=e.name,n.reversed=!0,t.setEdge(e.w,e.v,n,r.uniqueId("rev"))}))},undo:function(t){r.forEach(t.edges(),(function(e){var n=t.edge(e);if(n.reversed){t.removeEdge(e);var r=n.forwardName;delete n.reversed,delete n.forwardName,t.setEdge(e.w,e.v,n,r)}}))}}},function(t,e,n){var r=n(4),i=n(17).Graph,a=n(347);t.exports=function(t,e){if(t.nodeCount()<=1)return[];var n=function(t,e){var n=new i,o=0,s=0;r.forEach(t.nodes(),(function(t){n.setNode(t,{v:t,in:0,out:0})})),r.forEach(t.edges(),(function(t){var r=n.edge(t.v,t.w)||0,i=e(t),a=r+i;n.setEdge(t.v,t.w,a),s=Math.max(s,n.node(t.v).out+=i),o=Math.max(o,n.node(t.w).in+=i)}));var u=r.range(s+o+3).map((function(){return new a})),l=o+1;return r.forEach(n.nodes(),(function(t){c(u,l,n.node(t))})),{graph:n,buckets:u,zeroIdx:l}}(t,e||o),u=function(t,e,n){var r,i=[],a=e[e.length-1],o=e[0];for(;t.nodeCount();){for(;r=o.dequeue();)s(t,e,n,r);for(;r=a.dequeue();)s(t,e,n,r);if(t.nodeCount())for(var c=e.length-2;c>0;--c)if(r=e[c].dequeue()){i=i.concat(s(t,e,n,r,!0));break}}return i}(n.graph,n.buckets,n.zeroIdx);return r.flatten(r.map(u,(function(e){return t.outEdges(e.v,e.w)})),!0)};var o=r.constant(1);function s(t,e,n,i,a){var o=a?[]:void 0;return r.forEach(t.inEdges(i.v),(function(r){var i=t.edge(r),s=t.node(r.v);a&&o.push({v:r.v,w:r.w}),s.out-=i,c(e,n,s)})),r.forEach(t.outEdges(i.v),(function(r){var i=t.edge(r),a=r.w,o=t.node(a);o.in-=i,c(e,n,o)})),t.removeNode(i.v),o}function c(t,e,n){n.out?n.in?t[n.out-n.in+e].enqueue(n):t[t.length-1].enqueue(n):t[0].enqueue(n)}},function(t,e){function n(){var t={};t._next=t._prev=t,this._sentinel=t}function r(t){t._prev._next=t._next,t._next._prev=t._prev,delete t._next,delete t._prev}function i(t,e){if("_next"!==t&&"_prev"!==t)return e}t.exports=n,n.prototype.dequeue=function(){var t=this._sentinel,e=t._prev;if(e!==t)return r(e),e},n.prototype.enqueue=function(t){var e=this._sentinel;t._prev&&t._next&&r(t),t._next=e._next,e._next._prev=t,e._next=t,t._prev=e},n.prototype.toString=function(){for(var t=[],e=this._sentinel,n=e._prev;n!==e;)t.push(JSON.stringify(n,i)),n=n._prev;return"["+t.join(", ")+"]"}},function(t,e,n){"use strict";var r=n(4),i=n(8);t.exports={run:function(t){t.graph().dummyChains=[],r.forEach(t.edges(),(function(e){!function(t,e){var n,r,a,o=e.v,s=t.node(o).rank,c=e.w,u=t.node(c).rank,l=e.name,h=t.edge(e),f=h.labelRank;if(u===s+1)return;for(t.removeEdge(e),a=0,++s;s<u;++a,++s)h.points=[],r={width:0,height:0,edgeLabel:h,edgeObj:e,rank:s},n=i.addDummyNode(t,"edge",r,"_d"),s===f&&(r.width=h.width,r.height=h.height,r.dummy="edge-label",r.labelpos=h.labelpos),t.setEdge(o,n,{weight:h.weight},l),0===a&&t.graph().dummyChains.push(n),o=n;t.setEdge(o,c,{weight:h.weight},l)}(t,e)}))},undo:function(t){r.forEach(t.graph().dummyChains,(function(e){var n,r=t.node(e),i=r.edgeLabel;for(t.setEdge(r.edgeObj,i);r.dummy;)n=t.successors(e)[0],t.removeNode(e),i.points.push({x:r.x,y:r.y}),"edge-label"===r.dummy&&(i.x=r.x,i.y=r.y,i.width=r.width,i.height=r.height),e=n,r=t.node(e)}))}}},function(t,e,n){"use strict";var r=n(69).longestPath,i=n(164),a=n(350);t.exports=function(t){switch(t.graph().ranker){case"network-simplex":s(t);break;case"tight-tree":!function(t){r(t),i(t)}(t);break;case"longest-path":o(t);break;default:s(t)}};var o=r;function s(t){a(t)}},function(t,e,n){"use strict";var r=n(4),i=n(164),a=n(69).slack,o=n(69).longestPath,s=n(17).alg.preorder,c=n(17).alg.postorder,u=n(8).simplify;function l(t){t=u(t),o(t);var e,n=i(t);for(d(n),h(n,t);e=g(n);)v(n,t,e,y(n,t,e))}function h(t,e){var n=c(t,t.nodes());n=n.slice(0,n.length-1),r.forEach(n,(function(n){!function(t,e,n){var r=t.node(n).parent;t.edge(n,r).cutvalue=f(t,e,n)}(t,e,n)}))}function f(t,e,n){var i=t.node(n).parent,a=!0,o=e.edge(n,i),s=0;return o||(a=!1,o=e.edge(i,n)),s=o.weight,r.forEach(e.nodeEdges(n),(function(r){var o,c,u=r.v===n,l=u?r.w:r.v;if(l!==i){var h=u===a,f=e.edge(r).weight;if(s+=h?f:-f,o=n,c=l,t.hasEdge(o,c)){var d=t.edge(n,l).cutvalue;s+=h?-d:d}}})),s}function d(t,e){arguments.length<2&&(e=t.nodes()[0]),p(t,{},1,e)}function p(t,e,n,i,a){var o=n,s=t.node(i);return e[i]=!0,r.forEach(t.neighbors(i),(function(a){r.has(e,a)||(n=p(t,e,n,a,i))})),s.low=o,s.lim=n++,a?s.parent=a:delete s.parent,n}function g(t){return r.find(t.edges(),(function(e){return t.edge(e).cutvalue<0}))}function y(t,e,n){var i=n.v,o=n.w;e.hasEdge(i,o)||(i=n.w,o=n.v);var s=t.node(i),c=t.node(o),u=s,l=!1;s.lim>c.lim&&(u=c,l=!0);var h=r.filter(e.edges(),(function(e){return l===m(t,t.node(e.v),u)&&l!==m(t,t.node(e.w),u)}));return r.minBy(h,(function(t){return a(e,t)}))}function v(t,e,n,i){var a=n.v,o=n.w;t.removeEdge(a,o),t.setEdge(i.v,i.w,{}),d(t),h(t,e),function(t,e){var n=r.find(t.nodes(),(function(t){return!e.node(t).parent})),i=s(t,n);i=i.slice(1),r.forEach(i,(function(n){var r=t.node(n).parent,i=e.edge(n,r),a=!1;i||(i=e.edge(r,n),a=!0),e.node(n).rank=e.node(r).rank+(a?i.minlen:-i.minlen)}))}(t,e)}function m(t,e,n){return n.low<=e.lim&&e.lim<=n.lim}t.exports=l,l.initLowLimValues=d,l.initCutValues=h,l.calcCutValue=f,l.leaveEdge=g,l.enterEdge=y,l.exchangeEdges=v},function(t,e,n){var r=n(4);t.exports=function(t){var e=function(t){var e={},n=0;function i(a){var o=n;r.forEach(t.children(a),i),e[a]={low:o,lim:n++}}return r.forEach(t.children(),i),e}(t);r.forEach(t.graph().dummyChains,(function(n){for(var r=t.node(n),i=r.edgeObj,a=function(t,e,n,r){var i,a,o=[],s=[],c=Math.min(e[n].low,e[r].low),u=Math.max(e[n].lim,e[r].lim);i=n;do{i=t.parent(i),o.push(i)}while(i&&(e[i].low>c||u>e[i].lim));a=i,i=r;for(;(i=t.parent(i))!==a;)s.push(i);return{path:o.concat(s.reverse()),lca:a}}(t,e,i.v,i.w),o=a.path,s=a.lca,c=0,u=o[c],l=!0;n!==i.w;){if(r=t.node(n),l){for(;(u=o[c])!==s&&t.node(u).maxRank<r.rank;)c++;u===s&&(l=!1)}if(!l){for(;c<o.length-1&&t.node(u=o[c+1]).minRank<=r.rank;)c++;u=o[c]}t.setParent(n,u),n=t.successors(n)[0]}}))}},function(t,e,n){var r=n(4),i=n(8);t.exports={run:function(t){var e=i.addDummyNode(t,"root",{},"_root"),n=function(t){var e={};return r.forEach(t.children(),(function(n){!function n(i,a){var o=t.children(i);o&&o.length&&r.forEach(o,(function(t){n(t,a+1)}));e[i]=a}(n,1)})),e}(t),a=r.max(r.values(n))-1,o=2*a+1;t.graph().nestingRoot=e,r.forEach(t.edges(),(function(e){t.edge(e).minlen*=o}));var s=function(t){return r.reduce(t.edges(),(function(e,n){return e+t.edge(n).weight}),0)}(t)+1;r.forEach(t.children(),(function(c){!function t(e,n,a,o,s,c,u){var l=e.children(u);if(!l.length)return void(u!==n&&e.setEdge(n,u,{weight:0,minlen:a}));var h=i.addBorderNode(e,"_bt"),f=i.addBorderNode(e,"_bb"),d=e.node(u);e.setParent(h,u),d.borderTop=h,e.setParent(f,u),d.borderBottom=f,r.forEach(l,(function(r){t(e,n,a,o,s,c,r);var i=e.node(r),l=i.borderTop?i.borderTop:r,d=i.borderBottom?i.borderBottom:r,p=i.borderTop?o:2*o,g=l!==d?1:s-c[u]+1;e.setEdge(h,l,{weight:p,minlen:g,nestingEdge:!0}),e.setEdge(d,f,{weight:p,minlen:g,nestingEdge:!0})})),e.parent(u)||e.setEdge(n,h,{weight:0,minlen:s+c[u]})}(t,e,o,s,a,n,c)})),t.graph().nodeRankFactor=o},cleanup:function(t){var e=t.graph();t.removeNode(e.nestingRoot),delete e.nestingRoot,r.forEach(t.edges(),(function(e){t.edge(e).nestingEdge&&t.removeEdge(e)}))}}},function(t,e,n){var r=n(4),i=n(8);function a(t,e,n,r,a,o){var s={width:0,height:0,rank:o,borderType:e},c=a[e][o-1],u=i.addDummyNode(t,"border",s,n);a[e][o]=u,t.setParent(u,r),c&&t.setEdge(c,u,{weight:1})}t.exports=function(t){r.forEach(t.children(),(function e(n){var i=t.children(n),o=t.node(n);if(i.length&&r.forEach(i,e),r.has(o,"minRank")){o.borderLeft=[],o.borderRight=[];for(var s=o.minRank,c=o.maxRank+1;s<c;++s)a(t,"borderLeft","_bl",n,o,s),a(t,"borderRight","_br",n,o,s)}}))}},function(t,e,n){"use strict";var r=n(4);function i(t){r.forEach(t.nodes(),(function(e){a(t.node(e))})),r.forEach(t.edges(),(function(e){a(t.edge(e))}))}function a(t){var e=t.width;t.width=t.height,t.height=e}function o(t){t.y=-t.y}function s(t){var e=t.x;t.x=t.y,t.y=e}t.exports={adjust:function(t){var e=t.graph().rankdir.toLowerCase();"lr"!==e&&"rl"!==e||i(t)},undo:function(t){var e=t.graph().rankdir.toLowerCase();"bt"!==e&&"rl"!==e||function(t){r.forEach(t.nodes(),(function(e){o(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,o),r.has(n,"y")&&o(n)}))}(t);"lr"!==e&&"rl"!==e||(!function(t){r.forEach(t.nodes(),(function(e){s(t.node(e))})),r.forEach(t.edges(),(function(e){var n=t.edge(e);r.forEach(n.points,s),r.has(n,"x")&&s(n)}))}(t),i(t))}}},function(t,e,n){"use strict";var r=n(4),i=n(356),a=n(357),o=n(358),s=n(362),c=n(363),u=n(17).Graph,l=n(8);function h(t,e,n){return r.map(e,(function(e){return s(t,e,n)}))}function f(t,e){var n=new u;r.forEach(t,(function(t){var i=t.graph().root,a=o(t,i,n,e);r.forEach(a.vs,(function(e,n){t.node(e).order=n})),c(t,n,a.vs)}))}function d(t,e){r.forEach(e,(function(e){r.forEach(e,(function(e,n){t.node(e).order=n}))}))}t.exports=function(t){var e=l.maxRank(t),n=h(t,r.range(1,e+1),"inEdges"),o=h(t,r.range(e-1,-1,-1),"outEdges"),s=i(t);d(t,s);for(var c,u=Number.POSITIVE_INFINITY,p=0,g=0;g<4;++p,++g){f(p%2?n:o,p%4>=2),s=l.buildLayerMatrix(t);var y=a(t,s);y<u&&(g=0,c=r.cloneDeep(s),u=y)}d(t,c)}},function(t,e,n){"use strict";var r=n(4);t.exports=function(t){var e={},n=r.filter(t.nodes(),(function(e){return!t.children(e).length})),i=r.max(r.map(n,(function(e){return t.node(e).rank}))),a=r.map(r.range(i+1),(function(){return[]}));var o=r.sortBy(n,(function(e){return t.node(e).rank}));return r.forEach(o,(function n(i){if(r.has(e,i))return;e[i]=!0;var o=t.node(i);a[o.rank].push(i),r.forEach(t.successors(i),n)})),a}},function(t,e,n){"use strict";var r=n(4);function i(t,e,n){for(var i=r.zipObject(n,r.map(n,(function(t,e){return e}))),a=r.flatten(r.map(e,(function(e){return r.sortBy(r.map(t.outEdges(e),(function(e){return{pos:i[e.w],weight:t.edge(e).weight}})),"pos")})),!0),o=1;o<n.length;)o<<=1;var s=2*o-1;o-=1;var c=r.map(new Array(s),(function(){return 0})),u=0;return r.forEach(a.forEach((function(t){var e=t.pos+o;c[e]+=t.weight;for(var n=0;e>0;)e%2&&(n+=c[e+1]),c[e=e-1>>1]+=t.weight;u+=t.weight*n}))),u}t.exports=function(t,e){for(var n=0,r=1;r<e.length;++r)n+=i(t,e[r-1],e[r]);return n}},function(t,e,n){var r=n(4),i=n(359),a=n(360),o=n(361);t.exports=function t(e,n,s,c){var u=e.children(n),l=e.node(n),h=l?l.borderLeft:void 0,f=l?l.borderRight:void 0,d={};h&&(u=r.filter(u,(function(t){return t!==h&&t!==f})));var p=i(e,u);r.forEach(p,(function(n){if(e.children(n.v).length){var i=t(e,n.v,s,c);d[n.v]=i,r.has(i,"barycenter")&&(a=n,o=i,r.isUndefined(a.barycenter)?(a.barycenter=o.barycenter,a.weight=o.weight):(a.barycenter=(a.barycenter*a.weight+o.barycenter*o.weight)/(a.weight+o.weight),a.weight+=o.weight))}var a,o}));var g=a(p,s);!function(t,e){r.forEach(t,(function(t){t.vs=r.flatten(t.vs.map((function(t){return e[t]?e[t].vs:t})),!0)}))}(g,d);var y=o(g,c);if(h&&(y.vs=r.flatten([h,y.vs,f],!0),e.predecessors(h).length)){var v=e.node(e.predecessors(h)[0]),m=e.node(e.predecessors(f)[0]);r.has(y,"barycenter")||(y.barycenter=0,y.weight=0),y.barycenter=(y.barycenter*y.weight+v.order+m.order)/(y.weight+2),y.weight+=2}return y}},function(t,e,n){var r=n(4);t.exports=function(t,e){return r.map(e,(function(e){var n=t.inEdges(e);if(n.length){var i=r.reduce(n,(function(e,n){var r=t.edge(n),i=t.node(n.v);return{sum:e.sum+r.weight*i.order,weight:e.weight+r.weight}}),{sum:0,weight:0});return{v:e,barycenter:i.sum/i.weight,weight:i.weight}}return{v:e}}))}},function(t,e,n){"use strict";var r=n(4);t.exports=function(t,e){var n={};return r.forEach(t,(function(t,e){var i=n[t.v]={indegree:0,in:[],out:[],vs:[t.v],i:e};r.isUndefined(t.barycenter)||(i.barycenter=t.barycenter,i.weight=t.weight)})),r.forEach(e.edges(),(function(t){var e=n[t.v],i=n[t.w];r.isUndefined(e)||r.isUndefined(i)||(i.indegree++,e.out.push(n[t.w]))})),function(t){var e=[];function n(t){return function(e){e.merged||(r.isUndefined(e.barycenter)||r.isUndefined(t.barycenter)||e.barycenter>=t.barycenter)&&function(t,e){var n=0,r=0;t.weight&&(n+=t.barycenter*t.weight,r+=t.weight);e.weight&&(n+=e.barycenter*e.weight,r+=e.weight);t.vs=e.vs.concat(t.vs),t.barycenter=n/r,t.weight=r,t.i=Math.min(e.i,t.i),e.merged=!0}(t,e)}}function i(e){return function(n){n.in.push(e),0==--n.indegree&&t.push(n)}}for(;t.length;){var a=t.pop();e.push(a),r.forEach(a.in.reverse(),n(a)),r.forEach(a.out,i(a))}return r.map(r.filter(e,(function(t){return!t.merged})),(function(t){return r.pick(t,["vs","i","barycenter","weight"])}))}(r.filter(n,(function(t){return!t.indegree})))}},function(t,e,n){var r=n(4),i=n(8);function a(t,e,n){for(var i;e.length&&(i=r.last(e)).i<=n;)e.pop(),t.push(i.vs),n++;return n}t.exports=function(t,e){var n=i.partition(t,(function(t){return r.has(t,"barycenter")})),o=n.lhs,s=r.sortBy(n.rhs,(function(t){return-t.i})),c=[],u=0,l=0,h=0;o.sort((f=!!e,function(t,e){return t.barycenter<e.barycenter?-1:t.barycenter>e.barycenter?1:f?e.i-t.i:t.i-e.i})),h=a(c,s,h),r.forEach(o,(function(t){h+=t.vs.length,c.push(t.vs),u+=t.barycenter*t.weight,l+=t.weight,h=a(c,s,h)}));var f;var d={vs:r.flatten(c,!0)};l&&(d.barycenter=u/l,d.weight=l);return d}},function(t,e,n){var r=n(4),i=n(17).Graph;t.exports=function(t,e,n){var a=function(t){var e;for(;t.hasNode(e=r.uniqueId("_root")););return e}(t),o=new i({compound:!0}).setGraph({root:a}).setDefaultNodeLabel((function(e){return t.node(e)}));return r.forEach(t.nodes(),(function(i){var s=t.node(i),c=t.parent(i);(s.rank===e||s.minRank<=e&&e<=s.maxRank)&&(o.setNode(i),o.setParent(i,c||a),r.forEach(t[n](i),(function(e){var n=e.v===i?e.w:e.v,a=o.edge(n,i),s=r.isUndefined(a)?0:a.weight;o.setEdge(n,i,{weight:t.edge(e).weight+s})})),r.has(s,"minRank")&&o.setNode(i,{borderLeft:s.borderLeft[e],borderRight:s.borderRight[e]}))})),o}},function(t,e,n){var r=n(4);t.exports=function(t,e,n){var i,a={};r.forEach(n,(function(n){for(var r,o,s=t.parent(n);s;){if((r=t.parent(s))?(o=a[r],a[r]=s):(o=i,i=s),o&&o!==s)return void e.setEdge(o,s);s=r}}))}},function(t,e,n){"use strict";var r=n(4),i=n(8),a=n(365).positionX;t.exports=function(t){(function(t){var e=i.buildLayerMatrix(t),n=t.graph().ranksep,a=0;r.forEach(e,(function(e){var i=r.max(r.map(e,(function(e){return t.node(e).height})));r.forEach(e,(function(e){t.node(e).y=a+i/2})),a+=i+n}))})(t=i.asNonCompoundGraph(t)),r.forEach(a(t),(function(e,n){t.node(n).x=e}))}},function(t,e,n){"use strict";var r=n(4),i=n(17).Graph,a=n(8);function o(t,e){var n={};return r.reduce(e,(function(e,i){var a=0,o=0,s=e.length,u=r.last(i);return r.forEach(i,(function(e,l){var h=function(t,e){if(t.node(e).dummy)return r.find(t.predecessors(e),(function(e){return t.node(e).dummy}))}(t,e),f=h?t.node(h).order:s;(h||e===u)&&(r.forEach(i.slice(o,l+1),(function(e){r.forEach(t.predecessors(e),(function(r){var i=t.node(r),o=i.order;!(o<a||f<o)||i.dummy&&t.node(e).dummy||c(n,r,e)}))})),o=l+1,a=f)})),i})),n}function s(t,e){var n={};function i(e,i,a,o,s){var u;r.forEach(r.range(i,a),(function(i){u=e[i],t.node(u).dummy&&r.forEach(t.predecessors(u),(function(e){var r=t.node(e);r.dummy&&(r.order<o||r.order>s)&&c(n,e,u)}))}))}return r.reduce(e,(function(e,n){var a,o=-1,s=0;return r.forEach(n,(function(r,c){if("border"===t.node(r).dummy){var u=t.predecessors(r);u.length&&(a=t.node(u[0]).order,i(n,s,c,o,a),s=c,o=a)}i(n,s,n.length,a,e.length)})),n})),n}function c(t,e,n){if(e>n){var r=e;e=n,n=r}var i=t[e];i||(t[e]=i={}),i[n]=!0}function u(t,e,n){if(e>n){var i=e;e=n,n=i}return r.has(t[e],n)}function l(t,e,n,i){var a={},o={},s={};return r.forEach(e,(function(t){r.forEach(t,(function(t,e){a[t]=t,o[t]=t,s[t]=e}))})),r.forEach(e,(function(t){var e=-1;r.forEach(t,(function(t){var c=i(t);if(c.length)for(var l=((c=r.sortBy(c,(function(t){return s[t]}))).length-1)/2,h=Math.floor(l),f=Math.ceil(l);h<=f;++h){var d=c[h];o[t]===t&&e<s[d]&&!u(n,t,d)&&(o[d]=t,o[t]=a[t]=a[d],e=s[d])}}))})),{root:a,align:o}}function h(t,e,n,a,o){var s={},c=function(t,e,n,a){var o=new i,s=t.graph(),c=function(t,e,n){return function(i,a,o){var s,c=i.node(a),u=i.node(o),l=0;if(l+=c.width/2,r.has(c,"labelpos"))switch(c.labelpos.toLowerCase()){case"l":s=-c.width/2;break;case"r":s=c.width/2}if(s&&(l+=n?s:-s),s=0,l+=(c.dummy?e:t)/2,l+=(u.dummy?e:t)/2,l+=u.width/2,r.has(u,"labelpos"))switch(u.labelpos.toLowerCase()){case"l":s=u.width/2;break;case"r":s=-u.width/2}return s&&(l+=n?s:-s),s=0,l}}(s.nodesep,s.edgesep,a);return r.forEach(e,(function(e){var i;r.forEach(e,(function(e){var r=n[e];if(o.setNode(r),i){var a=n[i],s=o.edge(a,r);o.setEdge(a,r,Math.max(c(t,e,i),s||0))}i=e}))})),o}(t,e,n,o),u=o?"borderLeft":"borderRight";function l(t,e){for(var n=c.nodes(),r=n.pop(),i={};r;)i[r]?t(r):(i[r]=!0,n.push(r),n=n.concat(e(r))),r=n.pop()}return l((function(t){s[t]=c.inEdges(t).reduce((function(t,e){return Math.max(t,s[e.v]+c.edge(e))}),0)}),c.predecessors.bind(c)),l((function(e){var n=c.outEdges(e).reduce((function(t,e){return Math.min(t,s[e.w]-c.edge(e))}),Number.POSITIVE_INFINITY),r=t.node(e);n!==Number.POSITIVE_INFINITY&&r.borderType!==u&&(s[e]=Math.max(s[e],n))}),c.successors.bind(c)),r.forEach(a,(function(t){s[t]=s[n[t]]})),s}function f(t,e){return r.minBy(r.values(e),(function(e){var n=Number.NEGATIVE_INFINITY,i=Number.POSITIVE_INFINITY;return r.forIn(e,(function(e,r){var a=function(t,e){return t.node(e).width}(t,r)/2;n=Math.max(e+a,n),i=Math.min(e-a,i)})),n-i}))}function d(t,e){var n=r.values(e),i=r.min(n),a=r.max(n);r.forEach(["u","d"],(function(n){r.forEach(["l","r"],(function(o){var s,c=n+o,u=t[c];if(u!==e){var l=r.values(u);(s="l"===o?i-r.min(l):a-r.max(l))&&(t[c]=r.mapValues(u,(function(t){return t+s})))}}))}))}function p(t,e){return r.mapValues(t.ul,(function(n,i){if(e)return t[e.toLowerCase()][i];var a=r.sortBy(r.map(t,i));return(a[1]+a[2])/2}))}t.exports={positionX:function(t){var e,n=a.buildLayerMatrix(t),i=r.merge(o(t,n),s(t,n)),c={};r.forEach(["u","d"],(function(a){e="u"===a?n:r.values(n).reverse(),r.forEach(["l","r"],(function(n){"r"===n&&(e=r.map(e,(function(t){return r.values(t).reverse()})));var o=("u"===a?t.predecessors:t.successors).bind(t),s=l(t,e,i,o),u=h(t,e,s.root,s.align,"r"===n);"r"===n&&(u=r.mapValues(u,(function(t){return-t}))),c[a+n]=u}))}));var u=f(t,c);return d(c,u),p(c,t.graph().align)},findType1Conflicts:o,findType2Conflicts:s,addConflict:c,hasConflict:u,verticalAlignment:l,horizontalCompaction:h,alignCoordinates:d,findSmallestWidthAlignment:f,balance:p}},function(t,e,n){var r=n(4),i=n(8),a=n(17).Graph;t.exports={debugOrdering:function(t){var e=i.buildLayerMatrix(t),n=new a({compound:!0,multigraph:!0}).setGraph({});return r.forEach(t.nodes(),(function(e){n.setNode(e,{label:e}),n.setParent(e,"layer"+t.node(e).rank)})),r.forEach(t.edges(),(function(t){n.setEdge(t.v,t.w,{},t.name)})),r.forEach(e,(function(t,e){var i="layer"+e;n.setNode(i,{rank:"same"}),r.reduce(t,(function(t,e){return n.setEdge(t,e,{style:"invis"}),e}))})),n}}},function(t,e){t.exports="0.8.5"},function(t,e,n){t.exports={node:n(165),circle:n(166),ellipse:n(96),polygon:n(167),rect:n(168)}},function(t,e){function n(t,e){return t*e>0}t.exports=function(t,e,r,i){var a,o,s,c,u,l,h,f,d,p,g,y,v;if(a=e.y-t.y,s=t.x-e.x,u=e.x*t.y-t.x*e.y,d=a*r.x+s*r.y+u,p=a*i.x+s*i.y+u,0!==d&&0!==p&&n(d,p))return;if(o=i.y-r.y,c=r.x-i.x,l=i.x*r.y-r.x*i.y,h=o*t.x+c*t.y+l,f=o*e.x+c*e.y+l,0!==h&&0!==f&&n(h,f))return;if(0===(g=a*c-o*s))return;return y=Math.abs(g/2),{x:(v=s*l-c*u)<0?(v-y)/g:(v+y)/g,y:(v=o*u-a*l)<0?(v-y)/g:(v+y)/g}}},function(t,e,n){var r=n(43),i=n(31),a=n(153).layout;t.exports=function(){var t=n(371),e=n(374),i=n(375),u=n(376),l=n(377),h=n(378),f=n(379),d=n(380),p=n(381),g=function(n,g){!function(t){t.nodes().forEach((function(e){var n=t.node(e);r.has(n,"label")||t.children(e).length||(n.label=e),r.has(n,"paddingX")&&r.defaults(n,{paddingLeft:n.paddingX,paddingRight:n.paddingX}),r.has(n,"paddingY")&&r.defaults(n,{paddingTop:n.paddingY,paddingBottom:n.paddingY}),r.has(n,"padding")&&r.defaults(n,{paddingLeft:n.padding,paddingRight:n.padding,paddingTop:n.padding,paddingBottom:n.padding}),r.defaults(n,o),r.each(["paddingLeft","paddingRight","paddingTop","paddingBottom"],(function(t){n[t]=Number(n[t])})),r.has(n,"width")&&(n._prevWidth=n.width),r.has(n,"height")&&(n._prevHeight=n.height)})),t.edges().forEach((function(e){var n=t.edge(e);r.has(n,"label")||(n.label=""),r.defaults(n,s)}))}(g);var y=c(n,"output"),v=c(y,"clusters"),m=c(y,"edgePaths"),b=i(c(y,"edgeLabels"),g),x=t(c(y,"nodes"),g,d);a(g),l(x,g),h(b,g),u(m,g,p);var _=e(v,g);f(_,g),function(t){r.each(t.nodes(),(function(e){var n=t.node(e);r.has(n,"_prevWidth")?n.width=n._prevWidth:delete n.width,r.has(n,"_prevHeight")?n.height=n._prevHeight:delete n.height,delete n._prevWidth,delete n._prevHeight}))}(g)};return g.createNodes=function(e){return arguments.length?(t=e,g):t},g.createClusters=function(t){return arguments.length?(e=t,g):e},g.createEdgeLabels=function(t){return arguments.length?(i=t,g):i},g.createEdgePaths=function(t){return arguments.length?(u=t,g):u},g.shapes=function(t){return arguments.length?(d=t,g):d},g.arrows=function(t){return arguments.length?(p=t,g):p},g};var o={paddingLeft:10,paddingRight:10,paddingTop:10,paddingBottom:10,rx:0,ry:0,shape:"rect"},s={arrowhead:"normal",curve:i.curveLinear};function c(t,e){var n=t.select("g."+e);return n.empty()&&(n=t.append("g").attr("class",e)),n}},function(t,e,n){"use strict";var r=n(43),i=n(97),a=n(12),o=n(31);t.exports=function(t,e,n){var s,c=e.nodes().filter((function(t){return!a.isSubgraph(e,t)})),u=t.selectAll("g.node").data(c,(function(t){return t})).classed("update",!0);u.exit().remove(),u.enter().append("g").attr("class","node").style("opacity",0),(u=t.selectAll("g.node")).each((function(t){var s=e.node(t),c=o.select(this);a.applyClass(c,s.class,(c.classed("update")?"update ":"")+"node"),c.select("g.label").remove();var u=c.append("g").attr("class","label"),l=i(u,s),h=n[s.shape],f=r.pick(l.node().getBBox(),"width","height");s.elem=this,s.id&&c.attr("id",s.id),s.labelId&&u.attr("id",s.labelId),r.has(s,"width")&&(f.width=s.width),r.has(s,"height")&&(f.height=s.height),f.width+=s.paddingLeft+s.paddingRight,f.height+=s.paddingTop+s.paddingBottom,u.attr("transform","translate("+(s.paddingLeft-s.paddingRight)/2+","+(s.paddingTop-s.paddingBottom)/2+")");var d=o.select(this);d.select(".label-container").remove();var p=h(d,f,s).classed("label-container",!0);a.applyStyle(p,s.style);var g=p.node().getBBox();s.width=g.width,s.height=g.height})),s=u.exit?u.exit():u.selectAll(null);return a.applyTransition(s,e).style("opacity",0).remove(),u}},function(t,e,n){var r=n(12);t.exports=function(t,e){for(var n=t.append("text"),i=function(t){for(var e,n="",r=!1,i=0;i<t.length;++i)if(e=t[i],r){switch(e){case"n":n+="\n";break;default:n+=e}r=!1}else"\\"===e?r=!0:n+=e;return n}(e.label).split("\n"),a=0;a<i.length;a++)n.append("tspan").attr("xml:space","preserve").attr("dy","1em").attr("x","1").text(i[a]);return r.applyStyle(n,e.labelStyle),n}},function(t,e,n){var r=n(12);t.exports=function(t,e){var n=t;return n.node().appendChild(e.label),r.applyStyle(n,e.labelStyle),n}},function(t,e,n){var r=n(12),i=n(31),a=n(97);t.exports=function(t,e){var n,o=e.nodes().filter((function(t){return r.isSubgraph(e,t)})),s=t.selectAll("g.cluster").data(o,(function(t){return t}));s.selectAll("*").remove(),s.enter().append("g").attr("class","cluster").attr("id",(function(t){return e.node(t).id})).style("opacity",0),s=t.selectAll("g.cluster"),r.applyTransition(s,e).style("opacity",1),s.each((function(t){var n=e.node(t),r=i.select(this);i.select(this).append("rect");var o=r.append("g").attr("class","label");a(o,n,n.clusterLabelPos)})),s.selectAll("rect").each((function(t){var n=e.node(t),a=i.select(this);r.applyStyle(a,n.style)})),n=s.exit?s.exit():s.selectAll(null);return r.applyTransition(n,e).style("opacity",0).remove(),s}},function(t,e,n){"use strict";var r=n(43),i=n(97),a=n(12),o=n(31);t.exports=function(t,e){var n,s=t.selectAll("g.edgeLabel").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0);s.exit().remove(),s.enter().append("g").classed("edgeLabel",!0).style("opacity",0),(s=t.selectAll("g.edgeLabel")).each((function(t){var n=o.select(this);n.select(".label").remove();var a=e.edge(t),s=i(n,e.edge(t),0,0).classed("label",!0),c=s.node().getBBox();a.labelId&&s.attr("id",a.labelId),r.has(a,"width")||(a.width=c.width),r.has(a,"height")||(a.height=c.height)})),n=s.exit?s.exit():s.selectAll(null);return a.applyTransition(n,e).style("opacity",0).remove(),s}},function(t,e,n){"use strict";var r=n(43),i=n(165),a=n(12),o=n(31);function s(t,e){var n=(o.line||o.svg.line)().x((function(t){return t.x})).y((function(t){return t.y}));return(n.curve||n.interpolate)(t.curve),n(e)}t.exports=function(t,e,n){var c=t.selectAll("g.edgePath").data(e.edges(),(function(t){return a.edgeToId(t)})).classed("update",!0),u=function(t,e){var n=t.enter().append("g").attr("class","edgePath").style("opacity",0);return n.append("path").attr("class","path").attr("d",(function(t){var n=e.edge(t),i=e.node(t.v).elem;return s(n,r.range(n.points.length).map((function(){return e=(t=i).getBBox(),{x:(n=t.ownerSVGElement.getScreenCTM().inverse().multiply(t.getScreenCTM()).translate(e.width/2,e.height/2)).e,y:n.f};var t,e,n})))})),n.append("defs"),n}(c,e);!function(t,e){var n=t.exit();a.applyTransition(n,e).style("opacity",0).remove()}(c,e);var l=void 0!==c.merge?c.merge(u):c;return a.applyTransition(l,e).style("opacity",1),l.each((function(t){var n=o.select(this),r=e.edge(t);r.elem=this,r.id&&n.attr("id",r.id),a.applyClass(n,r.class,(n.classed("update")?"update ":"")+"edgePath")})),l.selectAll("path.path").each((function(t){var n=e.edge(t);n.arrowheadId=r.uniqueId("arrowhead");var c=o.select(this).attr("marker-end",(function(){return"url("+(t=location.href,e=n.arrowheadId,t.split("#")[0]+"#"+e)+")";var t,e})).style("fill","none");a.applyTransition(c,e).attr("d",(function(t){return function(t,e){var n=t.edge(e),r=t.node(e.v),a=t.node(e.w),o=n.points.slice(1,n.points.length-1);return o.unshift(i(r,o[0])),o.push(i(a,o[o.length-1])),s(n,o)}(e,t)})),a.applyStyle(c,n.style)})),l.selectAll("defs *").remove(),l.selectAll("defs").each((function(t){var r=e.edge(t);(0,n[r.arrowhead])(o.select(this),r.arrowheadId,r,"arrowhead")})),l}},function(t,e,n){"use strict";var r=n(12),i=n(31);t.exports=function(t,e){function n(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},function(t,e,n){"use strict";var r=n(12),i=n(31),a=n(43);t.exports=function(t,e){function n(t){var n=e.edge(t);return a.has(n,"x")?"translate("+n.x+","+n.y+")":""}t.filter((function(){return!i.select(this).classed("update")})).attr("transform",n),r.applyTransition(t,e).style("opacity",1).attr("transform",n)}},function(t,e,n){"use strict";var r=n(12),i=n(31);t.exports=function(t,e){var n=t.filter((function(){return!i.select(this).classed("update")}));function a(t){var n=e.node(t);return"translate("+n.x+","+n.y+")"}n.attr("transform",a),r.applyTransition(t,e).style("opacity",1).attr("transform",a),r.applyTransition(n.selectAll("rect"),e).attr("width",(function(t){return e.node(t).width})).attr("height",(function(t){return e.node(t).height})).attr("x",(function(t){return-e.node(t).width/2})).attr("y",(function(t){return-e.node(t).height/2}))}},function(t,e,n){"use strict";var r=n(168),i=n(96),a=n(166),o=n(167);t.exports={rect:function(t,e,n){var i=t.insert("rect",":first-child").attr("rx",n.rx).attr("ry",n.ry).attr("x",-e.width/2).attr("y",-e.height/2).attr("width",e.width).attr("height",e.height);return n.intersect=function(t){return r(n,t)},i},ellipse:function(t,e,n){var r=e.width/2,a=e.height/2,o=t.insert("ellipse",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("rx",r).attr("ry",a);return n.intersect=function(t){return i(n,r,a,t)},o},circle:function(t,e,n){var r=Math.max(e.width,e.height)/2,i=t.insert("circle",":first-child").attr("x",-e.width/2).attr("y",-e.height/2).attr("r",r);return n.intersect=function(t){return a(n,r,t)},i},diamond:function(t,e,n){var r=e.width*Math.SQRT2/2,i=e.height*Math.SQRT2/2,a=[{x:0,y:-i},{x:-r,y:0},{x:0,y:i},{x:r,y:0}],s=t.insert("polygon",":first-child").attr("points",a.map((function(t){return t.x+","+t.y})).join(" "));return n.intersect=function(t){return o(n,a,t)},s}}},function(t,e,n){var r=n(12);function i(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}t.exports={default:i,normal:i,vee:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 L 4 5 z").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])},undirected:function(t,e,n,i){var a=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 5 L 10 5").style("stroke-width",1).style("stroke-dasharray","1,0");r.applyStyle(a,n[i+"Style"]),n[i+"Class"]&&a.attr("class",n[i+"Class"])}}},function(t,e){t.exports="0.6.4"},function(t,e,n){"use strict";var r;function i(t){return r=r||document.createElement("div"),t=escape(t).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),r.innerHTML=t,unescape(r.textContent)}n.r(e);var a=n(23),o=n.n(a),s={debug:1,info:2,warn:3,error:4,fatal:5},c={debug:function(){},info:function(){},warn:function(){},error:function(){},fatal:function(){}},u=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"fatal";isNaN(t)&&(t=t.toLowerCase(),void 0!==s[t]&&(t=s[t])),c.trace=function(){},c.debug=function(){},c.info=function(){},c.warn=function(){},c.error=function(){},c.fatal=function(){},t<=s.fatal&&(c.fatal=console.error?console.error.bind(console,l("FATAL"),"color: orange"):console.log.bind(console,"",l("FATAL"))),t<=s.error&&(c.error=console.error?console.error.bind(console,l("ERROR"),"color: orange"):console.log.bind(console,"",l("ERROR"))),t<=s.warn&&(c.warn=console.warn?console.warn.bind(console,l("WARN"),"color: orange"):console.log.bind(console,"",l("WARN"))),t<=s.info&&(c.info=console.info?console.info.bind(console,l("INFO"),"color: lightblue"):console.log.bind(console,"",l("INFO"))),t<=s.debug&&(c.debug=console.debug?console.debug.bind(console,l("DEBUG"),"color: lightgreen"):console.log.bind(console,"",l("DEBUG")))},l=function(t){var e=o()().format("ss.SSS");return"%c".concat(e," : ").concat(t," : ")},h=n(169),f=n.n(h),d=n(0),p=n(44),g=n(70),y=function(t){for(var e="",n=0;n>=0;){if(!((n=t.indexOf("<script"))>=0)){e+=t,n=-1;break}e+=t.substr(0,n),(n=(t=t.substr(n+1)).indexOf("<\/script>"))>=0&&(n+=9,t=t.substr(n))}return e},v=/<br\s*\/?>/gi,m=function(t){return t.replace(v,"#br#")},b=function(t){return t.replace(/#br#/g,"<br/>")},x={getRows:function(t){if(!t)return 1;var e=m(t);return(e=e.replace(/\\n/g,"#br#")).split("#br#")},sanitizeText:function(t,e){var n=t,r=!0;if(!e.flowchart||!1!==e.flowchart.htmlLabels&&"false"!==e.flowchart.htmlLabels||(r=!1),r){var i=e.securityLevel;"antiscript"===i?n=y(n):"loose"!==i&&(n=(n=(n=m(n)).replace(/</g,"&lt;").replace(/>/g,"&gt;")).replace(/=/g,"&equals;"),n=b(n))}return n},hasBreaks:function(t){return/<br\s*[/]?>/gi.test(t)},splitBreaks:function(t){return t.split(/<br\s*[/]?>/gi)},lineBreakRegex:v,removeScript:y};function _(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}function k(t){return(k="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function w(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var E={curveBasis:d.curveBasis,curveBasisClosed:d.curveBasisClosed,curveBasisOpen:d.curveBasisOpen,curveLinear:d.curveLinear,curveLinearClosed:d.curveLinearClosed,curveMonotoneX:d.curveMonotoneX,curveMonotoneY:d.curveMonotoneY,curveNatural:d.curveNatural,curveStep:d.curveStep,curveStepAfter:d.curveStepAfter,curveStepBefore:d.curveStepBefore},T=/[%]{2}[{]\s*(?:(?:(\w+)\s*:|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,C=/\s*(?:(?:(\w+)(?=:):|(\w+))\s*(?:(?:(\w+))|((?:(?![}][%]{2}).|\r?\n)*))?\s*)(?:[}][%]{2})?/gi,A=/\s*%%.*\n/gm,S=function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;try{var n=new RegExp("[%]{2}(?![{]".concat(C.source,")(?=[}][%]{2}).*\n"),"ig");t=t.trim().replace(n,"").replace(/'/gm,'"'),c.debug("Detecting diagram directive".concat(null!==e?" type:"+e:""," based on the text:").concat(t));for(var r,i=[];null!==(r=T.exec(t));)if(r.index===T.lastIndex&&T.lastIndex++,r&&!e||e&&r[1]&&r[1].match(e)||e&&r[2]&&r[2].match(e)){var a=r[1]?r[1]:r[2],o=r[3]?r[3].trim():r[4]?JSON.parse(r[4].trim()):null;i.push({type:a,args:o})}return 0===i.length&&i.push({type:t,args:null}),1===i.length?i[0]:i}catch(n){return c.error("ERROR: ".concat(n.message," - Unable to parse directive").concat(null!==e?" type:"+e:""," based on the text:").concat(t)),{type:null,args:null}}},M=function(t){return t=t.replace(T,"").replace(A,"\n"),c.debug("Detecting diagram type based on the text "+t),t.match(/^\s*sequenceDiagram/)?"sequence":t.match(/^\s*gantt/)?"gantt":t.match(/^\s*classDiagram-v2/)?"classDiagram":t.match(/^\s*classDiagram/)?"class":t.match(/^\s*stateDiagram-v2/)?"stateDiagram":t.match(/^\s*stateDiagram/)?"state":t.match(/^\s*gitGraph/)?"git":t.match(/^\s*flowchart/)?"flowchart-v2":t.match(/^\s*info/)?"info":t.match(/^\s*pie/)?"pie":t.match(/^\s*erDiagram/)?"er":t.match(/^\s*journey/)?"journey":"flowchart"},O=function(t,e){var n={};return function(){for(var r=arguments.length,i=new Array(r),a=0;a<r;a++)i[a]=arguments[a];var o=e?e.apply(void 0,i):i[0];if(o in n)return n[o];var s=t.apply(void 0,i);return n[o]=s,s}},D=function(t,e){if(!t)return e;var n="curve".concat(t.charAt(0).toUpperCase()+t.slice(1));return E[n]||e},N=function(t,e){return t&&e?Math.sqrt(Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2)):0},B=function(t){for(var e="",n="",r=0;r<t.length;r++)void 0!==t[r]&&(t[r].startsWith("color:")||t[r].startsWith("text-align:")?n=n+t[r]+";":e=e+t[r]+";");return{style:e,labelStyle:n}},L=0,P=function(){return L++,"id-"+Math.random().toString(36).substr(2,12)+"-"+L};var I=function(t){return function(t){for(var e="",n="0123456789abcdef".length,r=0;r<t;r++)e+="0123456789abcdef".charAt(Math.floor(Math.random()*n));return e}(t.length)},F=function t(e,n,r){var i=Object.assign({depth:2,clobber:!1},r),a=i.depth,o=i.clobber;return Array.isArray(n)&&!Array.isArray(e)?(n.forEach((function(n){return t(e,n,r)})),e):Array.isArray(n)&&Array.isArray(e)?(n.forEach((function(t){-1===e.indexOf(t)&&e.push(t)})),e):void 0===e||a<=0?null!=e&&"object"===k(e)&&"object"===k(n)?Object.assign(e,n):n:(void 0!==n&&"object"===k(e)&&"object"===k(n)&&Object.keys(n).forEach((function(r){"object"!==k(n[r])||void 0!==e[r]&&"object"!==k(e[r])?(o||"object"!==k(e[r])&&"object"!==k(n[r]))&&(e[r]=n[r]):(void 0===e[r]&&(e[r]=Array.isArray(n[r])?[]:{}),e[r]=t(e[r],n[r],{depth:a-1,clobber:o}))})),e)},j=function(t,e){var n=e.text.replace(x.lineBreakRegex," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.style("text-anchor",e.anchor),r.style("font-family",e.fontFamily),r.style("font-size",e.fontSize),r.style("font-weight",e.fontWeight),r.attr("fill",e.fill),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.attr("fill",e.fill),i.text(n),r},R=O((function(t,e,n){if(!t)return t;if(n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",joinWith:"<br/>"},n),x.lineBreakRegex.test(t))return t;var r=t.split(" "),i=[],a="";return r.forEach((function(t,o){var s=z("".concat(t," "),n),c=z(a,n);if(s>e){var u=Y(t,e,"-",n),l=u.hyphenatedStrings,h=u.remainingWord;i.push.apply(i,[a].concat(w(l))),a=h}else c+s>=e?(i.push(a),a=t):a=[a,t].filter(Boolean).join(" ");o+1===r.length&&i.push(a)})),i.filter((function(t){return""!==t})).join(n.joinWith)}),(function(t,e,n){return"".concat(t,"-").concat(e,"-").concat(n.fontSize,"-").concat(n.fontWeight,"-").concat(n.fontFamily,"-").concat(n.joinWith)})),Y=O((function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;r=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},r);var i=t.split(""),a=[],o="";return i.forEach((function(t,s){var c="".concat(o).concat(t);if(z(c,r)>=e){var u=s+1,l=i.length===u,h="".concat(c).concat(n);a.push(l?c:h),o=""}else o=c})),{hyphenatedStrings:a,remainingWord:o}}),(function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"-",r=arguments.length>3?arguments[3]:void 0;return"".concat(t,"-").concat(e,"-").concat(n,"-").concat(r.fontSize,"-").concat(r.fontWeight,"-").concat(r.fontFamily)})),z=function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),U(t,e).width},U=O((function(t,e){var n=e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial"},e),r=n.fontSize,i=n.fontFamily,a=n.fontWeight;if(!t)return{width:0,height:0};var o=["sans-serif",i],s=t.split(x.lineBreakRegex),c=[],u=Object(d.select)("body");if(!u.remove)return{width:0,height:0,lineHeight:0};for(var l=u.append("svg"),h=0,f=o;h<f.length;h++){var p=f[h],g=0,y={width:0,height:0,lineHeight:0},v=!0,m=!1,b=void 0;try{for(var _,k=s[Symbol.iterator]();!(v=(_=k.next()).done);v=!0){var w=_.value,E={x:0,y:0,fill:void 0,anchor:"start",style:"#666",width:100,height:100,textMargin:0,rx:0,ry:0,valign:void 0};E.text=w;var T=j(l,E).style("font-size",r).style("font-weight",a).style("font-family",p),C=(T._groups||T)[0][0].getBBox();y.width=Math.round(Math.max(y.width,C.width)),g=Math.round(C.height),y.height+=g,y.lineHeight=Math.round(Math.max(y.lineHeight,g))}}catch(t){m=!0,b=t}finally{try{v||null==k.return||k.return()}finally{if(m)throw b}}c.push(y)}return l.remove(),c[isNaN(c[1].height)||isNaN(c[1].width)||isNaN(c[1].lineHeight)||c[0].height>c[1].height&&c[0].width>c[1].width&&c[0].lineHeight>c[1].lineHeight?0:1]}),(function(t,e){return"".concat(t,"-").concat(e.fontSize,"-").concat(e.fontWeight,"-").concat(e.fontFamily)})),$=function(t,e,n){var r=new Map;return r.set("height",t),n?(r.set("width","100%"),r.set("style","max-width: ".concat(e,"px;"))):r.set("width",e),r},W=function(t,e,n,r){!function(t,e){var n=!0,r=!1,i=void 0;try{for(var a,o=e[Symbol.iterator]();!(n=(a=o.next()).done);n=!0){var s=a.value;t.attr(s[0],s[1])}}catch(t){r=!0,i=t}finally{try{n||null==o.return||o.return()}finally{if(r)throw i}}}(t,$(e,n,r))},H={assignWithDepth:F,wrapLabel:R,calculateTextHeight:function(t,e){return e=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:15},e),U(t,e).height},calculateTextWidth:z,calculateTextDimensions:U,calculateSvgSizeAttrs:$,configureSvgSize:W,detectInit:function(t){var e=S(t,/(?:init\b)|(?:initialize\b)/),n={};if(Array.isArray(e)){var r=e.map((function(t){return t.args}));n=F(n,w(r))}else n=e.args;if(n){var i=M(t);["config"].forEach((function(t){void 0!==n[t]&&("flowchart-v2"===i&&(i="flowchart"),n[i]=n[t],delete n[t])}))}return n},detectDirective:S,detectType:M,isSubstringInArray:function(t,e){for(var n=0;n<e.length;n++)if(e[n].match(t))return n;return-1},interpolateToCurve:D,calcLabelPosition:function(t){return function(t){var e,n=0;t.forEach((function(t){n+=N(t,e),e=t}));var r=n/2,i=void 0;return e=void 0,t.forEach((function(t){if(e&&!i){var n=N(t,e);if(n<r)r-=n;else{var a=r/n;a<=0&&(i=e),a>=1&&(i={x:t.x,y:t.y}),a>0&&a<1&&(i={x:(1-a)*e.x+a*t.x,y:(1-a)*e.y+a*t.y})}}e=t})),i}(t)},calcCardinalityPosition:function(t,e,n){var r;c.info("our points",e),e[0]!==n&&(e=e.reverse()),e.forEach((function(t){N(t,r),r=t}));var i,a=25;r=void 0,e.forEach((function(t){if(r&&!i){var e=N(t,r);if(e<a)a-=e;else{var n=a/e;n<=0&&(i=r),n>=1&&(i={x:t.x,y:t.y}),n>0&&n<1&&(i={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var o=t?10:5,s=Math.atan2(e[0].y-i.y,e[0].x-i.x),u={x:0,y:0};return u.x=Math.sin(s)*o+(e[0].x+i.x)/2,u.y=-Math.cos(s)*o+(e[0].y+i.y)/2,u},calcTerminalLabelPosition:function(t,e,n){var r,i=JSON.parse(JSON.stringify(n));c.info("our points",i),"start_left"!==e&&"start_right"!==e&&(i=i.reverse()),i.forEach((function(t){N(t,r),r=t}));var a,o=25;r=void 0,i.forEach((function(t){if(r&&!a){var e=N(t,r);if(e<o)o-=e;else{var n=o/e;n<=0&&(a=r),n>=1&&(a={x:t.x,y:t.y}),n>0&&n<1&&(a={x:(1-n)*r.x+n*t.x,y:(1-n)*r.y+n*t.y})}}r=t}));var s=10,u=Math.atan2(i[0].y-a.y,i[0].x-a.x),l={x:0,y:0};return l.x=Math.sin(u)*s+(i[0].x+a.x)/2,l.y=-Math.cos(u)*s+(i[0].y+a.y)/2,"start_left"===e&&(l.x=Math.sin(u+Math.PI)*s+(i[0].x+a.x)/2,l.y=-Math.cos(u+Math.PI)*s+(i[0].y+a.y)/2),"end_right"===e&&(l.x=Math.sin(u-Math.PI)*s+(i[0].x+a.x)/2-5,l.y=-Math.cos(u-Math.PI)*s+(i[0].y+a.y)/2-5),"end_left"===e&&(l.x=Math.sin(u)*s+(i[0].x+a.x)/2-5,l.y=-Math.cos(u)*s+(i[0].y+a.y)/2-5),l},formatUrl:function(t,e){var n=t.trim();if(n)return"loose"!==e.securityLevel?Object(g.sanitizeUrl)(n):n},getStylesFromArray:B,generateId:P,random:I,memoize:O,runFunc:function(t){for(var e,n=t.split("."),r=n.length-1,i=n[r],a=window,o=0;o<r;o++)if(!(a=a[n[o]]))return;for(var s=arguments.length,c=new Array(s>1?s-1:0),u=1;u<s;u++)c[u-1]=arguments[u];(e=a)[i].apply(e,c)},initIdGeneratior:function(t,e){return t?new(function(){function t(){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.count=e?e.length:0}var n,r,i;return n=t,(r=[{key:"next",value:function(){return this.count++}}])&&_(n.prototype,r),i&&_(n,i),t}()):{next:function(){return Date.now()}}}},V=n(3),G=n.n(V),q=n(1),X=function(t,e){return e?Object(q.adjust)(t,{s:-40,l:10}):Object(q.adjust)(t,{s:-40,l:-10})};function Z(t){return(Z="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function J(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var K=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.darkMode=!1,this.primaryColor="#fff4dd",this.noteBkgColor="#fff5ad",this.noteTextColor="#333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.primaryTextColor=this.primaryTextColor||(this.darkMode?"#ddd":"#333"),this.secondaryColor=this.secondaryColor||Object(q.adjust)(this.primaryColor,{h:-120}),this.tertiaryColor=this.tertiaryColor||Object(q.adjust)(this.primaryColor,{h:180,l:5}),this.primaryBorderColor=this.primaryBorderColor||X(this.primaryColor,this.darkMode),this.secondaryBorderColor=this.secondaryBorderColor||X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=this.tertiaryBorderColor||X(this.tertiaryColor,this.darkMode),this.noteBorderColor=this.noteBorderColor||X(this.noteBkgColor,this.darkMode),this.secondaryTextColor=this.secondaryTextColor||Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=this.tertiaryTextColor||Object(q.invert)(this.tertiaryColor),this.lineColor=this.lineColor||Object(q.invert)(this.background),this.textColor=this.textColor||this.primaryTextColor,this.nodeBkg=this.nodeBkg||this.primaryColor,this.mainBkg=this.mainBkg||this.primaryColor,this.nodeBorder=this.nodeBorder||this.primaryBorderColor,this.clusterBkg=this.clusterBkg||this.tertiaryColor,this.clusterBorder=this.clusterBorder||this.tertiaryBorderColor,this.defaultLinkColor=this.defaultLinkColor||this.lineColor,this.titleColor=this.titleColor||this.tertiaryTextColor,this.edgeLabelBackground=this.edgeLabelBackground||(this.darkMode?Object(q.darken)(this.secondaryColor,30):this.secondaryColor),this.nodeTextColor=this.nodeTextColor||this.primaryTextColor,this.actorBorder=this.actorBorder||this.primaryBorderColor,this.actorBkg=this.actorBkg||this.mainBkg,this.actorTextColor=this.actorTextColor||this.primaryTextColor,this.actorLineColor=this.actorLineColor||"grey",this.labelBoxBkgColor=this.labelBoxBkgColor||this.actorBkg,this.signalColor=this.signalColor||this.textColor,this.signalTextColor=this.signalTextColor||this.textColor,this.labelBoxBorderColor=this.labelBoxBorderColor||this.actorBorder,this.labelTextColor=this.labelTextColor||this.actorTextColor,this.loopTextColor=this.loopTextColor||this.actorTextColor,this.activationBorderColor=this.activationBorderColor||Object(q.darken)(this.secondaryColor,10),this.activationBkgColor=this.activationBkgColor||this.secondaryColor,this.sequenceNumberColor=this.sequenceNumberColor||Object(q.invert)(this.lineColor),this.sectionBkgColor=this.sectionBkgColor||this.tertiaryColor,this.altSectionBkgColor=this.altSectionBkgColor||"white",this.sectionBkgColor=this.sectionBkgColor||this.secondaryColor,this.sectionBkgColor2=this.sectionBkgColor2||this.primaryColor,this.taskBorderColor=this.taskBorderColor||this.primaryBorderColor,this.taskBkgColor=this.taskBkgColor||this.primaryColor,this.activeTaskBorderColor=this.activeTaskBorderColor||this.primaryColor,this.activeTaskBkgColor=this.activeTaskBkgColor||Object(q.lighten)(this.primaryColor,23),this.gridColor=this.gridColor||"lightgrey",this.doneTaskBkgColor=this.doneTaskBkgColor||"lightgrey",this.doneTaskBorderColor=this.doneTaskBorderColor||"grey",this.critBorderColor=this.critBorderColor||"#ff8888",this.critBkgColor=this.critBkgColor||"red",this.todayLineColor=this.todayLineColor||"red",this.taskTextColor=this.taskTextColor||this.textColor,this.taskTextOutsideColor=this.taskTextOutsideColor||this.textColor,this.taskTextLightColor=this.taskTextLightColor||this.textColor,this.taskTextColor=this.taskTextColor||this.primaryTextColor,this.taskTextDarkColor=this.taskTextDarkColor||this.textColor,this.taskTextClickableColor=this.taskTextClickableColor||"#003163",this.labelColor=this.labelColor||this.primaryTextColor,this.altBackground=this.altBackground||this.tertiaryColor,this.errorBkgColor=this.errorBkgColor||this.tertiaryColor,this.errorTextColor=this.errorTextColor||this.tertiaryTextColor,this.classText=this.classText||this.textColor,this.fillType0=this.fillType0||this.primaryColor,this.fillType1=this.fillType1||this.secondaryColor,this.fillType2=this.fillType2||Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=this.fillType3||Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=this.fillType4||Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=this.fillType5||Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=this.fillType6||Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=this.fillType7||Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===Z(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&J(e.prototype,n),r&&J(e,r),t}();function Q(t){return(Q="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function tt(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var et=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#333",this.primaryColor="#1f2020",this.secondaryColor=Object(q.lighten)(this.primaryColor,16),this.tertiaryColor=Object(q.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.tertiaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.mainBkg="#1f2020",this.secondBkg="calculated",this.mainContrastColor="lightgrey",this.darkTextColor=Object(q.lighten)(Object(q.invert)("#323D47"),10),this.lineColor="calculated",this.border1="#81B1DB",this.border2=Object(q.rgba)(255,255,255,.25),this.arrowheadColor="calculated",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#181818",this.textColor="#ccc",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#F9FFFE",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="calculated",this.activationBkgColor="calculated",this.sequenceNumberColor="black",this.sectionBkgColor=Object(q.darken)("#EAE8D9",30),this.altSectionBkgColor="calculated",this.sectionBkgColor2="#EAE8D9",this.taskBorderColor=Object(q.rgba)(255,255,255,70),this.taskBkgColor="calculated",this.taskTextColor="calculated",this.taskTextLightColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor=Object(q.rgba)(255,255,255,50),this.activeTaskBkgColor="#81B1DB",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="grey",this.critBorderColor="#E83737",this.critBkgColor="#E83737",this.taskTextDarkColor="calculated",this.todayLineColor="#DB5757",this.labelColor="calculated",this.errorBkgColor="#a44141",this.errorTextColor="#ddd"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.secondBkg=Object(q.lighten)(this.mainBkg,16),this.lineColor=this.mainContrastColor,this.arrowheadColor=this.mainContrastColor,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.edgeLabelBackground=Object(q.lighten)(this.labelBackground,25),this.actorBorder=this.border1,this.actorBkg=this.mainBkg,this.actorTextColor=this.mainContrastColor,this.actorLineColor=this.mainContrastColor,this.signalColor=this.mainContrastColor,this.signalTextColor=this.mainContrastColor,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.mainContrastColor,this.loopTextColor=this.mainContrastColor,this.noteBorderColor=this.border2,this.noteTextColor=this.mainBkg,this.activationBorderColor=this.border1,this.activationBkgColor=this.secondBkg,this.altSectionBkgColor=this.background,this.taskBkgColor=Object(q.lighten)(this.mainBkg,23),this.taskTextColor=this.darkTextColor,this.taskTextLightColor=this.mainContrastColor,this.taskTextOutsideColor=this.taskTextLightColor,this.gridColor=this.mainContrastColor,this.doneTaskBkgColor=this.mainContrastColor,this.taskTextDarkColor=this.darkTextColor,this.labelColor=this.textColor,this.altBackground=Object(q.lighten)(this.background,20),this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128}),this.classText=this.primaryTextColor}},{key:"calculate",value:function(t){var e=this;if("object"===Q(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&tt(e.prototype,n),r&&tt(e,r),t}();function nt(t){return(nt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function rt(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var it=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#ECECFF",this.secondaryColor=Object(q.adjust)(this.primaryColor,{h:120}),this.secondaryColor="#ffffde",this.tertiaryColor=Object(q.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.tertiaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.background="white",this.mainBkg="#ECECFF",this.secondBkg="#ffffde",this.lineColor="#333333",this.border1="#9370DB",this.border2="#aaaa33",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.labelBackground="#e8e8e8",this.textColor="#333",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="calculated",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="calculated",this.sectionBkgColor2="calculated",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="calculated",this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor="calculated",this.taskTextOutsideColor=this.taskTextDarkColor,this.taskTextClickableColor="calculated",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBorderColor="calculated",this.critBkgColor="calculated",this.todayLineColor="calculated",this.sectionBkgColor=Object(q.rgba)(102,102,255,.49),this.altSectionBkgColor="white",this.sectionBkgColor2="#fff400",this.taskBorderColor="#534fbc",this.taskBkgColor="#8a90dd",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="#534fbc",this.activeTaskBkgColor="#bfc7ff",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222",this.updateColors()}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.textColor,this.edgeLabelBackground=this.labelBackground,this.actorBorder=Object(q.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.signalColor=this.textColor,this.signalTextColor=this.textColor,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===nt(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&rt(e.prototype,n),r&&rt(e,r),t}();function at(t){return(at="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ot(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var st=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.background="#f4f4f4",this.primaryColor="#cde498",this.secondaryColor="#cdffb2",this.background="white",this.mainBkg="#cde498",this.secondBkg="#cdffb2",this.lineColor="green",this.border1="#13540c",this.border2="#6eaa49",this.arrowheadColor="green",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.tertiaryColor=Object(q.lighten)("#cde498",10),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.primaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="#333",this.edgeLabelBackground="#e8e8e8",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="black",this.actorLineColor="grey",this.signalColor="#333",this.signalTextColor="#333",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="#326932",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="#fff5ad",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="#6eaa49",this.altSectionBkgColor="white",this.sectionBkgColor2="#6eaa49",this.taskBorderColor="calculated",this.taskBkgColor="#487e3a",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="black",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="lightgrey",this.doneTaskBkgColor="lightgrey",this.doneTaskBorderColor="grey",this.critBorderColor="#ff8888",this.critBkgColor="red",this.todayLineColor="red",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.actorBorder=Object(q.darken)(this.mainBkg,20),this.actorBkg=this.mainBkg,this.labelBoxBkgColor=this.actorBkg,this.labelTextColor=this.actorTextColor,this.loopTextColor=this.actorTextColor,this.noteBorderColor=this.border2,this.noteTextColor=this.actorTextColor,this.taskBorderColor=this.border1,this.taskTextColor=this.taskTextLightColor,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===at(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&ot(e.prototype,n),r&&ot(e,r),t}();function ct(t){return(ct="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function ut(t,e){for(var n=0;n<e.length;n++){var r=e[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(t,r.key,r)}}var lt=function(){function t(){!function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,t),this.primaryColor="#eee",this.contrast="#26a",this.secondaryColor=Object(q.lighten)(this.contrast,55),this.background="#ffffff",this.tertiaryColor=Object(q.adjust)(this.primaryColor,{h:-160}),this.primaryBorderColor=X(this.primaryColor,this.darkMode),this.secondaryBorderColor=X(this.secondaryColor,this.darkMode),this.tertiaryBorderColor=X(this.tertiaryColor,this.darkMode),this.primaryTextColor=Object(q.invert)(this.primaryColor),this.secondaryTextColor=Object(q.invert)(this.secondaryColor),this.tertiaryTextColor=Object(q.invert)(this.tertiaryColor),this.lineColor=Object(q.invert)(this.background),this.textColor=Object(q.invert)(this.background),this.altBackground=Object(q.lighten)(this.contrast,55),this.mainBkg="#eee",this.secondBkg="calculated",this.lineColor="#666",this.border1="#999",this.border2="calculated",this.note="#ffa",this.text="#333",this.critical="#d42",this.done="#bbb",this.arrowheadColor="#333333",this.fontFamily='"trebuchet ms", verdana, arial, sans-serif',this.fontSize="16px",this.nodeBkg="calculated",this.nodeBorder="calculated",this.clusterBkg="calculated",this.clusterBorder="calculated",this.defaultLinkColor="calculated",this.titleColor="calculated",this.edgeLabelBackground="white",this.actorBorder="calculated",this.actorBkg="calculated",this.actorTextColor="calculated",this.actorLineColor="calculated",this.signalColor="calculated",this.signalTextColor="calculated",this.labelBoxBkgColor="calculated",this.labelBoxBorderColor="calculated",this.labelTextColor="calculated",this.loopTextColor="calculated",this.noteBorderColor="calculated",this.noteBkgColor="calculated",this.noteTextColor="calculated",this.activationBorderColor="#666",this.activationBkgColor="#f4f4f4",this.sequenceNumberColor="white",this.sectionBkgColor="calculated",this.altSectionBkgColor="white",this.sectionBkgColor2="calculated",this.taskBorderColor="calculated",this.taskBkgColor="calculated",this.taskTextLightColor="white",this.taskTextColor="calculated",this.taskTextDarkColor="calculated",this.taskTextOutsideColor="calculated",this.taskTextClickableColor="#003163",this.activeTaskBorderColor="calculated",this.activeTaskBkgColor="calculated",this.gridColor="calculated",this.doneTaskBkgColor="calculated",this.doneTaskBorderColor="calculated",this.critBkgColor="calculated",this.critBorderColor="calculated",this.todayLineColor="calculated",this.labelColor="black",this.errorBkgColor="#552222",this.errorTextColor="#552222"}var e,n,r;return e=t,(n=[{key:"updateColors",value:function(){this.secondBkg=Object(q.lighten)(this.contrast,55),this.border2=this.contrast,this.nodeBkg=this.mainBkg,this.nodeBorder=this.border1,this.clusterBkg=this.secondBkg,this.clusterBorder=this.border2,this.defaultLinkColor=this.lineColor,this.titleColor=this.text,this.actorBorder=Object(q.lighten)(this.border1,23),this.actorBkg=this.mainBkg,this.actorTextColor=this.text,this.actorLineColor=this.lineColor,this.signalColor=this.text,this.signalTextColor=this.text,this.labelBoxBkgColor=this.actorBkg,this.labelBoxBorderColor=this.actorBorder,this.labelTextColor=this.text,this.loopTextColor=this.text,this.noteBorderColor=Object(q.darken)(this.note,60),this.noteBkgColor=this.note,this.noteTextColor=this.actorTextColor,this.sectionBkgColor=Object(q.lighten)(this.contrast,30),this.sectionBkgColor2=Object(q.lighten)(this.contrast,30),this.taskBorderColor=Object(q.darken)(this.contrast,10),this.taskBkgColor=this.contrast,this.taskTextColor=this.taskTextLightColor,this.taskTextDarkColor=this.text,this.taskTextOutsideColor=this.taskTextDarkColor,this.activeTaskBorderColor=this.taskBorderColor,this.activeTaskBkgColor=this.mainBkg,this.gridColor=Object(q.lighten)(this.border1,30),this.doneTaskBkgColor=this.done,this.doneTaskBorderColor=this.lineColor,this.critBkgColor=this.critical,this.critBorderColor=Object(q.darken)(this.critBkgColor,10),this.todayLineColor=this.critBkgColor,this.classText=this.primaryTextColor,this.fillType0=this.primaryColor,this.fillType1=this.secondaryColor,this.fillType2=Object(q.adjust)(this.primaryColor,{h:64}),this.fillType3=Object(q.adjust)(this.secondaryColor,{h:64}),this.fillType4=Object(q.adjust)(this.primaryColor,{h:-64}),this.fillType5=Object(q.adjust)(this.secondaryColor,{h:-64}),this.fillType6=Object(q.adjust)(this.primaryColor,{h:128}),this.fillType7=Object(q.adjust)(this.secondaryColor,{h:128})}},{key:"calculate",value:function(t){var e=this;if("object"===ct(t)){var n=Object.keys(t);n.forEach((function(n){e[n]=t[n]})),this.updateColors(),n.forEach((function(n){e[n]=t[n]}))}else this.updateColors()}}])&&ut(e.prototype,n),r&&ut(e,r),t}(),ht={base:{getThemeVariables:function(t){var e=new K;return e.calculate(t),e}},dark:{getThemeVariables:function(t){var e=new et;return e.calculate(t),e}},default:{getThemeVariables:function(t){var e=new it;return e.calculate(t),e}},forest:{getThemeVariables:function(t){var e=new st;return e.calculate(t),e}},neutral:{getThemeVariables:function(t){var e=new lt;return e.calculate(t),e}}},ft={theme:"default",themeVariables:ht.default.getThemeVariables(),themeCSS:void 0,maxTextSize:5e4,fontFamily:'"trebuchet ms", verdana, arial, sans-serif;',logLevel:5,securityLevel:"strict",startOnLoad:!0,arrowMarkerAbsolute:!1,secure:["secure","securityLevel","startOnLoad","maxTextSize"],deterministicIds:!1,deterministicIDSeed:void 0,flowchart:{diagramPadding:8,htmlLabels:!0,nodeSpacing:50,rankSpacing:50,curve:"linear",padding:15,useMaxWidth:!0},sequence:{activationWidth:10,diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",mirrorActors:!0,bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1,showSequenceNumbers:!1,actorFontSize:14,actorFontFamily:'"Open-Sans", "sans-serif"',actorFontWeight:400,noteFontSize:14,noteFontFamily:'"trebuchet ms", verdana, arial, sans-serif',noteFontWeight:400,noteAlign:"center",messageFontSize:16,messageFontFamily:'"trebuchet ms", verdana, arial, sans-serif',messageFontWeight:400,wrap:!1,wrapPadding:10,labelBoxWidth:50,labelBoxHeight:20,messageFont:function(){return{fontFamily:this.messageFontFamily,fontSize:this.messageFontSize,fontWeight:this.messageFontWeight}},noteFont:function(){return{fontFamily:this.noteFontFamily,fontSize:this.noteFontSize,fontWeight:this.noteFontWeight}},actorFont:function(){return{fontFamily:this.actorFontFamily,fontSize:this.actorFontSize,fontWeight:this.actorFontWeight}}},gantt:{titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,leftPadding:75,gridLineStartPadding:35,fontSize:11,fontFamily:'"Open-Sans", "sans-serif"',numberSectionStyles:4,axisFormat:"%Y-%m-%d",useMaxWidth:!0,useWidth:void 0},journey:{diagramMarginX:50,diagramMarginY:10,actorMargin:50,width:150,height:65,boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,useMaxWidth:!0,rightAngles:!1},class:{arrowMarkerAbsolute:!1,useMaxWidth:!0},git:{arrowMarkerAbsolute:!1,useWidth:void 0,useMaxWidth:!0},state:{dividerMargin:10,sizeUnit:5,padding:8,textHeight:10,titleShift:-15,noteMargin:10,forkWidth:70,forkHeight:7,miniPadding:2,fontSizeFactor:5.02,fontSize:24,labelHeight:16,edgeLengthFactor:"20",compositTitleSize:35,radius:5,useMaxWidth:!0},er:{diagramPadding:20,layoutDirection:"TB",minEntityWidth:100,minEntityHeight:75,entityPadding:15,stroke:"gray",fill:"honeydew",fontSize:12,useMaxWidth:!0},pie:{useWidth:void 0,useMaxWidth:!0}};ft.class.arrowMarkerAbsolute=ft.arrowMarkerAbsolute,ft.git.arrowMarkerAbsolute=ft.arrowMarkerAbsolute;var dt,pt=ft,gt=Object.freeze(pt),yt=F({},gt),vt=[],mt=F({},gt),bt=function(t,e){for(var n=F({},t),r={},i=0;i<e.length;i++){var a=e[i];kt(a),r=F(r,a)}if(n=F(n,r),r.theme){var o=F(dt.themeVariables||{},r.themeVariables);n.themeVariables=ht[n.theme].getThemeVariables(o)}return mt=n,n},xt=function(){return F({},yt)},_t=function(){return F({},mt)},kt=function(t){Object.keys(yt.secure).forEach((function(e){void 0!==t[yt.secure[e]]&&(c.debug("Denied attempt to modify a secure key ".concat(yt.secure[e]),t[yt.secure[e]]),delete t[yt.secure[e]])}))},wt=function(t){t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),vt.push(t),bt(yt,vt)},Et=function(){bt(yt,vt=[])};function Tt(t){return(Tt="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function Ct(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var At,St,Mt=0,Ot=_t(),Dt={},Nt=[],Bt=[],Lt=[],Pt={},It={},Ft=0,jt=!0,Rt=[],Yt=function(t){for(var e=Object.keys(Dt),n=0;n<e.length;n++)if(Dt[e[n]].id===t)return Dt[e[n]].domId;return t},zt=function(t,e,n,r){var i={start:t,end:e,type:void 0,text:""};void 0!==(r=n.text)&&(i.text=x.sanitizeText(r.trim(),Ot),'"'===i.text[0]&&'"'===i.text[i.text.length-1]&&(i.text=i.text.substring(1,i.text.length-1))),void 0!==n&&(i.type=n.type,i.stroke=n.stroke,i.length=n.length),Nt.push(i)},Ut=function(t,e){t.split(",").forEach((function(t){var n=t;void 0!==Dt[n]&&Dt[n].classes.push(e),void 0!==Pt[n]&&Pt[n].classes.push(e)}))},$t=function(t){var e=Object(d.select)(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=Object(d.select)("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),Object(d.select)(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=Object(d.select)(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),Object(d.select)(this).classed("hover",!1)}))};Rt.push($t);var Wt=function(t){for(var e=0;e<Lt.length;e++)if(Lt[e].id===t)return e;return-1},Ht=-1,Vt=[],Gt=function(t,e){var n=!1;return t.forEach((function(t){t.nodes.indexOf(e)>=0&&(n=!0)})),n},qt=function(t,e){var n=[];return t.nodes.forEach((function(r,i){Gt(e,r)||n.push(t.nodes[i])})),{nodes:n}},Xt={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},defaultConfig:function(){return gt.flowchart},addVertex:function(t,e,n,r,i){var a,o=t;void 0!==o&&0!==o.trim().length&&(void 0===Dt[o]&&(Dt[o]={id:o,domId:"flowchart-"+o+"-"+Mt,styles:[],classes:[]}),Mt++,void 0!==e?(Ot=_t(),'"'===(a=x.sanitizeText(e.trim(),Ot))[0]&&'"'===a[a.length-1]&&(a=a.substring(1,a.length-1)),Dt[o].text=a):void 0===Dt[o].text&&(Dt[o].text=t),void 0!==n&&(Dt[o].type=n),null!=r&&r.forEach((function(t){Dt[o].styles.push(t)})),null!=i&&i.forEach((function(t){Dt[o].classes.push(t)})))},lookUpDomId:Yt,addLink:function(t,e,n,r){var i,a;for(i=0;i<t.length;i++)for(a=0;a<e.length;a++)zt(t[i],e[a],n,r)},updateLinkInterpolate:function(t,e){t.forEach((function(t){"default"===t?Nt.defaultInterpolate=e:Nt[t].interpolate=e}))},updateLink:function(t,e){t.forEach((function(t){"default"===t?Nt.defaultStyle=e:(-1===H.isSubstringInArray("fill",e)&&e.push("fill:none"),Nt[t].style=e)}))},addClass:function(t,e){void 0===Bt[t]&&(Bt[t]={id:t,styles:[],textStyles:[]}),null!=e&&e.forEach((function(e){if(e.match("color")){var n=e.replace("fill","bgFill").replace("color","fill");Bt[t].textStyles.push(n)}Bt[t].styles.push(e)}))},setDirection:function(t){(At=t).match(/.*</)&&(At="RL"),At.match(/.*\^/)&&(At="BT"),At.match(/.*>/)&&(At="LR"),At.match(/.*v/)&&(At="TB")},setClass:Ut,setTooltip:function(t,e){t.split(",").forEach((function(t){void 0!==e&&(It["gen-1"===St?Yt(t):t]=x.sanitizeText(e,Ot))}))},getTooltip:function(t){return It[t]},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){var r=Yt(t);if("loose"===_t().securityLevel&&void 0!==e){var i=[];if("string"==typeof n){i=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var a=0;a<i.length;a++){var o=i[a].trim();'"'===o.charAt(0)&&'"'===o.charAt(o.length-1)&&(o=o.substr(1,o.length-2)),i[a]=o}}0===i.length&&i.push(t),void 0!==Dt[t]&&(Dt[t].haveCallback=!0,Rt.push((function(){var t=document.querySelector('[id="'.concat(r,'"]'));null!==t&&t.addEventListener("click",(function(){H.runFunc.apply(H,[e].concat(Ct(i)))}),!1)})))}}(t,e,n)})),Ut(t,"clickable")},setLink:function(t,e,n){t.split(",").forEach((function(t){void 0!==Dt[t]&&(Dt[t].link=H.formatUrl(e,Ot),Dt[t].linkTarget=n)})),Ut(t,"clickable")},bindFunctions:function(t){Rt.forEach((function(e){e(t)}))},getDirection:function(){return At.trim()},getVertices:function(){return Dt},getEdges:function(){return Nt},getClasses:function(){return Bt},clear:function(t){Dt={},Bt={},Nt=[],(Rt=[]).push($t),Lt=[],Pt={},Ft=0,It=[],jt=!0,St=t||"gen-1"},setGen:function(t){St=t||"gen-1"},defaultStyle:function(){return"fill:#ffa;stroke: #f66; stroke-width: 3px; stroke-dasharray: 5, 5;fill:#ffa;stroke: #666;"},addSubGraph:function(t,e,n){var r=t.trim(),i=n;t===n&&n.match(/\s/)&&(r=void 0);var a,o,s,u=[];if(a=u.concat.apply(u,e),o={boolean:{},number:{},string:{}},s=[],u=a.filter((function(t){var e=Tt(t);return""!==t.trim()&&(e in o?!o[e].hasOwnProperty(t)&&(o[e][t]=!0):!(s.indexOf(t)>=0)&&s.push(t))})),"gen-1"===St){c.warn("LOOKING UP");for(var l=0;l<u.length;l++)u[l]=Yt(u[l])}r=r||"subGraph"+Ft,i=i||"",i=x.sanitizeText(i,Ot),Ft+=1;var h={id:r,nodes:u,title:i.trim(),classes:[]};return c.info("Adding",h.id,h.nodes),h.nodes=qt(h,Lt).nodes,Lt.push(h),Pt[r]=h,r},getDepthFirstPos:function(t){return Vt[t]},indexNodes:function(){Ht=-1,Lt.length>0&&function t(e,n){var r=Lt[n].nodes;if(!((Ht+=1)>2e3)){if(Vt[Ht]=n,Lt[n].id===e)return{result:!0,count:0};for(var i=0,a=1;i<r.length;){var o=Wt(r[i]);if(o>=0){var s=t(e,o);if(s.result)return{result:!0,count:a+s.count};a+=s.count}i+=1}return{result:!1,count:a}}}("none",Lt.length-1)},getSubGraphs:function(){return Lt},destructLink:function(t,e){var n,r=function(t){var e=t.trim(),n=e.slice(0,-1),r="arrow_open";switch(e.slice(-1)){case"x":r="arrow_cross","x"===e[0]&&(r="double_"+r,n=n.slice(1));break;case">":r="arrow_point","<"===e[0]&&(r="double_"+r,n=n.slice(1));break;case"o":r="arrow_circle","o"===e[0]&&(r="double_"+r,n=n.slice(1))}var i="normal",a=n.length-1;"="===n[0]&&(i="thick");var o=function(t,e){for(var n=e.length,r=0,i=0;i<n;++i)e[i]===t&&++r;return r}(".",n);return o&&(i="dotted",a=o),{type:r,stroke:i,length:a}}(t);if(e){if((n=function(t){var e=t.trim(),n="arrow_open";switch(e[0]){case"<":n="arrow_point",e=e.slice(1);break;case"x":n="arrow_cross",e=e.slice(1);break;case"o":n="arrow_circle",e=e.slice(1)}var r="normal";return-1!==e.indexOf("=")&&(r="thick"),-1!==e.indexOf(".")&&(r="dotted"),{type:n,stroke:r}}(e)).stroke!==r.stroke)return{type:"INVALID",stroke:"INVALID"};if("arrow_open"===n.type)n.type=r.type;else{if(n.type!==r.type)return{type:"INVALID",stroke:"INVALID"};n.type="double_"+n.type}return"double_arrow"===n.type&&(n.type="double_arrow_point"),n.length=r.length,n}return r},lex:{firstGraph:function(){return!!jt&&(jt=!1,!0)}},exists:Gt,makeUniq:qt},Zt=n(26),Jt=n.n(Zt),Kt=n(6),Qt=n.n(Kt),te=n(50),ee=n.n(te);function ne(t,e,n){var r=.9*(e.width+e.height),i=[{x:r/2,y:0},{x:r,y:-r/2},{x:r/2,y:-r},{x:0,y:-r/2}],a=de(t,r,r,i);return n.intersect=function(t){return Qt.a.intersect.polygon(n,i,t)},a}function re(t,e,n){var r=e.height,i=r/4,a=e.width+2*i,o=[{x:i,y:0},{x:a-i,y:0},{x:a,y:-r/2},{x:a-i,y:-r},{x:i,y:-r},{x:0,y:-r/2}],s=de(t,a,r,o);return n.intersect=function(t){return Qt.a.intersect.polygon(n,o,t)},s}function ie(t,e,n){var r=e.width,i=e.height,a=[{x:-i/2,y:0},{x:r,y:0},{x:r,y:-i},{x:-i/2,y:-i},{x:0,y:-i/2}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function ae(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function oe(t,e,n){var r=e.width,i=e.height,a=[{x:2*i/6,y:0},{x:r+i/6,y:0},{x:r-2*i/6,y:-i},{x:-i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function se(t,e,n){var r=e.width,i=e.height,a=[{x:-2*i/6,y:0},{x:r+2*i/6,y:0},{x:r-i/6,y:-i},{x:i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function ce(t,e,n){var r=e.width,i=e.height,a=[{x:i/6,y:0},{x:r-i/6,y:0},{x:r+2*i/6,y:-i},{x:-2*i/6,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function ue(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r+i/2,y:0},{x:r,y:-i/2},{x:r+i/2,y:-i},{x:0,y:-i}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function le(t,e,n){var r=e.height,i=e.width+r/4,a=t.insert("rect",":first-child").attr("rx",r/2).attr("ry",r/2).attr("x",-i/2).attr("y",-r/2).attr("width",i).attr("height",r);return n.intersect=function(t){return Qt.a.intersect.rect(n,t)},a}function he(t,e,n){var r=e.width,i=e.height,a=[{x:0,y:0},{x:r,y:0},{x:r,y:-i},{x:0,y:-i},{x:0,y:0},{x:-8,y:0},{x:r+8,y:0},{x:r+8,y:-i},{x:-8,y:-i},{x:-8,y:0}],o=de(t,r,i,a);return n.intersect=function(t){return Qt.a.intersect.polygon(n,a,t)},o}function fe(t,e,n){var r=e.width,i=r/2,a=i/(2.5+r/50),o=e.height+a,s="M 0,"+a+" a "+i+","+a+" 0,0,0 "+r+" 0 a "+i+","+a+" 0,0,0 "+-r+" 0 l 0,"+o+" a "+i+","+a+" 0,0,0 "+r+" 0 l 0,"+-o,c=t.attr("label-offset-y",a).insert("path",":first-child").attr("d",s).attr("transform","translate("+-r/2+","+-(o/2+a)+")");return n.intersect=function(t){var e=Qt.a.intersect.rect(n,t),r=e.x-n.x;if(0!=i&&(Math.abs(r)<n.width/2||Math.abs(r)==n.width/2&&Math.abs(e.y-n.y)>n.height/2-a)){var o=a*a*(1-r*r/(i*i));0!=o&&(o=Math.sqrt(o)),o=a-o,t.y-n.y>0&&(o=-o),e.y+=o}return e},c}function de(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("transform","translate("+-e/2+","+n/2+")")}var pe={addToRender:function(t){t.shapes().question=ne,t.shapes().hexagon=re,t.shapes().stadium=le,t.shapes().subroutine=he,t.shapes().cylinder=fe,t.shapes().rect_left_inv_arrow=ie,t.shapes().lean_right=ae,t.shapes().lean_left=oe,t.shapes().trapezoid=se,t.shapes().inv_trapezoid=ce,t.shapes().rect_right_inv_arrow=ue},addToRenderV2:function(t){t({question:ne}),t({hexagon:re}),t({stadium:le}),t({subroutine:he}),t({cylinder:fe}),t({rect_left_inv_arrow:ie}),t({lean_right:ae}),t({lean_left:oe}),t({trapezoid:se}),t({inv_trapezoid:ce}),t({rect_right_inv_arrow:ue})}},ge={},ye=function(t,e,n){var r=Object(d.select)('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var o,s=B(i.styles),u=void 0!==i.text?i.text:i.id;if(_t().flowchart.htmlLabels){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(o=ee()(r,l).node()).parentNode.removeChild(o)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",s.labelStyle.replace("color:","fill:"));for(var f=u.split(x.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}o=h}var g=0,y="";switch(i.type){case"round":g=5,y="rect";break;case"square":y="rect";break;case"diamond":y="question";break;case"hexagon":y="hexagon";break;case"odd":y="rect_left_inv_arrow";break;case"lean_right":y="lean_right";break;case"lean_left":y="lean_left";break;case"trapezoid":y="trapezoid";break;case"inv_trapezoid":y="inv_trapezoid";break;case"odd_right":y="rect_left_inv_arrow";break;case"circle":y="circle";break;case"ellipse":y="ellipse";break;case"stadium":y="stadium";break;case"subroutine":y="subroutine";break;case"cylinder":y="cylinder";break;case"group":y="rect";break;default:y="rect"}c.warn("Adding node",i.id,i.domId),e.setNode(Xt.lookUpDomId(i.id),{labelType:"svg",labelStyle:s.labelStyle,shape:y,label:o,rx:g,ry:g,class:a,style:s.style,id:Xt.lookUpDomId(i.id)})}))},ve=function(t,e){var n,r,i=0;if(void 0!==t.defaultStyle){var a=B(t.defaultStyle);n=a.style,r=a.labelStyle}t.forEach((function(a){i++;var o="L-"+a.start+"-"+a.end,s="LS-"+a.start,c="LE-"+a.end,u={};"arrow_open"===a.type?u.arrowhead="none":u.arrowhead="normal";var l="",h="";if(void 0!==a.style){var f=B(a.style);l=f.style,h=f.labelStyle}else switch(a.stroke){case"normal":l="fill:none",void 0!==n&&(l=n),void 0!==r&&(h=r);break;case"dotted":l="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":l=" stroke-width: 3.5px;fill:none"}u.style=l,u.labelStyle=h,void 0!==a.interpolate?u.curve=D(a.interpolate,d.curveLinear):void 0!==t.defaultInterpolate?u.curve=D(t.defaultInterpolate,d.curveLinear):u.curve=D(ge.curve,d.curveLinear),void 0===a.text?void 0!==a.style&&(u.arrowheadStyle="fill: #333"):(u.arrowheadStyle="fill: #333",u.labelpos="c",_t().flowchart.htmlLabels?(u.labelType="html",u.label='<span id="L-'.concat(o,'" class="edgeLabel L-').concat(s,"' L-").concat(c,'">').concat(a.text.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),"</span>")):(u.labelType="text",u.label=a.text.replace(x.lineBreakRegex,"\n"),void 0===a.style&&(u.style=u.style||"stroke: #333; stroke-width: 1.5px;fill:none"),u.labelStyle=u.labelStyle.replace("color:","fill:"))),u.id=o,u.class=s+" "+c,u.minlen=a.length||1,e.setEdge(Xt.lookUpDomId(a.start),Xt.lookUpDomId(a.end),u,i)}))},me=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)ge[e[n]]=t[e[n]]},be=function(t){c.info("Extracting classes"),Xt.clear();try{var e=Jt.a.parser;return e.yy=Xt,e.parse(t),Xt.getClasses()}catch(t){return}},xe=function(t,e){c.info("Drawing flowchart"),Xt.clear(),Xt.setGen("gen-1");var n=Jt.a.parser;n.yy=Xt,n.parse(t);var r=Xt.getDirection();void 0===r&&(r="TD");for(var i,a=_t().flowchart,o=a.nodeSpacing||50,s=a.rankSpacing||50,u=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:o,ranksep:s,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=Xt.getSubGraphs(),h=l.length-1;h>=0;h--)i=l[h],Xt.addVertex(i.id,i.title,"group",void 0,i.classes);var f=Xt.getVertices();c.warn("Get vertices",f);var p=Xt.getEdges(),g=0;for(g=l.length-1;g>=0;g--){i=l[g],Object(d.selectAll)("cluster").append("text");for(var y=0;y<i.nodes.length;y++)c.warn("Setting subgraph",i.nodes[y],Xt.lookUpDomId(i.nodes[y]),Xt.lookUpDomId(i.id)),u.setParent(Xt.lookUpDomId(i.nodes[y]),Xt.lookUpDomId(i.id))}ye(f,u,e),ve(p,u);var v=new(0,Qt.a.render);pe.addToRender(v),v.arrows().none=function(t,e,n,r){var i=t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 0 0 L 0 0 z");Qt.a.util.applyStyle(i,n[r+"Style"])},v.arrows().normal=function(t,e){t.append("marker").attr("id",e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","strokeWidth").attr("markerWidth",8).attr("markerHeight",6).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowheadPath").style("stroke-width",1).style("stroke-dasharray","1,0")};var m=Object(d.select)('[id="'.concat(e,'"]'));m.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),c.warn(u);var b=Object(d.select)("#"+e+" g");v(b,u),b.selectAll("g.node").attr("title",(function(){return Xt.getTooltip(this.id)}));var x=a.diagramPadding,_=m.node().getBBox(),k=_.width+2*x,w=_.height+2*x;W(m,w,k,a.useMaxWidth);var E="".concat(_.x-x," ").concat(_.y-x," ").concat(k," ").concat(w);for(c.debug("viewBox ".concat(E)),m.attr("viewBox",E),Xt.indexNodes("subGraph"+g),g=0;g<l.length;g++)if("undefined"!==(i=l[g]).title){var T=document.querySelectorAll("#"+e+' [id="'+Xt.lookUpDomId(i.id)+'"] rect'),C=document.querySelectorAll("#"+e+' [id="'+Xt.lookUpDomId(i.id)+'"]'),A=T[0].x.baseVal.value,S=T[0].y.baseVal.value,M=T[0].width.baseVal.value,O=Object(d.select)(C[0]).select(".label");O.attr("transform","translate(".concat(A+M/2,", ").concat(S+14,")")),O.attr("id",e+"Text");for(var D=0;D<i.classes.length;D++)C[0].classList.add(i.classes[D])}a.htmlLabels;for(var N=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),B=0;B<N.length;B++){var L=N[B],P=L.getBBox(),I=document.createElementNS("http://www.w3.org/2000/svg","rect");I.setAttribute("rx",0),I.setAttribute("ry",0),I.setAttribute("width",P.width),I.setAttribute("height",P.height),L.insertBefore(I,L.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=Object(d.select)("#"+e+' [id="'+Xt.lookUpDomId(t)+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))},_e=n(18),ke=n.n(_e),we={extension:function(t,e,n){c.trace("Making markers for ",n),t.append("defs").append("marker").attr("id",e+"-extensionStart").attr("class","marker extension "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),t.append("defs").append("marker").attr("id",e+"-extensionEnd").attr("class","marker extension "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z")},composition:function(t,e){t.append("defs").append("marker").attr("id",e+"-compositionStart").attr("class","marker composition "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-compositionEnd").attr("class","marker composition "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},aggregation:function(t,e){t.append("defs").append("marker").attr("id",e+"-aggregationStart").attr("class","marker aggregation "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-aggregationEnd").attr("class","marker aggregation "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z")},dependency:function(t,e){t.append("defs").append("marker").attr("id",e+"-dependencyStart").attr("class","marker dependency "+e).attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),t.append("defs").append("marker").attr("id",e+"-dependencyEnd").attr("class","marker dependency "+e).attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},point:function(t,e){t.append("marker").attr("id",e+"-pointEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-pointStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",0).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 5 L 10 10 L 10 0 z").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},circle:function(t,e){t.append("marker").attr("id",e+"-circleEnd").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",11).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-circleStart").attr("class","marker "+e).attr("viewBox","0 0 10 10").attr("refX",-1).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("circle").attr("cx","5").attr("cy","5").attr("r","5").attr("class","arrowMarkerPath").style("stroke-width",1).style("stroke-dasharray","1,0")},cross:function(t,e){t.append("marker").attr("id",e+"-crossEnd").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",12).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0"),t.append("marker").attr("id",e+"-crossStart").attr("class","marker cross "+e).attr("viewBox","0 0 11 11").attr("refX",-1).attr("refY",5.2).attr("markerUnits","userSpaceOnUse").attr("markerWidth",11).attr("markerHeight",11).attr("orient","auto").append("path").attr("d","M 1,1 l 9,9 M 10,1 l -9,9").attr("class","arrowMarkerPath").style("stroke-width",2).style("stroke-dasharray","1,0")},barb:function(t,e){t.append("defs").append("marker").attr("id",e+"-barbEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",14).attr("markerUnits","strokeWidth").attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z")}},Ee=function(t,e,n,r){e.forEach((function(e){we[e](t,n,r)}))};var Te=function(t,e,n,r){var i=t||"";if(_t().flowchart.htmlLabels)return i=i.replace(/\\n|\n/g,"<br />"),c.info("vertexText"+i),function(t){var e,n,r=Object(d.select)(document.createElementNS("http://www.w3.org/2000/svg","foreignObject")),i=r.append("xhtml:div"),a=t.label,o=t.isNode?"nodeLabel":"edgeLabel";return i.html('<span class="'+o+'">'+a+"</span>"),e=i,(n=t.labelStyle)&&e.attr("style",n),i.style("display","inline-block"),i.style("white-space","nowrap"),i.attr("xmlns","http://www.w3.org/1999/xhtml"),r.node()}({isNode:r,label:i.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")})),labelStyle:e.replace("fill:","color:")});var a=document.createElementNS("http://www.w3.org/2000/svg","text");a.setAttribute("style",e.replace("color:","fill:"));var o=[];o="string"==typeof i?i.split(/\\n|\n|<br\s*\/?>/gi):Array.isArray(i)?i:[];for(var s=0;s<o.length;s++){var u=document.createElementNS("http://www.w3.org/2000/svg","tspan");u.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),u.setAttribute("dy","1em"),u.setAttribute("x","0"),n?u.setAttribute("class","title-row"):u.setAttribute("class","row"),u.textContent=o[s].trim(),a.appendChild(u)}return a},Ce=function(t,e,n,r){var i;i=n||"node default";var a=t.insert("g").attr("class",i).attr("id",e.domId||e.id),o=a.insert("g").attr("class","label").attr("style",e.labelStyle),s=o.node().appendChild(Te(e.labelText,e.labelStyle,!1,r)),c=s.getBBox();if(_t().flowchart.htmlLabels){var u=s.children[0],l=Object(d.select)(s);c=u.getBoundingClientRect(),l.attr("width",c.width),l.attr("height",c.height)}var h=e.padding/2;return o.attr("transform","translate("+-c.width/2+", "+-c.height/2+")"),{shapeSvg:a,bbox:c,halfPadding:h,label:o}},Ae=function(t,e){var n=e.node().getBBox();t.width=n.width,t.height=n.height};function Se(t,e,n,r){return t.insert("polygon",":first-child").attr("points",r.map((function(t){return t.x+","+t.y})).join(" ")).attr("class","label-container").attr("transform","translate("+-e/2+","+n/2+")")}var Me={},Oe={},De={},Ne=function(t,e){return c.debug("In isDecendant",e," ",t," = ",Oe[e].indexOf(t)>=0),Oe[e].indexOf(t)>=0},Be=function t(e,n,r,i){c.warn("Copying children of ",e,"root",i,"data",n.node(e),i);var a=n.children(e)||[];e!==i&&a.push(e),c.warn("Copying (nodes) clusterId",e,"nodes",a),a.forEach((function(a){if(n.children(a).length>0)t(a,n,r,i);else{var o=n.node(a);c.info("cp ",a," to ",i," with parent ",e),r.setNode(a,o),i!==n.parent(a)&&(c.warn("Setting parent",a,n.parent(a)),r.setParent(a,n.parent(a))),e!==i&&a!==e?(c.debug("Setting parent",a,e),r.setParent(a,e)):(c.info("In copy ",e,"root",i,"data",n.node(e),i),c.debug("Not Setting parent for node=",a,"cluster!==rootId",e!==i,"node!==clusterId",a!==e));var s=n.edges(a);c.debug("Copying Edges",s),s.forEach((function(t){c.info("Edge",t);var a=n.edge(t.v,t.w,t.name);c.info("Edge data",a,i);try{!function(t,e){return c.info("Decendants of ",e," is ",Oe[e]),c.info("Edge is ",t),t.v!==e&&(t.w!==e&&(Oe[e]?(c.info("Here "),Oe[e].indexOf(t.v)>=0||(!!Ne(t.v,e)||(!!Ne(t.w,e)||Oe[e].indexOf(t.w)>=0))):(c.debug("Tilt, ",e,",not in decendants"),!1)))}(t,i)?c.info("Skipping copy of edge ",t.v,"--\x3e",t.w," rootId: ",i," clusterId:",e):(c.info("Copying as ",t.v,t.w,a,t.name),r.setEdge(t.v,t.w,a,t.name),c.info("newGraph edges ",r.edges(),r.edge(r.edges()[0])))}catch(t){c.error(t)}}))}c.debug("Removing node",a),n.removeNode(a)}))},Le=function t(e,n){c.trace("Searching",e);var r=n.children(e);if(c.trace("Searching children of id ",e,r),r.length<1)return c.trace("This is a valid node",e),e;for(var i=0;i<r.length;i++){var a=t(r[i],n);if(a)return c.trace("Found replacement for",e," => ",a),a}},Pe=function(t){return Me[t]&&Me[t].externalConnections&&Me[t]?Me[t].id:t},Ie=function(t,e){!t||e>10?c.debug("Opting out, no graph "):(c.debug("Opting in, graph "),t.nodes().forEach((function(e){t.children(e).length>0&&(c.warn("Cluster identified",e," Replacement id in edges: ",Le(e,t)),Oe[e]=function t(e,n){for(var r=n.children(e),i=[].concat(r),a=0;a<r.length;a++)De[r[a]]=e,i=i.concat(t(r[a],n));return i}(e,t),Me[e]={id:Le(e,t),clusterData:t.node(e)})})),t.nodes().forEach((function(e){var n=t.children(e),r=t.edges();n.length>0?(c.debug("Cluster identified",e,Oe),r.forEach((function(t){t.v!==e&&t.w!==e&&(Ne(t.v,e)^Ne(t.w,e)&&(c.warn("Edge: ",t," leaves cluster ",e),c.warn("Decendants of XXX ",e,": ",Oe[e]),Me[e].externalConnections=!0))}))):c.debug("Not a cluster ",e,Oe)})),t.edges().forEach((function(e){var n=t.edge(e);c.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(e)),c.warn("Edge "+e.v+" -> "+e.w+": "+JSON.stringify(t.edge(e)));var r=e.v,i=e.w;c.warn("Fix XXX",Me,"ids:",e.v,e.w,"Translateing: ",Me[e.v]," --- ",Me[e.w]),(Me[e.v]||Me[e.w])&&(c.warn("Fixing and trixing - removing XXX",e.v,e.w,e.name),r=Pe(e.v),i=Pe(e.w),t.removeEdge(e.v,e.w,e.name),r!==e.v&&(n.fromCluster=e.v),i!==e.w&&(n.toCluster=e.w),c.warn("Fix Replacing with XXX",r,i,e.name),t.setEdge(r,i,n,e.name))})),c.warn("Adjusted Graph",G.a.json.write(t)),Fe(t,0),c.trace(Me))},Fe=function t(e,n){if(c.warn("extractor - ",n,G.a.json.write(e),e.children("D")),n>10)c.error("Bailing out");else{for(var r=e.nodes(),i=!1,a=0;a<r.length;a++){var o=r[a],s=e.children(o);i=i||s.length>0}if(i){c.debug("Nodes = ",r,n);for(var u=0;u<r.length;u++){var l=r[u];if(c.debug("Extracting node",l,Me,Me[l]&&!Me[l].externalConnections,!e.parent(l),e.node(l),e.children("D")," Depth ",n),Me[l])if(!Me[l].externalConnections&&e.children(l)&&e.children(l).length>0){c.warn("Cluster without external connections, without a parent and with children",l,n);var h=e.graph(),f=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:"TB"===h.rankdir?"LR":"TB",nodesep:50,ranksep:50,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));c.warn("Old graph before copy",G.a.json.write(e)),Be(l,e,f,l),e.setNode(l,{clusterNode:!0,id:l,clusterData:Me[l].clusterData,labelText:Me[l].labelText,graph:f}),c.warn("New graph after copy node: (",l,")",G.a.json.write(f)),c.debug("Old graph after copy",G.a.json.write(e))}else c.warn("Cluster ** ",l," **not meeting the criteria !externalConnections:",!Me[l].externalConnections," no parent: ",!e.parent(l)," children ",e.children(l)&&e.children(l).length>0,e.children("D"),n),c.debug(Me);else c.debug("Not a cluster",l,n)}r=e.nodes(),c.warn("New list of nodes",r);for(var d=0;d<r.length;d++){var p=r[d],g=e.node(p);c.warn(" Now next level",p,g),g.clusterNode&&t(g.graph,n+1)}}else c.debug("Done, no node has children",e.nodes())}},je=function(t){return function t(e,n){if(0===n.length)return[];var r=Object.assign(n);return n.forEach((function(n){var i=e.children(n),a=t(e,i);r=r.concat(a)})),r}(t,t.children())},Re=n(170);var Ye=function(t,e,n,r){var i=t.x,a=t.y,o=i-r.x,s=a-r.y,c=Math.sqrt(e*e*s*s+n*n*o*o),u=Math.abs(e*n*o/c);r.x<i&&(u=-u);var l=Math.abs(e*n*s/c);return r.y<a&&(l=-l),{x:i+u,y:a+l}};var ze=function(t,e,n){return Ye(t,e,e,n)};function Ue(t,e){return t*e>0}var $e=function(t,e,n,r){var i,a,o,s,c,u,l,h,f,d,p,g,y;if(i=e.y-t.y,o=t.x-e.x,c=e.x*t.y-t.x*e.y,f=i*n.x+o*n.y+c,d=i*r.x+o*r.y+c,!(0!==f&&0!==d&&Ue(f,d)||(a=r.y-n.y,s=n.x-r.x,u=r.x*n.y-n.x*r.y,l=a*t.x+s*t.y+u,h=a*e.x+s*e.y+u,0!==l&&0!==h&&Ue(l,h)||0==(p=i*s-a*o))))return g=Math.abs(p/2),{x:(y=o*u-s*c)<0?(y-g)/p:(y+g)/p,y:(y=a*c-i*u)<0?(y-g)/p:(y+g)/p}},We=function(t,e,n){var r=t.x,i=t.y,a=[],o=Number.POSITIVE_INFINITY,s=Number.POSITIVE_INFINITY;"function"==typeof e.forEach?e.forEach((function(t){o=Math.min(o,t.x),s=Math.min(s,t.y)})):(o=Math.min(o,e.x),s=Math.min(s,e.y));for(var c=r-t.width/2-o,u=i-t.height/2-s,l=0;l<e.length;l++){var h=e[l],f=e[l<e.length-1?l+1:0],d=$e(t,n,{x:c+h.x,y:u+h.y},{x:c+f.x,y:u+f.y});d&&a.push(d)}if(!a.length)return t;a.length>1&&a.sort((function(t,e){var r=t.x-n.x,i=t.y-n.y,a=Math.sqrt(r*r+i*i),o=e.x-n.x,s=e.y-n.y,c=Math.sqrt(o*o+s*s);return a<c?-1:a===c?0:1}));return a[0]};var He=function(t,e){var n,r,i=t.x,a=t.y,o=e.x-i,s=e.y-a,c=t.width/2,u=t.height/2;return Math.abs(s)*c>Math.abs(o)*u?(s<0&&(u=-u),n=0===s?0:u*o/s,r=u):(o<0&&(c=-c),n=c,r=0===o?0:c*s/o),{x:i+n,y:a+r}},Ve={node:n.n(Re).a,circle:ze,ellipse:Ye,polygon:We,rect:He},Ge=function(t,e){var n=Ce(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;c.info("Classes = ",e.classes);var o=r.insert("rect",":first-child");return o.attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),Ae(e,o),e.intersect=function(t){return Ve.rect(e,t)},r};function qe(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var Xe=[],Ze={},Je=0,Ke=[],Qe=function(t){var e="",n=t;if(t.indexOf("~")>0){var r=t.split("~");n=r[0],e=r[1]}return{className:n,type:e}},tn=function(t){var e=Qe(t);void 0===Ze[e.className]&&(Ze[e.className]={id:e.className,type:e.type,cssClasses:[],methods:[],members:[],annotations:[],domId:"classid-"+e.className+"-"+Je},Je++)},en=function(t){for(var e=Object.keys(Ze),n=0;n<e.length;n++)if(Ze[e[n]].id===t)return Ze[e[n]].domId},nn=function(t,e){var n=Qe(t).className,r=Ze[n];if("string"==typeof e){var i=e.trim();i.startsWith("<<")&&i.endsWith(">>")?r.annotations.push(i.substring(2,i.length-2)):i.indexOf(")")>0?r.methods.push(i):i&&r.members.push(i)}},rn=function(t,e){t.split(",").forEach((function(t){var n=t;t[0].match(/\d/)&&(n="classid-"+n),void 0!==Ze[n]&&Ze[n].cssClasses.push(e)}))},an=function(t,e,n){var r=_t(),i=t,a=en(i);if("loose"===r.securityLevel&&void 0!==e&&void 0!==Ze[i]){var o=[];if("string"==typeof n){o=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var s=0;s<o.length;s++){var c=o[s].trim();'"'===c.charAt(0)&&'"'===c.charAt(c.length-1)&&(c=c.substr(1,c.length-2)),o[s]=c}}0===o.length&&o.push(a),Ke.push((function(){var t=document.querySelector('[id="'.concat(a,'"]'));null!==t&&t.addEventListener("click",(function(){H.runFunc.apply(H,[e].concat(qe(o)))}),!1)}))}},on={AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},sn=function(t){var e=Object(d.select)(".mermaidTooltip");null===(e._groups||e)[0][0]&&(e=Object(d.select)("body").append("div").attr("class","mermaidTooltip").style("opacity",0)),Object(d.select)(t).select("svg").selectAll("g.node").on("mouseover",(function(){var t=Object(d.select)(this);if(null!==t.attr("title")){var n=this.getBoundingClientRect();e.transition().duration(200).style("opacity",".9"),e.html(t.attr("title")).style("left",window.scrollX+n.left+(n.right-n.left)/2+"px").style("top",window.scrollY+n.top-14+document.body.scrollTop+"px"),t.classed("hover",!0)}})).on("mouseout",(function(){e.transition().duration(500).style("opacity",0),Object(d.select)(this).classed("hover",!1)}))};Ke.push(sn);var cn={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().class},addClass:tn,bindFunctions:function(t){Ke.forEach((function(e){e(t)}))},clear:function(){Xe=[],Ze={},(Ke=[]).push(sn)},getClass:function(t){return Ze[t]},getClasses:function(){return Ze},addAnnotation:function(t,e){var n=Qe(t).className;Ze[n].annotations.push(e)},getRelations:function(){return Xe},addRelation:function(t){c.debug("Adding relation: "+JSON.stringify(t)),tn(t.id1),tn(t.id2),t.id1=Qe(t.id1).className,t.id2=Qe(t.id2).className,Xe.push(t)},addMember:nn,addMembers:function(t,e){Array.isArray(e)&&(e.reverse(),e.forEach((function(e){return nn(t,e)})))},cleanupLabel:function(t){return":"===t.substring(0,1)?t.substr(1).trim():t.trim()},lineType:{LINE:0,DOTTED_LINE:1},relationType:on,setClickEvent:function(t,e,n){t.split(",").forEach((function(t){an(t,e,n),Ze[t].haveCallback=!0})),rn(t,"clickable")},setCssClass:rn,setLink:function(t,e,n){var r=_t();t.split(",").forEach((function(t){var i=t;t[0].match(/\d/)&&(i="classid-"+i),void 0!==Ze[i]&&(Ze[i].link=H.formatUrl(e,r),Ze[i].linkTarget="string"==typeof n?n:"_blank")})),rn(t,"clickable")},setTooltip:function(t,e){var n=_t();t.split(",").forEach((function(t){void 0!==e&&(Ze[t].tooltip=x.sanitizeText(e,n))}))},lookUpDomId:en},un=0,ln=function(t){var e=t.match(/(\+|-|~|#)?(\w+)(~\w+~|\[\])?\s+(\w+)/),n=t.match(/^([+|\-|~|#])?(\w+) *\( *(.*)\) *(\*|\$)? *(\w*[~|[\]]*\s*\w*~?)$/);return e&&!n?hn(e):n?fn(n):dn(t)},hn=function(t){var e="";try{e=(t[1]?t[1].trim():"")+(t[2]?t[2].trim():"")+(t[3]?gn(t[3].trim()):"")+" "+(t[4]?t[4].trim():"")}catch(n){e=t}return{displayText:e,cssStyle:""}},fn=function(t){var e="",n="";try{var r=t[1]?t[1].trim():"",i=t[2]?t[2].trim():"",a=t[3]?gn(t[3].trim()):"",o=t[4]?t[4].trim():"";n=r+i+"("+a+")"+(t[5]?" : "+gn(t[5]).trim():""),e=yn(o)}catch(e){n=t}return{displayText:n,cssStyle:e}},dn=function(t){var e="",n="",r="",i=t.indexOf("("),a=t.indexOf(")");if(i>1&&a>i&&a<=t.length){var o="",s="",c=t.substring(0,1);c.match(/\w/)?s=t.substring(0,i).trim():(c.match(/\+|-|~|#/)&&(o=c),s=t.substring(1,i).trim());var u=t.substring(i+1,a),l=t.substring(a+1,1);n=yn(l),e=o+s+"("+gn(u.trim())+")",a<"".length&&""!==(r=t.substring(a+2).trim())&&(r=" : "+gn(r))}else e=gn(t);return{displayText:e,cssStyle:n}},pn=function(t,e,n,r){var i=ln(e),a=t.append("tspan").attr("x",r.padding).text(i.displayText);""!==i.cssStyle&&a.attr("style",i.cssStyle),n||a.attr("dy",r.textHeight)},gn=function t(e){var n=e;return-1!=e.indexOf("~")?t(n=(n=n.replace("~","<")).replace("~",">")):n},yn=function(t){switch(t){case"*":return"font-style:italic;";case"$":return"text-decoration:underline;";default:return""}},vn=function(t,e,n){c.info("Rendering class "+e);var r,i=e.id,a={id:i,label:e.id,width:0,height:0},o=t.append("g").attr("id",en(i)).attr("class","classGroup");r=e.link?o.append("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget).append("text").attr("y",n.textHeight+n.padding).attr("x",0):o.append("text").attr("y",n.textHeight+n.padding).attr("x",0);var s=!0;e.annotations.forEach((function(t){var e=r.append("tspan").text("«"+t+"»");s||e.attr("dy",n.textHeight),s=!1}));var u=e.id;void 0!==e.type&&""!==e.type&&(u+="<"+e.type+">");var l=r.append("tspan").text(u).attr("class","title");s||l.attr("dy",n.textHeight);var h=r.node().getBBox().height,f=o.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin/2).attr("y2",n.padding+h+n.dividerMargin/2),d=o.append("text").attr("x",n.padding).attr("y",h+n.dividerMargin+n.textHeight).attr("fill","white").attr("class","classText");s=!0,e.members.forEach((function(t){pn(d,t,s,n),s=!1}));var p=d.node().getBBox(),g=o.append("line").attr("x1",0).attr("y1",n.padding+h+n.dividerMargin+p.height).attr("y2",n.padding+h+n.dividerMargin+p.height),y=o.append("text").attr("x",n.padding).attr("y",h+2*n.dividerMargin+p.height+n.textHeight).attr("fill","white").attr("class","classText");s=!0,e.methods.forEach((function(t){pn(y,t,s,n),s=!1}));var v=o.node().getBBox(),m=" ";e.cssClasses.length>0&&(m+=e.cssClasses.join(" "));var b=o.insert("rect",":first-child").attr("x",0).attr("y",0).attr("width",v.width+2*n.padding).attr("height",v.height+n.padding+.5*n.dividerMargin).attr("class",m).node().getBBox().width;return r.node().childNodes.forEach((function(t){t.setAttribute("x",(b-t.getBBox().width)/2)})),e.tooltip&&r.insert("title").text(e.tooltip),f.attr("x2",b),g.attr("x2",b),a.width=b,a.height=v.height+n.padding+.5*n.dividerMargin,a},mn=function(t,e,n,r){var i=function(t){switch(t){case on.AGGREGATION:return"aggregation";case on.EXTENSION:return"extension";case on.COMPOSITION:return"composition";case on.DEPENDENCY:return"dependency"}};e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var a,o,s=e.points,u=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis),l=t.append("path").attr("d",u(s)).attr("id","edge"+un).attr("class","relation"),h="";r.arrowMarkerAbsolute&&(h=(h=(h=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),1==n.relation.lineType&&l.attr("class","relation dashed-line"),"none"!==n.relation.type1&&l.attr("marker-start","url("+h+"#"+i(n.relation.type1)+"Start)"),"none"!==n.relation.type2&&l.attr("marker-end","url("+h+"#"+i(n.relation.type2)+"End)");var f,p,g,y,v=e.points.length,m=H.calcLabelPosition(e.points);if(a=m.x,o=m.y,v%2!=0&&v>1){var b=H.calcCardinalityPosition("none"!==n.relation.type1,e.points,e.points[0]),x=H.calcCardinalityPosition("none"!==n.relation.type2,e.points,e.points[v-1]);c.debug("cardinality_1_point "+JSON.stringify(b)),c.debug("cardinality_2_point "+JSON.stringify(x)),f=b.x,p=b.y,g=x.x,y=x.y}if(void 0!==n.title){var _=t.append("g").attr("class","classLabel"),k=_.append("text").attr("class","label").attr("x",a).attr("y",o).attr("fill","red").attr("text-anchor","middle").text(n.title);window.label=k;var w=k.node().getBBox();_.insert("rect",":first-child").attr("class","box").attr("x",w.x-r.padding/2).attr("y",w.y-r.padding/2).attr("width",w.width+r.padding).attr("height",w.height+r.padding)}(c.info("Rendering relation "+JSON.stringify(n)),void 0!==n.relationTitle1&&"none"!==n.relationTitle1)&&t.append("g").attr("class","cardinality").append("text").attr("class","type1").attr("x",f).attr("y",p).attr("fill","black").attr("font-size","6").text(n.relationTitle1);void 0!==n.relationTitle2&&"none"!==n.relationTitle2&&t.append("g").attr("class","cardinality").append("text").attr("class","type2").attr("x",g).attr("y",y).attr("fill","black").attr("font-size","6").text(n.relationTitle2);un++},bn=function(t,e,n){var r=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),i=70,a=10;"LR"===n&&(i=10,a=70);var o=r.append("rect").style("stroke","black").style("fill","black").attr("x",-1*i/2).attr("y",-1*a/2).attr("width",i).attr("height",a).attr("class","fork-join");return Ae(e,o),e.height=e.height+e.padding/2,e.width=e.width+e.padding/2,e.intersect=function(t){return Ve.rect(e,t)},r},xn={question:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding+(i.height+e.padding),o=[{x:a/2,y:0},{x:a,y:-a/2},{x:a/2,y:-a},{x:0,y:-a/2}];c.info("Question main (Circle)");var s=Se(r,a,a,o);return Ae(e,s),e.intersect=function(t){return c.warn("Intersect called"),Ve.polygon(e,o,t)},r},rect:function(t,e){var n=Ce(t,e,"node "+e.classes,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding;c.trace("Classes = ",e.classes);var o=r.insert("rect",":first-child");return o.attr("class","basic label-container").attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",-i.width/2-a).attr("y",-i.height/2-a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),Ae(e,o),e.intersect=function(t){return Ve.rect(e,t)},r},rectWithTitle:function(t,e){var n;n=e.classes?"node "+e.classes:"node default";var r=t.insert("g").attr("class",n).attr("id",e.domId||e.id),i=r.insert("rect",":first-child"),a=r.insert("line"),o=r.insert("g").attr("class","label"),s=e.labelText.flat();c.info("Label text",s[0]);var u,l=o.node().appendChild(Te(s[0],e.labelStyle,!0,!0));if(_t().flowchart.htmlLabels){var h=l.children[0],f=Object(d.select)(l);u=h.getBoundingClientRect(),f.attr("width",u.width),f.attr("height",u.height)}c.info("Text 2",s);var p=s.slice(1,s.length),g=l.getBBox(),y=o.node().appendChild(Te(p.join("<br/>"),e.labelStyle,!0,!0));if(_t().flowchart.htmlLabels){var v=y.children[0],m=Object(d.select)(y);u=v.getBoundingClientRect(),m.attr("width",u.width),m.attr("height",u.height)}var b=e.padding/2;return Object(d.select)(y).attr("transform","translate( "+(u.width>g.width?0:(g.width-u.width)/2)+", "+(g.height+b+5)+")"),Object(d.select)(l).attr("transform","translate( "+(u.width<g.width?0:-(g.width-u.width)/2)+", 0)"),u=o.node().getBBox(),o.attr("transform","translate("+-u.width/2+", "+(-u.height/2-b+3)+")"),i.attr("class","outer title-state").attr("x",-u.width/2-b).attr("y",-u.height/2-b).attr("width",u.width+e.padding).attr("height",u.height+e.padding),a.attr("class","divider").attr("x1",-u.width/2-b).attr("x2",u.width/2+b).attr("y1",-u.height/2-b+g.height+b).attr("y2",-u.height/2-b+g.height+b),Ae(e,i),e.intersect=function(t){return Ve.rect(e,t)},r},circle:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=n.halfPadding,o=r.insert("circle",":first-child");return o.attr("rx",e.rx).attr("ry",e.ry).attr("r",i.width/2+a).attr("width",i.width+e.padding).attr("height",i.height+e.padding),c.info("Circle main"),Ae(e,o),e.intersect=function(t){return c.info("Circle intersect",e,i.width/2+a,t),Ve.circle(e,i.width/2+a,t)},r},stadium:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=i.width+a/4+e.padding,s=r.insert("rect",":first-child").attr("rx",a/2).attr("ry",a/2).attr("x",-o/2).attr("y",-a/2).attr("width",o).attr("height",a);return Ae(e,s),e.intersect=function(t){return Ve.rect(e,t)},r},hexagon:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.height+e.padding,o=a/4,s=i.width+2*o+e.padding,c=Se(r,s,a,[{x:o,y:0},{x:s-o,y:0},{x:s,y:-a/2},{x:s-o,y:-a},{x:o,y:-a},{x:0,y:-a/2}]);return Ae(e,c),e.intersect=function(t){return Ve.polygon(e,t)},r},rect_left_inv_arrow:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:-o/2,y:0},{x:a,y:0},{x:a,y:-o},{x:-o/2,y:-o},{x:0,y:-o/2}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},lean_right:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:-2*o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},lean_left:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:2*o/6,y:0},{x:a+o/6,y:0},{x:a-2*o/6,y:-o},{x:-o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},trapezoid:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:-2*o/6,y:0},{x:a+2*o/6,y:0},{x:a-o/6,y:-o},{x:o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},inv_trapezoid:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:o/6,y:0},{x:a-o/6,y:0},{x:a+2*o/6,y:-o},{x:-2*o/6,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},rect_right_inv_arrow:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:0,y:0},{x:a+o/2,y:0},{x:a,y:-o/2},{x:a+o/2,y:-o},{x:0,y:-o}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},cylinder:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=a/2,s=o/(2.5+a/50),c=i.height+s+e.padding,u="M 0,"+s+" a "+o+","+s+" 0,0,0 "+a+" 0 a "+o+","+s+" 0,0,0 "+-a+" 0 l 0,"+c+" a "+o+","+s+" 0,0,0 "+a+" 0 l 0,"+-c,l=r.attr("label-offset-y",s).insert("path",":first-child").attr("d",u).attr("transform","translate("+-a/2+","+-(c/2+s)+")");return Ae(e,l),e.intersect=function(t){var n=Ve.rect(e,t),r=n.x-e.x;if(0!=o&&(Math.abs(r)<e.width/2||Math.abs(r)==e.width/2&&Math.abs(n.y-e.y)>e.height/2-s)){var i=s*s*(1-r*r/(o*o));0!=i&&(i=Math.sqrt(i)),i=s-i,t.y-e.y>0&&(i=-i),n.y+=i}return n},r},start:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child");return r.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),Ae(e,r),e.intersect=function(t){return Ve.circle(e,7,t)},n},end:function(t,e){var n=t.insert("g").attr("class","node default").attr("id",e.domId||e.id),r=n.insert("circle",":first-child"),i=n.insert("circle",":first-child");return i.attr("class","state-start").attr("r",7).attr("width",14).attr("height",14),r.attr("class","state-end").attr("r",5).attr("width",10).attr("height",10),Ae(e,i),e.intersect=function(t){return Ve.circle(e,7,t)},n},note:Ge,subroutine:function(t,e){var n=Ce(t,e,void 0,!0),r=n.shapeSvg,i=n.bbox,a=i.width+e.padding,o=i.height+e.padding,s=Se(r,a,o,[{x:0,y:0},{x:a,y:0},{x:a,y:-o},{x:0,y:-o},{x:0,y:0},{x:-8,y:0},{x:a+8,y:0},{x:a+8,y:-o},{x:-8,y:-o},{x:-8,y:0}]);return Ae(e,s),e.intersect=function(t){return Ve.polygon(e,t)},r},fork:bn,join:bn,class_box:function(t,e){var n,r=e.padding/2;n=e.classes?"node "+e.classes:"node default";var i=t.insert("g").attr("class",n).attr("id",e.domId||e.id),a=i.insert("rect",":first-child"),o=i.insert("line"),s=i.insert("line"),c=0,u=4,l=i.insert("g").attr("class","label"),h=0,f=e.classData.annotations&&e.classData.annotations[0],p=e.classData.annotations[0]?"«"+e.classData.annotations[0]+"»":"",g=l.node().appendChild(Te(p,e.labelStyle,!0,!0)),y=g.getBBox();if(_t().flowchart.htmlLabels){var v=g.children[0],m=Object(d.select)(g);y=v.getBoundingClientRect(),m.attr("width",y.width),m.attr("height",y.height)}e.classData.annotations[0]&&(u+=y.height+4,c+=y.width);var b=e.classData.id;void 0!==e.classData.type&&""!==e.classData.type&&(b+="<"+e.classData.type+">");var x=l.node().appendChild(Te(b,e.labelStyle,!0,!0));Object(d.select)(x).attr("class","classTitle");var _=x.getBBox();if(_t().flowchart.htmlLabels){var k=x.children[0],w=Object(d.select)(x);_=k.getBoundingClientRect(),w.attr("width",_.width),w.attr("height",_.height)}u+=_.height+4,_.width>c&&(c=_.width);var E=[];e.classData.members.forEach((function(t){var n=ln(t).displayText,r=l.node().appendChild(Te(n,e.labelStyle,!0,!0)),i=r.getBBox();if(_t().flowchart.htmlLabels){var a=r.children[0],o=Object(d.select)(r);i=a.getBoundingClientRect(),o.attr("width",i.width),o.attr("height",i.height)}i.width>c&&(c=i.width),u+=i.height+4,E.push(r)})),u+=8;var T=[];if(e.classData.methods.forEach((function(t){var n=ln(t).displayText,r=l.node().appendChild(Te(n,e.labelStyle,!0,!0)),i=r.getBBox();if(_t().flowchart.htmlLabels){var a=r.children[0],o=Object(d.select)(r);i=a.getBoundingClientRect(),o.attr("width",i.width),o.attr("height",i.height)}i.width>c&&(c=i.width),u+=i.height+4,T.push(r)})),u+=8,f){var C=(c-y.width)/2;Object(d.select)(g).attr("transform","translate( "+(-1*c/2+C)+", "+-1*u/2+")"),h=y.height+4}var A=(c-_.width)/2;return Object(d.select)(x).attr("transform","translate( "+(-1*c/2+A)+", "+(-1*u/2+h)+")"),h+=_.height+4,o.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,E.forEach((function(t){Object(d.select)(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h+4)+")"),h+=_.height+4})),h+=8,s.attr("class","divider").attr("x1",-c/2-r).attr("x2",c/2+r).attr("y1",-u/2-r+8+h).attr("y2",-u/2-r+8+h),h+=8,T.forEach((function(t){Object(d.select)(t).attr("transform","translate( "+-c/2+", "+(-1*u/2+h)+")"),h+=_.height+4})),a.attr("class","outer title-state").attr("x",-c/2-r).attr("y",-u/2-r).attr("width",c+e.padding).attr("height",u+e.padding),Ae(e,a),e.intersect=function(t){return Ve.rect(e,t)},i}},_n={},kn=function(t){var e=_n[t.id];c.trace("Transforming node",t,"translate("+(t.x-t.width/2-5)+", "+(t.y-t.height/2-5)+")");t.clusterNode?e.attr("transform","translate("+(t.x-t.width/2-8)+", "+(t.y-t.height/2-8)+")"):e.attr("transform","translate("+t.x+", "+t.y+")")},wn={rect:function(t,e){c.trace("Creating subgraph rect for ",e.id,e);var n=t.insert("g").attr("class","cluster"+(e.class?" "+e.class:"")).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=i.node().appendChild(Te(e.labelText,e.labelStyle,void 0,!0)),o=a.getBBox();if(_t().flowchart.htmlLabels){var s=a.children[0],u=Object(d.select)(a);o=s.getBoundingClientRect(),u.attr("width",o.width),u.attr("height",o.height)}var l=0*e.padding,h=l/2;c.trace("Data ",e,JSON.stringify(e)),r.attr("style",e.style).attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-h).attr("y",e.y-e.height/2-h).attr("width",e.width+l).attr("height",e.height+l),i.attr("transform","translate("+(e.x-o.width/2)+", "+(e.y-e.height/2+e.padding/3)+")");var f=r.node().getBBox();return e.width=f.width,e.height=f.height,e.intersect=function(t){return He(e,t)},n},roundedWithTitle:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=n.insert("g").attr("class","cluster-label"),a=n.append("rect"),o=i.node().appendChild(Te(e.labelText,e.labelStyle,void 0,!0)),s=o.getBBox();if(_t().flowchart.htmlLabels){var c=o.children[0],u=Object(d.select)(o);s=c.getBoundingClientRect(),u.attr("width",s.width),u.attr("height",s.height)}s=o.getBBox();var l=0*e.padding,h=l/2;r.attr("class","outer").attr("x",e.x-e.width/2-h).attr("y",e.y-e.height/2-h).attr("width",e.width+l).attr("height",e.height+l),a.attr("class","inner").attr("x",e.x-e.width/2-h).attr("y",e.y-e.height/2-h+s.height-1).attr("width",e.width+l).attr("height",e.height+l-s.height-3),i.attr("transform","translate("+(e.x-s.width/2)+", "+(e.y-e.height/2-e.padding/3+(_t().flowchart.htmlLabels?5:3))+")");var f=r.node().getBBox();return e.width=f.width,e.height=f.height,e.intersect=function(t){return He(e,t)},n},noteGroup:function(t,e){var n=t.insert("g").attr("class","note-cluster").attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("rx",e.rx).attr("ry",e.ry).attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2-a).attr("width",e.width+i).attr("height",e.height+i).attr("fill","none");var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.intersect=function(t){return He(e,t)},n},divider:function(t,e){var n=t.insert("g").attr("class",e.classes).attr("id",e.id),r=n.insert("rect",":first-child"),i=0*e.padding,a=i/2;r.attr("class","divider").attr("x",e.x-e.width/2-a).attr("y",e.y-e.height/2).attr("width",e.width+i).attr("height",e.height+i);var o=r.node().getBBox();return e.width=o.width,e.height=o.height,e.intersect=function(t){return He(e,t)},n}},En={},Tn={},Cn={},An=function(t,e){var n=t.x,r=t.y,i=Math.abs(e.x-n),a=Math.abs(e.y-r),o=t.width/2,s=t.height/2;return i>=o||a>=s},Sn=function(t,e,n){c.warn("intersection calc o:",e," i:",n,t);var r=t.x,i=t.y,a=Math.abs(r-n.x),o=t.width/2,s=n.x<e.x?o-a:o+a,u=t.height/2,l=r-o,h=r+o,f=i-u,d=i+u;if(e.x===l||e.x===h||e.y===f||e.y===d)return c.warn("calc equals on edge"),e;var p=Math.abs(e.y-n.y),g=Math.abs(e.x-n.x);if(Math.abs(i-e.y)*o>Math.abs(r-e.x)*u){var y=n.y<e.y?e.y-u-i:i-u-e.y;s=g*y/p;var v={x:n.x<e.x?n.x+g-s:n.x-s,y:n.y<e.y?n.y+p-y:n.y-y};return c.warn("topp/bott calc, Q ".concat(p,", q ").concat(y,", R ").concat(g,", r ").concat(s),v),v}var m=m=p*(s=n.x<e.x?e.x-o-r:r-o-e.x)/g;return c.warn("sides calc, Q ".concat(p,", q ").concat(m,", R ").concat(g,", r ").concat(s),{x:n.x<e.x?n.x+g-s:n.x+a-o,y:n.y<e.y?n.y+m:n.y-m}),{x:n.x<e.x?n.x+g-s:n.x+a-o,y:n.y<e.y?n.y+m:n.y-m}},Mn=function t(e,n,r,i){c.info("Graph in recursive render: XXX",G.a.json.write(n),i);var a=n.graph().rankdir;c.warn("Dir in recursive render - dir:",a);var o=e.insert("g").attr("class","root");n.nodes()?c.info("Recursive render XXX",n.nodes()):c.info("No nodes found for",n),n.edges().length>0&&c.info("Recursive edges",n.edge(n.edges()[0]));var s=o.insert("g").attr("class","clusters"),u=o.insert("g").attr("class","edgePaths"),l=o.insert("g").attr("class","edgeLabels"),h=o.insert("g").attr("class","nodes");return n.nodes().forEach((function(e){var o=n.node(e);if(void 0!==i){var s=JSON.parse(JSON.stringify(i.clusterData));c.info("Setting data for cluster XXX (",e,") ",s,i),n.setNode(i.id,s),n.parent(e)||(c.warn("Setting parent",e,i.id),n.setParent(e,i.id,s))}if(c.info("(Insert) Node XXX"+e+": "+JSON.stringify(n.node(e))),o&&o.clusterNode){c.info("Cluster identified",e,o,n.node(e));var u=t(h,o.graph,r,n.node(e));Ae(o,u),function(t,e){_n[e.id]=t}(u,o),c.warn("Recursive render complete",u,o)}else n.children(e).length>0?(c.info("Cluster - the non recursive path XXX",e,o.id,o,n),c.info(Le(o.id,n)),Me[o.id]={id:Le(o.id,n),node:o}):(c.info("Node - the non recursive path",e,o.id,o),function(t,e,n){var r,i;e.link?(r=t.insert("svg:a").attr("xlink:href",e.link).attr("target",e.linkTarget||"_blank"),i=xn[e.shape](r,e,n)):r=i=xn[e.shape](t,e,n),e.tooltip&&i.attr("title",e.tooltip),e.class&&i.attr("class","node default "+e.class),_n[e.id]=r,e.haveCallback&&_n[e.id].attr("class",_n[e.id].attr("class")+" clickable")}(h,n.node(e),a))})),n.edges().forEach((function(t){var e=n.edge(t.v,t.w,t.name);c.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t)),c.info("Edge "+t.v+" -> "+t.w+": ",t," ",JSON.stringify(n.edge(t))),c.info("Fix",Me,"ids:",t.v,t.w,"Translateing: ",Me[t.v],Me[t.w]),function(t,e){var n=Te(e.label,e.labelStyle),r=t.insert("g").attr("class","edgeLabel"),i=r.insert("g").attr("class","label");i.node().appendChild(n);var a=n.getBBox();if(_t().flowchart.htmlLabels){var o=n.children[0],s=Object(d.select)(n);a=o.getBoundingClientRect(),s.attr("width",a.width),s.attr("height",a.height)}if(i.attr("transform","translate("+-a.width/2+", "+-a.height/2+")"),Tn[e.id]=r,e.width=a.width,e.height=a.height,e.startLabelLeft){var c=Te(e.startLabelLeft,e.labelStyle),u=t.insert("g").attr("class","edgeTerminals"),l=u.insert("g").attr("class","inner");l.node().appendChild(c);var h=c.getBBox();l.attr("transform","translate("+-h.width/2+", "+-h.height/2+")"),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].startLeft=u}if(e.startLabelRight){var f=Te(e.startLabelRight,e.labelStyle),p=t.insert("g").attr("class","edgeTerminals"),g=p.insert("g").attr("class","inner");p.node().appendChild(f),g.node().appendChild(f);var y=f.getBBox();g.attr("transform","translate("+-y.width/2+", "+-y.height/2+")"),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].startRight=p}if(e.endLabelLeft){var v=Te(e.endLabelLeft,e.labelStyle),m=t.insert("g").attr("class","edgeTerminals"),b=m.insert("g").attr("class","inner");b.node().appendChild(v);var x=v.getBBox();b.attr("transform","translate("+-x.width/2+", "+-x.height/2+")"),m.node().appendChild(v),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].endLeft=m}if(e.endLabelRight){var _=Te(e.endLabelRight,e.labelStyle),k=t.insert("g").attr("class","edgeTerminals"),w=k.insert("g").attr("class","inner");w.node().appendChild(_);var E=_.getBBox();w.attr("transform","translate("+-E.width/2+", "+-E.height/2+")"),k.node().appendChild(_),Cn[e.id]||(Cn[e.id]={}),Cn[e.id].endRight=k}}(l,e)})),n.edges().forEach((function(t){c.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(t))})),c.info("#############################################"),c.info("### Layout ###"),c.info("#############################################"),c.info(n),ke.a.layout(n),c.info("Graph after layout:",G.a.json.write(n)),je(n).forEach((function(t){var e=n.node(t);c.info("Position "+t+": "+JSON.stringify(n.node(t))),c.info("Position "+t+": ("+e.x,","+e.y,") width: ",e.width," height: ",e.height),e&&e.clusterNode?kn(e):n.children(t).length>0?(!function(t,e){c.trace("Inserting cluster");var n=e.shape||"rect";En[e.id]=wn[n](t,e)}(s,e),Me[e.id].node=e):kn(e)})),n.edges().forEach((function(t){var e=n.edge(t);c.info("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(e),e);var i=function(t,e,n,r,i,a){var o=n.points,s=!1,u=a.node(e.v),l=a.node(e.w);if(l.intersect&&u.intersect&&((o=o.slice(1,n.points.length-1)).unshift(u.intersect(o[0])),c.info("Last point",o[o.length-1],l,l.intersect(o[o.length-1])),o.push(l.intersect(o[o.length-1]))),n.toCluster){var h;c.trace("edge",n),c.trace("to cluster",r[n.toCluster]),o=[];var f=!1;n.points.forEach((function(t){var e=r[n.toCluster].node;if(An(e,t)||f)f||o.push(t);else{c.trace("inside",n.toCluster,t,h);var i=Sn(e,h,t),a=!1;o.forEach((function(t){a=a||t.x===i.x&&t.y===i.y})),o.find((function(t){return t.x===i.x&&t.y===i.y}))?c.warn("no intersect",i,o):o.push(i),f=!0}h=t})),s=!0}if(n.fromCluster){c.trace("edge",n),c.warn("from cluster",r[n.fromCluster]);for(var p,g=[],y=!1,v=o.length-1;v>=0;v--){var m=o[v],b=r[n.fromCluster].node;if(An(b,m)||y)c.trace("Outside point",m),y||g.unshift(m);else{c.warn("inside",n.fromCluster,m,b);var x=Sn(b,p,m);g.unshift(x),y=!0}p=m}o=g,s=!0}var _,k=o.filter((function(t){return!Number.isNaN(t.y)})),w=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis);switch(n.thickness){case"normal":_="edge-thickness-normal";break;case"thick":_="edge-thickness-thick";break;default:_=""}switch(n.pattern){case"solid":_+=" edge-pattern-solid";break;case"dotted":_+=" edge-pattern-dotted";break;case"dashed":_+=" edge-pattern-dashed"}var E=t.append("path").attr("d",w(k)).attr("id",n.id).attr("class"," "+_+(n.classes?" "+n.classes:"")).attr("style",n.style),T="";switch(_t().state.arrowMarkerAbsolute&&(T=(T=(T=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),c.info("arrowTypeStart",n.arrowTypeStart),c.info("arrowTypeEnd",n.arrowTypeEnd),n.arrowTypeStart){case"arrow_cross":E.attr("marker-start","url("+T+"#"+i+"-crossStart)");break;case"arrow_point":E.attr("marker-start","url("+T+"#"+i+"-pointStart)");break;case"arrow_barb":E.attr("marker-start","url("+T+"#"+i+"-barbStart)");break;case"arrow_circle":E.attr("marker-start","url("+T+"#"+i+"-circleStart)");break;case"aggregation":E.attr("marker-start","url("+T+"#"+i+"-aggregationStart)");break;case"extension":E.attr("marker-start","url("+T+"#"+i+"-extensionStart)");break;case"composition":E.attr("marker-start","url("+T+"#"+i+"-compositionStart)");break;case"dependency":E.attr("marker-start","url("+T+"#"+i+"-dependencyStart)")}switch(n.arrowTypeEnd){case"arrow_cross":E.attr("marker-end","url("+T+"#"+i+"-crossEnd)");break;case"arrow_point":E.attr("marker-end","url("+T+"#"+i+"-pointEnd)");break;case"arrow_barb":E.attr("marker-end","url("+T+"#"+i+"-barbEnd)");break;case"arrow_circle":E.attr("marker-end","url("+T+"#"+i+"-circleEnd)");break;case"aggregation":E.attr("marker-end","url("+T+"#"+i+"-aggregationEnd)");break;case"extension":E.attr("marker-end","url("+T+"#"+i+"-extensionEnd)");break;case"composition":E.attr("marker-end","url("+T+"#"+i+"-compositionEnd)");break;case"dependency":E.attr("marker-end","url("+T+"#"+i+"-dependencyEnd)")}var C={};return s&&(C.updatedPath=o),C.originalPath=n.points,C}(u,t,e,Me,r,n);!function(t,e){c.info("Moving label",t.id,t.label,Tn[t.id]);var n=e.updatedPath?e.updatedPath:e.originalPath;if(t.label){var r=Tn[t.id],i=t.x,a=t.y;if(n){var o=H.calcLabelPosition(n);c.info("Moving label from (",i,",",a,") to (",o.x,",",o.y,")")}r.attr("transform","translate("+i+", "+a+")")}if(t.startLabelLeft){var s=Cn[t.id].startLeft,u=t.x,l=t.y;if(n){var h=H.calcTerminalLabelPosition(0,"start_left",n);u=h.x,l=h.y}s.attr("transform","translate("+u+", "+l+")")}if(t.startLabelRight){var f=Cn[t.id].startRight,d=t.x,p=t.y;if(n){var g=H.calcTerminalLabelPosition(0,"start_right",n);d=g.x,p=g.y}f.attr("transform","translate("+d+", "+p+")")}if(t.endLabelLeft){var y=Cn[t.id].endLeft,v=t.x,m=t.y;if(n){var b=H.calcTerminalLabelPosition(0,"end_left",n);v=b.x,m=b.y}y.attr("transform","translate("+v+", "+m+")")}if(t.endLabelRight){var x=Cn[t.id].endRight,_=t.x,k=t.y;if(n){var w=H.calcTerminalLabelPosition(0,"end_right",n);_=w.x,k=w.y}x.attr("transform","translate("+_+", "+k+")")}}(e,i)})),o},On=function(t,e,n,r,i){Ee(t,n,r,i),_n={},Tn={},Cn={},En={},Oe={},De={},Me={},c.warn("Graph at first:",G.a.json.write(e)),Ie(e),c.warn("Graph after:",G.a.json.write(e)),Mn(t,e,r)},Dn={},Nn=function(t,e,n){var r=Object(d.select)('[id="'.concat(n,'"]'));Object.keys(t).forEach((function(n){var i=t[n],a="default";i.classes.length>0&&(a=i.classes.join(" "));var o,s=B(i.styles),u=void 0!==i.text?i.text:i.id;if(_t().flowchart.htmlLabels){var l={label:u.replace(/fa[lrsb]?:fa-[\w-]+/g,(function(t){return"<i class='".concat(t.replace(":"," "),"'></i>")}))};(o=ee()(r,l).node()).parentNode.removeChild(o)}else{var h=document.createElementNS("http://www.w3.org/2000/svg","text");h.setAttribute("style",s.labelStyle.replace("color:","fill:"));for(var f=u.split(x.lineBreakRegex),d=0;d<f.length;d++){var p=document.createElementNS("http://www.w3.org/2000/svg","tspan");p.setAttributeNS("http://www.w3.org/XML/1998/namespace","xml:space","preserve"),p.setAttribute("dy","1em"),p.setAttribute("x","1"),p.textContent=f[d],h.appendChild(p)}o=h}var g=0,y="";switch(i.type){case"round":g=5,y="rect";break;case"square":y="rect";break;case"diamond":y="question";break;case"hexagon":y="hexagon";break;case"odd":y="rect_left_inv_arrow";break;case"lean_right":y="lean_right";break;case"lean_left":y="lean_left";break;case"trapezoid":y="trapezoid";break;case"inv_trapezoid":y="inv_trapezoid";break;case"odd_right":y="rect_left_inv_arrow";break;case"circle":y="circle";break;case"ellipse":y="ellipse";break;case"stadium":y="stadium";break;case"subroutine":y="subroutine";break;case"cylinder":y="cylinder";break;case"group":y="rect";break;default:y="rect"}e.setNode(i.id,{labelStyle:s.labelStyle,shape:y,labelText:u,rx:g,ry:g,class:a,style:s.style,id:i.id,link:i.link,linkTarget:i.linkTarget,tooltip:Xt.getTooltip(i.id)||"",domId:Xt.lookUpDomId(i.id),haveCallback:i.haveCallback,width:"group"===i.type?500:void 0,type:i.type,padding:_t().flowchart.padding}),c.info("setNode",{labelStyle:s.labelStyle,shape:y,labelText:u,rx:g,ry:g,class:a,style:s.style,id:i.id,domId:Xt.lookUpDomId(i.id),width:"group"===i.type?500:void 0,type:i.type,padding:_t().flowchart.padding})}))},Bn=function(t,e){var n,r,i=0;if(void 0!==t.defaultStyle){var a=B(t.defaultStyle);n=a.style,r=a.labelStyle}t.forEach((function(a){i++;var o="L-"+a.start+"-"+a.end,s="LS-"+a.start,c="LE-"+a.end,u={style:"",labelStyle:""};switch(u.minlen=a.length||1,"arrow_open"===a.type?u.arrowhead="none":u.arrowhead="normal",u.arrowTypeStart="arrow_open",u.arrowTypeEnd="arrow_open",a.type){case"double_arrow_cross":u.arrowTypeStart="arrow_cross";case"arrow_cross":u.arrowTypeEnd="arrow_cross";break;case"double_arrow_point":u.arrowTypeStart="arrow_point";case"arrow_point":u.arrowTypeEnd="arrow_point";break;case"double_arrow_circle":u.arrowTypeStart="arrow_circle";case"arrow_circle":u.arrowTypeEnd="arrow_circle"}var l="",h="";switch(a.stroke){case"normal":l="fill:none;",void 0!==n&&(l=n),void 0!==r&&(h=r),u.thickness="normal",u.pattern="solid";break;case"dotted":u.thickness="normal",u.pattern="dotted",u.style="fill:none;stroke-width:2px;stroke-dasharray:3;";break;case"thick":u.thickness="thick",u.pattern="solid",u.style="stroke-width: 3.5px;fill:none;"}if(void 0!==a.style){var f=B(a.style);l=f.style,h=f.labelStyle}u.style=u.style+=l,u.labelStyle=u.labelStyle+=h,void 0!==a.interpolate?u.curve=D(a.interpolate,d.curveLinear):void 0!==t.defaultInterpolate?u.curve=D(t.defaultInterpolate,d.curveLinear):u.curve=D(Dn.curve,d.curveLinear),void 0===a.text?void 0!==a.style&&(u.arrowheadStyle="fill: #333"):(u.arrowheadStyle="fill: #333",u.labelpos="c"),u.labelType="text",u.label=a.text.replace(x.lineBreakRegex,"\n"),void 0===a.style&&(u.style=u.style||"stroke: #333; stroke-width: 1.5px;fill:none;"),u.labelStyle=u.labelStyle.replace("color:","fill:"),u.id=o,u.classes="flowchart-link "+s+" "+c,e.setEdge(a.start,a.end,u,i)}))},Ln=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)Dn[e[n]]=t[e[n]]},Pn=function(t,e){c.info("Drawing flowchart"),Xt.clear(),Xt.setGen("gen-2");var n=Jt.a.parser;n.yy=Xt,n.parse(t);var r=Xt.getDirection();void 0===r&&(r="TD");var i,a=_t().flowchart,o=a.nodeSpacing||50,s=a.rankSpacing||50,u=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:r,nodesep:o,ranksep:s,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),l=Xt.getSubGraphs();c.info("Subgraphs - ",l);for(var h=l.length-1;h>=0;h--)i=l[h],c.info("Subgraph - ",i),Xt.addVertex(i.id,i.title,"group",void 0,i.classes);var f=Xt.getVertices(),p=Xt.getEdges();c.info(p);var g=0;for(g=l.length-1;g>=0;g--){i=l[g],Object(d.selectAll)("cluster").append("text");for(var y=0;y<i.nodes.length;y++)c.info("Setting up subgraphs",i.nodes[y],i.id),u.setParent(i.nodes[y],i.id)}Nn(f,u,e),Bn(p,u);var v=Object(d.select)('[id="'.concat(e,'"]'));v.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var m=Object(d.select)("#"+e+" g");On(m,u,["point","circle","cross"],"flowchart",e);var b=a.diagramPadding,x=v.node().getBBox(),_=x.width+2*b,k=x.height+2*b;if(c.debug("new ViewBox 0 0 ".concat(_," ").concat(k),"translate(".concat(b-u._label.marginx,", ").concat(b-u._label.marginy,")")),W(v,k,_,a.useMaxWidth),v.attr("viewBox","0 0 ".concat(_," ").concat(k)),v.select("g").attr("transform","translate(".concat(b-u._label.marginx,", ").concat(b-x.y,")")),Xt.indexNodes("subGraph"+g),!a.htmlLabels)for(var w=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),E=0;E<w.length;E++){var T=w[E],C=T.getBBox(),A=document.createElementNS("http://www.w3.org/2000/svg","rect");A.setAttribute("rx",0),A.setAttribute("ry",0),A.setAttribute("width",C.width),A.setAttribute("height",C.height),T.insertBefore(A,T.firstChild)}Object.keys(f).forEach((function(t){var n=f[t];if(n.link){var r=Object(d.select)("#"+e+' [id="'+t+'"]');if(r){var i=document.createElementNS("http://www.w3.org/2000/svg","a");i.setAttributeNS("http://www.w3.org/2000/svg","class",n.classes.join(" ")),i.setAttributeNS("http://www.w3.org/2000/svg","href",n.link),i.setAttributeNS("http://www.w3.org/2000/svg","rel","noopener"),n.linkTarget&&i.setAttributeNS("http://www.w3.org/2000/svg","target",n.linkTarget);var a=r.insert((function(){return i}),":first-child"),o=r.select(".label-container");o&&a.append((function(){return o.node()}));var s=r.select(".label");s&&a.append((function(){return s.node()}))}}}))},In=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},Fn=function(t,e){var n=0,r=0,i=e.text.split(x.lineBreakRegex),a=[],o=0,s=function(){return e.y};if(void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0)switch(e.valign){case"top":case"start":s=function(){return Math.round(e.y+e.textMargin)};break;case"middle":case"center":s=function(){return Math.round(e.y+(n+r+e.textMargin)/2)};break;case"bottom":case"end":s=function(){return Math.round(e.y+(n+r+2*e.textMargin)-e.textMargin)}}if(void 0!==e.anchor&&void 0!==e.textMargin&&void 0!==e.width)switch(e.anchor){case"left":case"start":e.x=Math.round(e.x+e.textMargin),e.anchor="start",e.dominantBaseline="text-after-edge",e.alignmentBaseline="middle";break;case"middle":case"center":e.x=Math.round(e.x+e.width/2),e.anchor="middle",e.dominantBaseline="middle",e.alignmentBaseline="middle";break;case"right":case"end":e.x=Math.round(e.x+e.width-e.textMargin),e.anchor="end",e.dominantBaseline="text-before-edge",e.alignmentBaseline="middle"}for(var c=0;c<i.length;c++){var u=i[c];void 0!==e.textMargin&&0===e.textMargin&&void 0!==e.fontSize&&(o=c*e.fontSize);var l=t.append("text");if(l.attr("x",e.x),l.attr("y",s()),void 0!==e.anchor&&l.attr("text-anchor",e.anchor).attr("dominant-baseline",e.dominantBaseline).attr("alignment-baseline",e.alignmentBaseline),void 0!==e.fontFamily&&l.style("font-family",e.fontFamily),void 0!==e.fontSize&&l.style("font-size",e.fontSize),void 0!==e.fontWeight&&l.style("font-weight",e.fontWeight),void 0!==e.fill&&l.attr("fill",e.fill),void 0!==e.class&&l.attr("class",e.class),void 0!==e.dy?l.attr("dy",e.dy):0!==o&&l.attr("dy",o),e.tspan){var h=l.append("tspan");h.attr("x",e.x),void 0!==e.fill&&h.attr("fill",e.fill),h.text(u)}else l.text(u);void 0!==e.valign&&void 0!==e.textMargin&&e.textMargin>0&&(r+=(l._groups||l)[0][0].getBBox().height,n=r),a.push(l)}return a},jn=function(t,e){var n,r,i,a,o,s=t.append("polygon");return s.attr("points",(n=e.x,r=e.y,i=e.width,a=e.height,n+","+r+" "+(n+i)+","+r+" "+(n+i)+","+(r+a-(o=7))+" "+(n+i-1.2*o)+","+(r+a)+" "+n+","+(r+a))),s.attr("class","labelBox"),e.y=e.y+e.height/2,Fn(t,e),s},Rn=-1,Yn=function(){return{x:0,y:0,fill:void 0,anchor:void 0,style:"#666",width:void 0,height:void 0,textMargin:0,rx:0,ry:0,tspan:!0,valign:void 0}},zn=function(){return{x:0,y:0,fill:"#EDF2AE",stroke:"#666",width:100,anchor:"start",height:100,rx:0,ry:0}},Un=function(){function t(t,e,n,i,a,o,s){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c){for(var u=c.actorFontSize,l=c.actorFontFamily,h=c.actorFontWeight,f=t.split(x.lineBreakRegex),d=0;d<f.length;d++){var p=d*u-u*(f.length-1)/2,g=e.append("text").attr("x",n+a/2).attr("y",i).style("text-anchor","middle").style("font-size",u).style("font-weight",h).style("font-family",l);g.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),g.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(g,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).append("div").style("display","table").style("height","100%").style("width","100%");h.append("div").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)e.hasOwnProperty(n)&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}(),$n={drawRect:In,drawText:Fn,drawLabel:jn,drawActor:function(t,e,n){var r=e.x+e.width/2,i=t.append("g");0===e.y&&(Rn++,i.append("line").attr("id","actor"+Rn).attr("x1",r).attr("y1",5).attr("x2",r).attr("y2",2e3).attr("class","actor-line").attr("stroke-width","0.5px").attr("stroke","#999"));var a=zn();a.x=e.x,a.y=e.y,a.fill="#eaeaea",a.width=e.width,a.height=e.height,a.class="actor",a.rx=3,a.ry=3,In(i,a),Un(n)(e.description,i,a.x,a.y,a.width,a.height,{class:"actor"},n)},anchorElement:function(t){return t.append("g")},drawActivation:function(t,e,n,r,i){var a=zn(),o=e.anchored;a.x=e.startx,a.y=e.starty,a.class="activation"+i%3,a.width=e.stopx-e.startx,a.height=n-e.starty,In(o,a)},drawLoop:function(t,e,n,r){var i=r.boxMargin,a=r.boxTextMargin,o=r.labelBoxHeight,s=r.labelBoxWidth,c=r.messageFontFamily,u=r.messageFontSize,l=r.messageFontWeight,h=t.append("g"),f=function(t,e,n,r){return h.append("line").attr("x1",t).attr("y1",e).attr("x2",n).attr("y2",r).attr("class","loopLine")};f(e.startx,e.starty,e.stopx,e.starty),f(e.stopx,e.starty,e.stopx,e.stopy),f(e.startx,e.stopy,e.stopx,e.stopy),f(e.startx,e.starty,e.startx,e.stopy),void 0!==e.sections&&e.sections.forEach((function(t){f(e.startx,t.y,e.stopx,t.y).style("stroke-dasharray","3, 3")}));var d=Yn();d.text=n,d.x=e.startx,d.y=e.starty,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.anchor="middle",d.valign="middle",d.tspan=!1,d.width=s||50,d.height=o||20,d.textMargin=a,d.class="labelText",jn(h,d),(d=Yn()).text=e.title,d.x=e.startx+s/2+(e.stopx-e.startx)/2,d.y=e.starty+i+a,d.anchor="middle",d.valign="middle",d.textMargin=a,d.class="loopText",d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=!0;var p=Fn(h,d);return void 0!==e.sectionTitles&&e.sectionTitles.forEach((function(t,n){if(t.message){d.text=t.message,d.x=e.startx+(e.stopx-e.startx)/2,d.y=e.sections[n].y+i+a,d.class="loopText",d.anchor="middle",d.valign="middle",d.tspan=!1,d.fontFamily=c,d.fontSize=u,d.fontWeight=l,d.wrap=e.wrap,p=Fn(h,d);var r=Math.round(p.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));e.sections[n].height+=r-(i+a)}})),e.height=Math.round(e.stopy-e.starty),h},drawBackgroundRect:function(t,e){In(t,{x:e.startx,y:e.starty,width:e.stopx-e.startx,height:e.stopy-e.starty,fill:e.fill,class:"rect"}).lower()},insertArrowHead:function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",9).attr("refY",5).attr("markerUnits","userSpaceOnUse").attr("markerWidth",12).attr("markerHeight",12).attr("orient","auto").append("path").attr("d","M 0 0 L 10 5 L 0 10 z")},insertArrowFilledHead:function(t){t.append("defs").append("marker").attr("id","filled-head").attr("refX",18).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z")},insertSequenceNumber:function(t){t.append("defs").append("marker").attr("id","sequencenumber").attr("refX",15).attr("refY",15).attr("markerWidth",60).attr("markerHeight",40).attr("orient","auto").append("circle").attr("cx",15).attr("cy",15).attr("r",6)},insertArrowCrossHead:function(t){var e=t.append("defs").append("marker").attr("id","crosshead").attr("markerWidth",15).attr("markerHeight",8).attr("orient","auto").attr("refX",16).attr("refY",4);e.append("path").attr("fill","black").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 9,2 V 6 L16,4 Z"),e.append("path").attr("fill","none").attr("stroke","#000000").style("stroke-dasharray","0, 0").attr("stroke-width","1px").attr("d","M 0,1 L 6,7 M 6,1 L 0,7")},getTextObj:Yn,getNoteRect:zn},Wn=n(2),Hn=n.n(Wn),Vn=void 0,Gn={},qn=[],Xn=[],Zn="",Jn=!1,Kn=!1,Qn=!1,tr=function(t,e,n){var r=Gn[t];r&&e===r.name&&null==n||(null!=n&&null!=n.text||(n={text:e,wrap:null}),Gn[t]={name:e,description:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,prevActor:Vn},Vn&&Gn[Vn]&&(Gn[Vn].nextActor=t),Vn=t)},er=function(t){var e,n=0;for(e=0;e<qn.length;e++)qn[e].type===ir.ACTIVE_START&&qn[e].from.actor===t&&n++,qn[e].type===ir.ACTIVE_END&&qn[e].from.actor===t&&n--;return n},nr=function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{text:void 0,wrap:void 0},r=arguments.length>3?arguments[3]:void 0;if(r===ir.ACTIVE_END){var i=er(t.actor);if(i<1){var a=new Error("Trying to inactivate an inactive participant ("+t.actor+")");throw a.hash={text:"->>-",token:"->>-",line:"1",loc:{first_line:1,last_line:1,first_column:1,last_column:1},expected:["'ACTIVE_PARTICIPANT'"]},a}}return qn.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,type:r}),!0},rr=function(){return Qn},ir={SOLID:0,DOTTED:1,NOTE:2,SOLID_CROSS:3,DOTTED_CROSS:4,SOLID_OPEN:5,DOTTED_OPEN:6,LOOP_START:10,LOOP_END:11,ALT_START:12,ALT_ELSE:13,ALT_END:14,OPT_START:15,OPT_END:16,ACTIVE_START:17,ACTIVE_END:18,PAR_START:19,PAR_AND:20,PAR_END:21,RECT_START:22,RECT_END:23,SOLID_POINT:24,DOTTED_POINT:25},ar=function(t,e,n){var r={actor:t,placement:e,message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap},i=[].concat(t,t);Xn.push(r),qn.push({from:i[0],to:i[1],message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,type:ir.NOTE,placement:e})},or=function(t){Zn=t.text,Jn=void 0===t.wrap&&rr()||!!t.wrap},sr={addActor:tr,addMessage:function(t,e,n,r){qn.push({from:t,to:e,message:n.text,wrap:void 0===n.wrap&&rr()||!!n.wrap,answer:r})},addSignal:nr,autoWrap:rr,setWrap:function(t){Qn=t},enableSequenceNumbers:function(){Kn=!0},showSequenceNumbers:function(){return Kn},getMessages:function(){return qn},getActors:function(){return Gn},getActor:function(t){return Gn[t]},getActorKeys:function(){return Object.keys(Gn)},getTitle:function(){return Zn},parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().sequence},getTitleWrapped:function(){return Jn},clear:function(){Gn={},qn=[]},parseMessage:function(t){var e=t.trim(),n={text:e.replace(/^[:]?(?:no)?wrap:/,"").trim(),wrap:null!==e.match(/^[:]?wrap:/)||null===e.match(/^[:]?nowrap:/)&&void 0};return c.debug("parseMessage:",n),n},LINETYPE:ir,ARROWTYPE:{FILLED:0,OPEN:1},PLACEMENT:{LEFTOF:0,RIGHTOF:1,OVER:2},addNote:ar,setTitle:or,apply:function t(e){if(e instanceof Array)e.forEach((function(e){t(e)}));else switch(e.type){case"addActor":tr(e.actor,e.actor,e.description);break;case"activeStart":case"activeEnd":nr(e.actor,void 0,void 0,e.signalType);break;case"addNote":ar(e.actor,e.placement,e.text);break;case"addMessage":nr(e.from,e.to,e.msg,e.signalType);break;case"loopStart":nr(void 0,void 0,e.loopText,e.signalType);break;case"loopEnd":nr(void 0,void 0,void 0,e.signalType);break;case"rectStart":nr(void 0,void 0,e.color,e.signalType);break;case"rectEnd":nr(void 0,void 0,void 0,e.signalType);break;case"optStart":nr(void 0,void 0,e.optText,e.signalType);break;case"optEnd":nr(void 0,void 0,void 0,e.signalType);break;case"altStart":case"else":nr(void 0,void 0,e.altText,e.signalType);break;case"altEnd":nr(void 0,void 0,void 0,e.signalType);break;case"setTitle":or(e.text);break;case"parStart":case"and":nr(void 0,void 0,e.parText,e.signalType);break;case"parEnd":nr(void 0,void 0,void 0,e.signalType)}}};Wn.parser.yy=sr;var cr={},ur={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],activations:[],models:{getHeight:function(){return Math.max.apply(null,0===this.actors.length?[0]:this.actors.map((function(t){return t.height||0})))+(0===this.loops.length?0:this.loops.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.messages.length?0:this.messages.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))+(0===this.notes.length?0:this.notes.map((function(t){return t.height||0})).reduce((function(t,e){return t+e})))},clear:function(){this.actors=[],this.loops=[],this.messages=[],this.notes=[]},addActor:function(t){this.actors.push(t)},addLoop:function(t){this.loops.push(t)},addMessage:function(t){this.messages.push(t)},addNote:function(t){this.notes.push(t)},lastActor:function(){return this.actors[this.actors.length-1]},lastLoop:function(){return this.loops[this.loops.length-1]},lastMessage:function(){return this.messages[this.messages.length-1]},lastNote:function(){return this.notes[this.notes.length-1]},actors:[],loops:[],messages:[],notes:[]},init:function(){this.sequenceItems=[],this.activations=[],this.models.clear(),this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0,pr(Wn.parser.yy.getConfig())},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i=this,a=0;function o(o){return function(s){a++;var c=i.sequenceItems.length-a+1;i.updateVal(s,"starty",e-c*cr.boxMargin,Math.min),i.updateVal(s,"stopy",r+c*cr.boxMargin,Math.max),i.updateVal(ur.data,"startx",t-c*cr.boxMargin,Math.min),i.updateVal(ur.data,"stopx",n+c*cr.boxMargin,Math.max),"activation"!==o&&(i.updateVal(s,"startx",t-c*cr.boxMargin,Math.min),i.updateVal(s,"stopx",n+c*cr.boxMargin,Math.max),i.updateVal(ur.data,"starty",e-c*cr.boxMargin,Math.min),i.updateVal(ur.data,"stopy",r+c*cr.boxMargin,Math.max))}}this.sequenceItems.forEach(o()),this.activations.forEach(o("activation"))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(ur.data,"startx",i,Math.min),this.updateVal(ur.data,"starty",o,Math.min),this.updateVal(ur.data,"stopx",a,Math.max),this.updateVal(ur.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},newActivation:function(t,e,n){var r=n[t.from.actor],i=gr(t.from.actor).length||0,a=r.x+r.width/2+(i-1)*cr.activationWidth/2;this.activations.push({startx:a,starty:this.verticalPos+2,stopx:a+cr.activationWidth,stopy:void 0,actor:t.from.actor,anchored:$n.anchorElement(e)})},endActivation:function(t){var e=this.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);return this.activations.splice(e,1)[0]},createLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;return{startx:void 0,starty:this.verticalPos,stopx:void 0,stopy:void 0,title:t.message,wrap:t.wrap,width:t.width,height:0,fill:e}},newLoop:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{message:void 0,wrap:!1,width:void 0},e=arguments.length>1?arguments[1]:void 0;this.sequenceItems.push(this.createLoop(t,e))},endLoop:function(){return this.sequenceItems.pop()},addSectionToLoop:function(t){var e=this.sequenceItems.pop();e.sections=e.sections||[],e.sectionTitles=e.sectionTitles||[],e.sections.push({y:ur.getVerticalPos(),height:0}),e.sectionTitles.push(t),this.sequenceItems.push(e)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return{bounds:this.data,models:this.models}}},lr=function(t){return{fontFamily:t.messageFontFamily,fontSize:t.messageFontSize,fontWeight:t.messageFontWeight}},hr=function(t){return{fontFamily:t.noteFontFamily,fontSize:t.noteFontSize,fontWeight:t.noteFontWeight}},fr=function(t){return{fontFamily:t.actorFontFamily,fontSize:t.actorFontSize,fontWeight:t.actorFontWeight}},dr=function(t,e,n,r){for(var i=0,a=0,o=0;o<n.length;o++){var s=e[n[o]];s.width=s.width||cr.width,s.height=Math.max(s.height||cr.height,cr.height),s.margin=s.margin||cr.actorMargin,s.x=i+a,s.y=r,$n.drawActor(t,s,cr),ur.insert(s.x,r,s.x+s.width,s.height),i+=s.width,a+=s.margin,ur.models.addActor(s)}ur.bumpVerticalPos(cr.height)},pr=function(t){F(cr,t),t.fontFamily&&(cr.actorFontFamily=cr.noteFontFamily=cr.messageFontFamily=t.fontFamily),t.fontSize&&(cr.actorFontSize=cr.noteFontSize=cr.messageFontSize=t.fontSize),t.fontWeight&&(cr.actorFontWeight=cr.noteFontWeight=cr.messageFontWeight=t.fontWeight)},gr=function(t){return ur.activations.filter((function(e){return e.actor===t}))},yr=function(t,e){var n=e[t],r=gr(t);return[r.reduce((function(t,e){return Math.min(t,e.startx)}),n.x+n.width/2),r.reduce((function(t,e){return Math.max(t,e.stopx)}),n.x+n.width/2)]};function vr(t,e,n,r,i){ur.bumpVerticalPos(n);var a=r;if(e.id&&e.message&&t[e.id]){var o=t[e.id].width,s=lr(cr);e.message=H.wrapLabel("[".concat(e.message,"]"),o-2*cr.wrapPadding,s),e.width=o,e.wrap=!0;var u=H.calculateTextDimensions(e.message,s),l=Math.max(u.height,cr.labelBoxHeight);a=r+l,c.debug("".concat(l," - ").concat(e.message))}i(e),ur.bumpVerticalPos(a)}var mr=function(t,e){var n={};return e.forEach((function(e){if(t[e.to]&&t[e.from]){var r=t[e.to];if(e.placement===Wn.parser.yy.PLACEMENT.LEFTOF&&!r.prevActor)return;if(e.placement===Wn.parser.yy.PLACEMENT.RIGHTOF&&!r.nextActor)return;var i=void 0!==e.placement,a=!i,o=i?hr(cr):lr(cr),s=e.wrap?H.wrapLabel(e.message,cr.width-2*cr.wrapPadding,o):e.message,c=H.calculateTextDimensions(s,o).width+2*cr.wrapPadding;a&&e.from===r.nextActor?n[e.to]=Math.max(n[e.to]||0,c):a&&e.from===r.prevActor?n[e.from]=Math.max(n[e.from]||0,c):a&&e.from===e.to?(n[e.from]=Math.max(n[e.from]||0,c/2),n[e.to]=Math.max(n[e.to]||0,c/2)):e.placement===Wn.parser.yy.PLACEMENT.RIGHTOF?n[e.from]=Math.max(n[e.from]||0,c):e.placement===Wn.parser.yy.PLACEMENT.LEFTOF?n[r.prevActor]=Math.max(n[r.prevActor]||0,c):e.placement===Wn.parser.yy.PLACEMENT.OVER&&(r.prevActor&&(n[r.prevActor]=Math.max(n[r.prevActor]||0,c/2)),r.nextActor&&(n[e.from]=Math.max(n[e.from]||0,c/2)))}})),c.debug("maxMessageWidthPerActor:",n),n},br=function(t,e){var n=0;for(var r in Object.keys(t).forEach((function(e){var r=t[e];r.wrap&&(r.description=H.wrapLabel(r.description,cr.width-2*cr.wrapPadding,fr(cr)));var i=H.calculateTextDimensions(r.description,fr(cr));r.width=r.wrap?cr.width:Math.max(cr.width,i.width+2*cr.wrapPadding),r.height=r.wrap?Math.max(i.height,cr.height):cr.height,n=Math.max(n,r.height)})),e){var i=t[r];if(i){var a=t[i.nextActor];if(a){var o=e[r]+cr.actorMargin-i.width/2-a.width/2;i.margin=Math.max(o,cr.actorMargin)}}}return Math.max(n,cr.height)},xr=function(t,e){var n,r,i,a={},o=[];return t.forEach((function(t){switch(t.id=H.random({length:10}),t.type){case Wn.parser.yy.LINETYPE.LOOP_START:case Wn.parser.yy.LINETYPE.ALT_START:case Wn.parser.yy.LINETYPE.OPT_START:case Wn.parser.yy.LINETYPE.PAR_START:o.push({id:t.id,msg:t.message,from:Number.MAX_SAFE_INTEGER,to:Number.MIN_SAFE_INTEGER,width:0});break;case Wn.parser.yy.LINETYPE.ALT_ELSE:case Wn.parser.yy.LINETYPE.PAR_AND:t.message&&(n=o.pop(),a[n.id]=n,a[t.id]=n,o.push(n));break;case Wn.parser.yy.LINETYPE.LOOP_END:case Wn.parser.yy.LINETYPE.ALT_END:case Wn.parser.yy.LINETYPE.OPT_END:case Wn.parser.yy.LINETYPE.PAR_END:n=o.pop(),a[n.id]=n;break;case Wn.parser.yy.LINETYPE.ACTIVE_START:var s=e[t.from?t.from.actor:t.to.actor],u=gr(t.from?t.from.actor:t.to.actor).length,l=s.x+s.width/2+(u-1)*cr.activationWidth/2,h={startx:l,stopx:l+cr.activationWidth,actor:t.from.actor,enabled:!0};ur.activations.push(h);break;case Wn.parser.yy.LINETYPE.ACTIVE_END:var f=ur.activations.map((function(t){return t.actor})).lastIndexOf(t.from.actor);delete ur.activations.splice(f,1)[0]}void 0!==t.placement?(r=function(t,e){var n=e[t.from].x,r=e[t.to].x,i=t.wrap&&t.message,a=H.calculateTextDimensions(i?H.wrapLabel(t.message,cr.width,hr(cr)):t.message,hr(cr)),o={width:i?cr.width:Math.max(cr.width,a.width+2*cr.noteMargin),height:0,startx:e[t.from].x,stopx:0,starty:0,stopy:0,message:t.message};return t.placement===Wn.parser.yy.PLACEMENT.RIGHTOF?(o.width=i?Math.max(cr.width,a.width):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*cr.noteMargin),o.startx=n+(e[t.from].width+cr.actorMargin)/2):t.placement===Wn.parser.yy.PLACEMENT.LEFTOF?(o.width=i?Math.max(cr.width,a.width+2*cr.noteMargin):Math.max(e[t.from].width/2+e[t.to].width/2,a.width+2*cr.noteMargin),o.startx=n-o.width+(e[t.from].width-cr.actorMargin)/2):t.to===t.from?(a=H.calculateTextDimensions(i?H.wrapLabel(t.message,Math.max(cr.width,e[t.from].width),hr(cr)):t.message,hr(cr)),o.width=i?Math.max(cr.width,e[t.from].width):Math.max(e[t.from].width,cr.width,a.width+2*cr.noteMargin),o.startx=n+(e[t.from].width-o.width)/2):(o.width=Math.abs(n+e[t.from].width/2-(r+e[t.to].width/2))+cr.actorMargin,o.startx=n<r?n+e[t.from].width/2-cr.actorMargin/2:r+e[t.to].width/2-cr.actorMargin/2),i&&(o.message=H.wrapLabel(t.message,o.width-2*cr.wrapPadding,hr(cr))),c.debug("NM:[".concat(o.startx,",").concat(o.stopx,",").concat(o.starty,",").concat(o.stopy,":").concat(o.width,",").concat(o.height,"=").concat(t.message,"]")),o}(t,e),t.noteModel=r,o.forEach((function(t){(n=t).from=Math.min(n.from,r.startx),n.to=Math.max(n.to,r.startx+r.width),n.width=Math.max(n.width,Math.abs(n.from-n.to))-cr.labelBoxWidth}))):(i=function(t,e){var n=!1;if([Wn.parser.yy.LINETYPE.SOLID_OPEN,Wn.parser.yy.LINETYPE.DOTTED_OPEN,Wn.parser.yy.LINETYPE.SOLID,Wn.parser.yy.LINETYPE.DOTTED,Wn.parser.yy.LINETYPE.SOLID_CROSS,Wn.parser.yy.LINETYPE.DOTTED_CROSS,Wn.parser.yy.LINETYPE.SOLID_POINT,Wn.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&(n=!0),!n)return{};var r=yr(t.from,e),i=yr(t.to,e),a=r[0]<=i[0]?1:0,o=r[0]<i[0]?0:1,s=r.concat(i),c=Math.abs(i[o]-r[a]);t.wrap&&t.message&&(t.message=H.wrapLabel(t.message,Math.max(c+2*cr.wrapPadding,cr.width),lr(cr)));var u=H.calculateTextDimensions(t.message,lr(cr));return{width:Math.max(t.wrap?0:u.width+2*cr.wrapPadding,c+2*cr.wrapPadding,cr.width),height:0,startx:r[a],stopx:i[o],starty:0,stopy:0,message:t.message,type:t.type,wrap:t.wrap,fromBounds:Math.min.apply(null,s),toBounds:Math.max.apply(null,s)}}(t,e),t.msgModel=i,i.startx&&i.stopx&&o.length>0&&o.forEach((function(r){if(n=r,i.startx===i.stopx){var a=e[t.from],o=e[t.to];n.from=Math.min(a.x-i.width/2,a.x-a.width/2,n.from),n.to=Math.max(o.x+i.width/2,o.x+a.width/2,n.to),n.width=Math.max(n.width,Math.abs(n.to-n.from))-cr.labelBoxWidth}else n.from=Math.min(i.startx,n.from),n.to=Math.max(i.stopx,n.to),n.width=Math.max(n.width,i.width)-cr.labelBoxWidth})))})),ur.activations=[],c.debug("Loop type widths:",a),a},_r={bounds:ur,drawActors:dr,setConf:pr,draw:function(t,e){cr=_t().sequence,Wn.parser.yy.clear(),Wn.parser.yy.setWrap(cr.wrap),Wn.parser.parse(t+"\n"),ur.init(),c.debug("C:".concat(JSON.stringify(cr,null,2)));var n=Object(d.select)('[id="'.concat(e,'"]')),r=Wn.parser.yy.getActors(),i=Wn.parser.yy.getActorKeys(),a=Wn.parser.yy.getMessages(),o=Wn.parser.yy.getTitle(),s=mr(r,a);cr.height=br(r,s),dr(n,r,i,0);var u=xr(a,r,s);$n.insertArrowHead(n),$n.insertArrowCrossHead(n),$n.insertArrowFilledHead(n),$n.insertSequenceNumber(n);var l=1;a.forEach((function(t){var e,i,a;switch(t.type){case Wn.parser.yy.LINETYPE.NOTE:i=t.noteModel,function(t,e){ur.bumpVerticalPos(cr.boxMargin),e.height=cr.boxMargin,e.starty=ur.getVerticalPos();var n=$n.getNoteRect();n.x=e.startx,n.y=e.starty,n.width=e.width||cr.width,n.class="note";var r=t.append("g"),i=$n.drawRect(r,n),a=$n.getTextObj();a.x=e.startx,a.y=e.starty,a.width=n.width,a.dy="1em",a.text=e.message,a.class="noteText",a.fontFamily=cr.noteFontFamily,a.fontSize=cr.noteFontSize,a.fontWeight=cr.noteFontWeight,a.anchor=cr.noteAlign,a.textMargin=cr.noteMargin,a.valign=cr.noteAlign;var o=Fn(r,a),s=Math.round(o.map((function(t){return(t._groups||t)[0][0].getBBox().height})).reduce((function(t,e){return t+e})));i.attr("height",s+2*cr.noteMargin),e.height+=s+2*cr.noteMargin,ur.bumpVerticalPos(s+2*cr.noteMargin),e.stopy=e.starty+s+2*cr.noteMargin,e.stopx=e.startx+n.width,ur.insert(e.startx,e.starty,e.stopx,e.stopy),ur.models.addNote(e)}(n,i);break;case Wn.parser.yy.LINETYPE.ACTIVE_START:ur.newActivation(t,n,r);break;case Wn.parser.yy.LINETYPE.ACTIVE_END:!function(t,e){var r=ur.endActivation(t);r.starty+18>e&&(r.starty=e-6,e+=12),$n.drawActivation(n,r,e,cr,gr(t.from.actor).length),ur.insert(r.startx,e-10,r.stopx,e)}(t,ur.getVerticalPos());break;case Wn.parser.yy.LINETYPE.LOOP_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.LOOP_END:e=ur.endLoop(),$n.drawLoop(n,e,"loop",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;case Wn.parser.yy.LINETYPE.RECT_START:vr(u,t,cr.boxMargin,cr.boxMargin,(function(t){return ur.newLoop(void 0,t.message)}));break;case Wn.parser.yy.LINETYPE.RECT_END:e=ur.endLoop(),$n.drawBackgroundRect(n,e),ur.models.addLoop(e),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos());break;case Wn.parser.yy.LINETYPE.OPT_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.OPT_END:e=ur.endLoop(),$n.drawLoop(n,e,"opt",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;case Wn.parser.yy.LINETYPE.ALT_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.ALT_ELSE:vr(u,t,cr.boxMargin+cr.boxTextMargin,cr.boxMargin,(function(t){return ur.addSectionToLoop(t)}));break;case Wn.parser.yy.LINETYPE.ALT_END:e=ur.endLoop(),$n.drawLoop(n,e,"alt",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;case Wn.parser.yy.LINETYPE.PAR_START:vr(u,t,cr.boxMargin,cr.boxMargin+cr.boxTextMargin,(function(t){return ur.newLoop(t)}));break;case Wn.parser.yy.LINETYPE.PAR_AND:vr(u,t,cr.boxMargin+cr.boxTextMargin,cr.boxMargin,(function(t){return ur.addSectionToLoop(t)}));break;case Wn.parser.yy.LINETYPE.PAR_END:e=ur.endLoop(),$n.drawLoop(n,e,"par",cr),ur.bumpVerticalPos(e.stopy-ur.getVerticalPos()),ur.models.addLoop(e);break;default:try{(a=t.msgModel).starty=ur.getVerticalPos(),a.sequenceIndex=l,function(t,e){ur.bumpVerticalPos(10);var n=e.startx,r=e.stopx,i=e.starty,a=e.message,o=e.type,s=e.sequenceIndex,c=x.splitBreaks(a).length,u=H.calculateTextDimensions(a,lr(cr)),l=u.height/c;e.height+=l,ur.bumpVerticalPos(l);var h=$n.getTextObj();h.x=n,h.y=i+10,h.width=r-n,h.class="messageText",h.dy="1em",h.text=a,h.fontFamily=cr.messageFontFamily,h.fontSize=cr.messageFontSize,h.fontWeight=cr.messageFontWeight,h.anchor=cr.messageAlign,h.valign=cr.messageAlign,h.textMargin=cr.wrapPadding,h.tspan=!1,Fn(t,h);var f,d,p=u.height-10,g=u.width;if(n===r){d=ur.getVerticalPos()+p,cr.rightAngles?f=t.append("path").attr("d","M ".concat(n,",").concat(d," H ").concat(n+Math.max(cr.width/2,g/2)," V ").concat(d+25," H ").concat(n)):(p+=cr.boxMargin,d=ur.getVerticalPos()+p,f=t.append("path").attr("d","M "+n+","+d+" C "+(n+60)+","+(d-10)+" "+(n+60)+","+(d+30)+" "+n+","+(d+20))),p+=30;var y=Math.max(g/2,cr.width/2);ur.insert(n-y,ur.getVerticalPos()-10+p,r+y,ur.getVerticalPos()+30+p)}else p+=cr.boxMargin,d=ur.getVerticalPos()+p,(f=t.append("line")).attr("x1",n),f.attr("y1",d),f.attr("x2",r),f.attr("y2",d),ur.insert(n,d-10,r,d);o===Wn.parser.yy.LINETYPE.DOTTED||o===Wn.parser.yy.LINETYPE.DOTTED_CROSS||o===Wn.parser.yy.LINETYPE.DOTTED_POINT||o===Wn.parser.yy.LINETYPE.DOTTED_OPEN?(f.style("stroke-dasharray","3, 3"),f.attr("class","messageLine1")):f.attr("class","messageLine0");var v="";cr.arrowMarkerAbsolute&&(v=(v=(v=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),f.attr("stroke-width",2),f.attr("stroke","none"),f.style("fill","none"),o!==Wn.parser.yy.LINETYPE.SOLID&&o!==Wn.parser.yy.LINETYPE.DOTTED||f.attr("marker-end","url("+v+"#arrowhead)"),o!==Wn.parser.yy.LINETYPE.SOLID_POINT&&o!==Wn.parser.yy.LINETYPE.DOTTED_POINT||f.attr("marker-end","url("+v+"#filled-head)"),o!==Wn.parser.yy.LINETYPE.SOLID_CROSS&&o!==Wn.parser.yy.LINETYPE.DOTTED_CROSS||f.attr("marker-end","url("+v+"#crosshead)"),(sr.showSequenceNumbers()||cr.showSequenceNumbers)&&(f.attr("marker-start","url("+v+"#sequencenumber)"),t.append("text").attr("x",n).attr("y",d+4).attr("font-family","sans-serif").attr("font-size","12px").attr("text-anchor","middle").attr("textLength","16px").attr("class","sequenceNumber").text(s)),ur.bumpVerticalPos(p),e.height+=p,e.stopy=e.starty+e.height,ur.insert(e.fromBounds,e.starty,e.toBounds,e.stopy)}(n,a),ur.models.addMessage(a)}catch(t){c.error("error while drawing message",t)}}[Wn.parser.yy.LINETYPE.SOLID_OPEN,Wn.parser.yy.LINETYPE.DOTTED_OPEN,Wn.parser.yy.LINETYPE.SOLID,Wn.parser.yy.LINETYPE.DOTTED,Wn.parser.yy.LINETYPE.SOLID_CROSS,Wn.parser.yy.LINETYPE.DOTTED_CROSS,Wn.parser.yy.LINETYPE.SOLID_POINT,Wn.parser.yy.LINETYPE.DOTTED_POINT].includes(t.type)&&l++})),cr.mirrorActors&&(ur.bumpVerticalPos(2*cr.boxMargin),dr(n,r,i,ur.getVerticalPos()));var h=ur.getBounds().bounds;c.debug("For line height fix Querying: #"+e+" .actor-line"),Object(d.selectAll)("#"+e+" .actor-line").attr("y2",h.stopy);var f=h.stopy-h.starty+2*cr.diagramMarginY;cr.mirrorActors&&(f=f-cr.boxMargin+cr.bottomMarginAdj);var p=h.stopx-h.startx+2*cr.diagramMarginX;o&&n.append("text").text(o).attr("x",(h.stopx-h.startx)/2-2*cr.diagramMarginX).attr("y",-25),W(n,f,p,cr.useMaxWidth);var g=o?40:0;n.attr("viewBox",h.startx-cr.diagramMarginX+" -"+(cr.diagramMarginY+g)+" "+p+" "+(f+g)),c.debug("models:",ur.models)}},kr=n(27),wr=n.n(kr);function Er(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var Tr,Cr,Ar="",Sr="",Mr="",Or=[],Dr="",Nr=[],Br=[],Lr="",Pr=["active","done","crit","milestone"],Ir=[],Fr=!1,jr=0,Rr=function(t,e,n){return t.isoWeekday()>=6&&n.indexOf("weekends")>=0||(n.indexOf(t.format("dddd").toLowerCase())>=0||n.indexOf(t.format(e.trim()))>=0)},Yr=function(t,e,n){if(n.length&&!t.manualEndTime){var r=o()(t.startTime,e,!0);r.add(1,"d");var i=o()(t.endTime,e,!0),a=zr(r,i,e,n);t.endTime=i.toDate(),t.renderEndTime=a}},zr=function(t,e,n,r){for(var i=!1,a=null;t<=e;)i||(a=e.toDate()),(i=Rr(t,n,r))&&e.add(1,"d"),t.add(1,"d");return a},Ur=function(t,e,n){n=n.trim();var r=/^after\s+([\d\w- ]+)/.exec(n.trim());if(null!==r){var i=null;if(r[1].split(" ").forEach((function(t){var e=Xr(t);void 0!==e&&(i?e.endTime>i.endTime&&(i=e):i=e)})),i)return i.endTime;var a=new Date;return a.setHours(0,0,0,0),a}var s=o()(n,e.trim(),!0);return s.isValid()?s.toDate():(c.debug("Invalid date:"+n),c.debug("With date format:"+e.trim()),new Date)},$r=function(t,e){if(null!==t)switch(t[2]){case"s":e.add(t[1],"seconds");break;case"m":e.add(t[1],"minutes");break;case"h":e.add(t[1],"hours");break;case"d":e.add(t[1],"days");break;case"w":e.add(t[1],"weeks")}return e.toDate()},Wr=function(t,e,n,r){r=r||!1,n=n.trim();var i=o()(n,e.trim(),!0);return i.isValid()?(r&&i.add(1,"d"),i.toDate()):$r(/^([\d]+)([wdhms])/.exec(n.trim()),o()(t))},Hr=0,Vr=function(t){return void 0===t?"task"+(Hr+=1):t},Gr=[],qr={},Xr=function(t){var e=qr[t];return Gr[e]},Zr=function(){for(var t=function(t){var e=Gr[t],n="";switch(Gr[t].raw.startTime.type){case"prevTaskEnd":var r=Xr(e.prevTaskId);e.startTime=r.endTime;break;case"getStartDate":(n=Ur(0,Ar,Gr[t].raw.startTime.startData))&&(Gr[t].startTime=n)}return Gr[t].startTime&&(Gr[t].endTime=Wr(Gr[t].startTime,Ar,Gr[t].raw.endTime.data,Fr),Gr[t].endTime&&(Gr[t].processed=!0,Gr[t].manualEndTime=o()(Gr[t].raw.endTime.data,"YYYY-MM-DD",!0).isValid(),Yr(Gr[t],Ar,Or))),Gr[t].processed},e=!0,n=0;n<Gr.length;n++)t(n),e=e&&Gr[n].processed;return e},Jr=function(t,e){t.split(",").forEach((function(t){var n=Xr(t);void 0!==n&&n.classes.push(e)}))},Kr=function(t,e){Ir.push((function(){var n=document.querySelector('[id="'.concat(t,'"]'));null!==n&&n.addEventListener("click",(function(){e()}))})),Ir.push((function(){var n=document.querySelector('[id="'.concat(t,'-text"]'));null!==n&&n.addEventListener("click",(function(){e()}))}))},Qr={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().gantt},clear:function(){Nr=[],Br=[],Lr="",Ir=[],Dr="",Hr=0,Tr=void 0,Cr=void 0,Gr=[],Ar="",Sr="",Mr="",Or=[],Fr=!1,jr=0},setDateFormat:function(t){Ar=t},getDateFormat:function(){return Ar},enableInclusiveEndDates:function(){Fr=!0},endDatesAreInclusive:function(){return Fr},setAxisFormat:function(t){Sr=t},getAxisFormat:function(){return Sr},setTodayMarker:function(t){Mr=t},getTodayMarker:function(){return Mr},setTitle:function(t){Dr=t},getTitle:function(){return Dr},addSection:function(t){Lr=t,Nr.push(t)},getSections:function(){return Nr},getTasks:function(){for(var t=Zr(),e=0;!t&&e<10;)t=Zr(),e++;return Br=Gr},addTask:function(t,e){var n={section:Lr,type:Lr,processed:!1,manualEndTime:!1,renderEndTime:null,raw:{data:e},task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};ti(n,r,Pr);for(var i=0;i<n.length;i++)n[i]=n[i].trim();switch(n.length){case 1:r.id=Vr(),r.startTime={type:"prevTaskEnd",id:t},r.endTime={data:n[0]};break;case 2:r.id=Vr(),r.startTime={type:"getStartDate",startData:n[0]},r.endTime={data:n[1]};break;case 3:r.id=Vr(n[0]),r.startTime={type:"getStartDate",startData:n[1]},r.endTime={data:n[2]}}return r}(Cr,e);n.raw.startTime=r.startTime,n.raw.endTime=r.endTime,n.id=r.id,n.prevTaskId=Cr,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,n.order=jr,jr++;var i=Gr.push(n);Cr=n.id,qr[n.id]=i-1},findTaskById:Xr,addTaskOrg:function(t,e){var n={section:Lr,type:Lr,description:t,task:t,classes:[]},r=function(t,e){var n=(":"===e.substr(0,1)?e.substr(1,e.length):e).split(","),r={};ti(n,r,Pr);for(var i=0;i<n.length;i++)n[i]=n[i].trim();var a="";switch(n.length){case 1:r.id=Vr(),r.startTime=t.endTime,a=n[0];break;case 2:r.id=Vr(),r.startTime=Ur(0,Ar,n[0]),a=n[1];break;case 3:r.id=Vr(n[0]),r.startTime=Ur(0,Ar,n[1]),a=n[2]}return a&&(r.endTime=Wr(r.startTime,Ar,a,Fr),r.manualEndTime=o()(a,"YYYY-MM-DD",!0).isValid(),Yr(r,Ar,Or)),r}(Tr,e);n.startTime=r.startTime,n.endTime=r.endTime,n.id=r.id,n.active=r.active,n.done=r.done,n.crit=r.crit,n.milestone=r.milestone,Tr=n,Br.push(n)},setExcludes:function(t){Or=t.toLowerCase().split(/[\s,]+/)},getExcludes:function(){return Or},setClickEvent:function(t,e,n){t.split(",").forEach((function(t){!function(t,e,n){if("loose"===_t().securityLevel&&void 0!==e){var r=[];if("string"==typeof n){r=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(var i=0;i<r.length;i++){var a=r[i].trim();'"'===a.charAt(0)&&'"'===a.charAt(a.length-1)&&(a=a.substr(1,a.length-2)),r[i]=a}}0===r.length&&r.push(t),void 0!==Xr(t)&&Kr(t,(function(){H.runFunc.apply(H,[e].concat(Er(r)))}))}}(t,e,n)})),Jr(t,"clickable")},setLink:function(t,e){var n=e;"loose"!==_t().securityLevel&&(n=Object(g.sanitizeUrl)(e)),t.split(",").forEach((function(t){void 0!==Xr(t)&&Kr(t,(function(){window.open(n,"_self")}))})),Jr(t,"clickable")},bindFunctions:function(t){Ir.forEach((function(e){e(t)}))},durationToDate:$r};function ti(t,e,n){for(var r=!0;r;)r=!1,n.forEach((function(n){var i=new RegExp("^\\s*"+n+"\\s*$");t[0].match(i)&&(e[n]=!0,t.shift(1),r=!0)}))}kr.parser.yy=Qr;var ei,ni={titleTopMargin:25,barHeight:20,barGap:4,topPadding:50,rightPadding:75,leftPadding:75,gridLineStartPadding:35,fontSize:11,fontFamily:'"Open-Sans", "sans-serif"'},ri=function(t){Object.keys(t).forEach((function(e){ni[e]=t[e]}))},ii=function(t,e){kr.parser.yy.clear(),kr.parser.parse(t);var n=document.getElementById(e);void 0===(ei=n.parentElement.offsetWidth)&&(ei=1200),void 0!==ni.useWidth&&(ei=ni.useWidth);var r=kr.parser.yy.getTasks(),i=r.length*(ni.barHeight+ni.barGap)+2*ni.topPadding;n.setAttribute("viewBox","0 0 "+ei+" "+i);for(var a=Object(d.select)('[id="'.concat(e,'"]')),o=Object(d.scaleTime)().domain([Object(d.min)(r,(function(t){return t.startTime})),Object(d.max)(r,(function(t){return t.endTime}))]).rangeRound([0,ei-ni.leftPadding-ni.rightPadding]),s=[],c=0;c<r.length;c++)s.push(r[c].type);var u=s;function l(t){for(var e=t.length,n={};e;)n[t[--e]]=(n[t[e]]||0)+1;return n}s=function(t){for(var e={},n=[],r=0,i=t.length;r<i;++r)e.hasOwnProperty(t[r])||(e[t[r]]=!0,n.push(t[r]));return n}(s),r.sort((function(t,e){var n=t.startTime,r=e.startTime,i=0;return n>r?i=1:n<r&&(i=-1),i})),function(t,e,n){var r=ni.barHeight,i=r+ni.barGap,c=ni.topPadding,h=ni.leftPadding;Object(d.scaleLinear)().domain([0,s.length]).range(["#00B9FA","#F95002"]).interpolate(d.interpolateHcl);(function(t,e,n,r){var i=Object(d.axisBottom)(o).tickSize(-r+e+ni.gridLineStartPadding).tickFormat(Object(d.timeFormat)(kr.parser.yy.getAxisFormat()||ni.axisFormat||"%Y-%m-%d"));a.append("g").attr("class","grid").attr("transform","translate("+t+", "+(r-50)+")").call(i).selectAll("text").style("text-anchor","middle").attr("fill","#000").attr("stroke","none").attr("font-size",10).attr("dy","1em")})(h,c,0,n),function(t,e,n,r,i,c,u){a.append("g").selectAll("rect").data(t).enter().append("rect").attr("x",0).attr("y",(function(t,r){return t.order*e+n-2})).attr("width",(function(){return u-ni.rightPadding/2})).attr("height",e).attr("class",(function(t){for(var e=0;e<s.length;e++)if(t.type===s[e])return"section section"+e%ni.numberSectionStyles;return"section section0"}));var l=a.append("g").selectAll("rect").data(t).enter();l.append("rect").attr("id",(function(t){return t.id})).attr("rx",3).attr("ry",3).attr("x",(function(t){return t.milestone?o(t.startTime)+r+.5*(o(t.endTime)-o(t.startTime))-.5*i:o(t.startTime)+r})).attr("y",(function(t,r){return t.order*e+n})).attr("width",(function(t){return t.milestone?i:o(t.renderEndTime||t.endTime)-o(t.startTime)})).attr("height",i).attr("transform-origin",(function(t,a){return(o(t.startTime)+r+.5*(o(t.endTime)-o(t.startTime))).toString()+"px "+(a*e+n+.5*i).toString()+"px"})).attr("class",(function(t){var e="";t.classes.length>0&&(e=t.classes.join(" "));for(var n=0,r=0;r<s.length;r++)t.type===s[r]&&(n=r%ni.numberSectionStyles);var i="";return t.active?t.crit?i+=" activeCrit":i=" active":t.done?i=t.crit?" doneCrit":" done":t.crit&&(i+=" crit"),0===i.length&&(i=" task"),t.milestone&&(i=" milestone "+i),i+=n,"task"+(i+=" "+e)})),l.append("text").attr("id",(function(t){return t.id+"-text"})).text((function(t){return t.task})).attr("font-size",ni.fontSize).attr("x",(function(t){var e=o(t.startTime),n=o(t.renderEndTime||t.endTime);t.milestone&&(e+=.5*(o(t.endTime)-o(t.startTime))-.5*i),t.milestone&&(n=e+i);var a=this.getBBox().width;return a>n-e?n+a+1.5*ni.leftPadding>u?e+r-5:n+r+5:(n-e)/2+e+r})).attr("y",(function(t,r){return t.order*e+ni.barHeight/2+(ni.fontSize/2-2)+n})).attr("text-height",i).attr("class",(function(t){var e=o(t.startTime),n=o(t.endTime);t.milestone&&(n=e+i);var r=this.getBBox().width,a="";t.classes.length>0&&(a=t.classes.join(" "));for(var c=0,l=0;l<s.length;l++)t.type===s[l]&&(c=l%ni.numberSectionStyles);var h="";return t.active&&(h=t.crit?"activeCritText"+c:"activeText"+c),t.done?h=t.crit?h+" doneCritText"+c:h+" doneText"+c:t.crit&&(h=h+" critText"+c),t.milestone&&(h+=" milestoneText"),r>n-e?n+r+1.5*ni.leftPadding>u?a+" taskTextOutsideLeft taskTextOutside"+c+" "+h:a+" taskTextOutsideRight taskTextOutside"+c+" "+h+" width-"+r:a+" taskText taskText"+c+" "+h+" width-"+r}))}(t,i,c,h,r,0,e),function(t,e){for(var n=[],r=0,i=0;i<s.length;i++)n[i]=[s[i],(o=s[i],c=u,l(c)[o]||0)];var o,c;a.append("g").selectAll("text").data(n).enter().append((function(t){var e=t[0].split(x.lineBreakRegex),n=-(e.length-1)/2,r=document.createElementNS("http://www.w3.org/2000/svg","text");r.setAttribute("dy",n+"em");for(var i=0;i<e.length;i++){var a=document.createElementNS("http://www.w3.org/2000/svg","tspan");a.setAttribute("alignment-baseline","central"),a.setAttribute("x","10"),i>0&&a.setAttribute("dy","1em"),a.textContent=e[i],r.appendChild(a)}return r})).attr("x",10).attr("y",(function(i,a){if(!(a>0))return i[1]*t/2+e;for(var o=0;o<a;o++)return r+=n[a-1][1],i[1]*t/2+r*t+e})).attr("class",(function(t){for(var e=0;e<s.length;e++)if(t[0]===s[e])return"sectionTitle sectionTitle"+e%ni.numberSectionStyles;return"sectionTitle"}))}(i,c),function(t,e,n,r){var i=Qr.getTodayMarker();if("off"===i)return;var s=a.append("g").attr("class","today"),c=new Date,u=s.append("line");u.attr("x1",o(c)+t).attr("x2",o(c)+t).attr("y1",ni.titleTopMargin).attr("y2",r-ni.titleTopMargin).attr("class","today"),""!==i&&u.attr("style",i.replace(/,/g,";"))}(h,0,0,n)}(r,ei,i),W(a,i,ei,ni.useMaxWidth),a.append("text").text(kr.parser.yy.getTitle()).attr("x",ei/2).attr("y",ni.titleTopMargin).attr("class","titleText")},ai=n(13),oi=n.n(ai);ai.parser.yy=cn;var si={},ci={dividerMargin:10,padding:5,textHeight:10},ui=function(t){for(var e=Object.keys(si),n=0;n<e.length;n++)if(si[e[n]].label===t)return e[n]},li=function(t){Object.keys(t).forEach((function(e){ci[e]=t[e]}))},hi=function(t,e){si={},ai.parser.yy.clear(),ai.parser.parse(t),c.info("Rendering diagram "+t);var n,r=Object(d.select)("[id='".concat(e,"']"));r.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),(n=r).append("defs").append("marker").attr("id","extensionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 1,7 L18,13 V 1 Z"),n.append("defs").append("marker").attr("id","extensionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 1,1 V 13 L18,7 Z"),n.append("defs").append("marker").attr("id","compositionStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","compositionEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","aggregationEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyStart").attr("class","extension").attr("refX",0).attr("refY",7).attr("markerWidth",190).attr("markerHeight",240).attr("orient","auto").append("path").attr("d","M 5,7 L9,13 L1,7 L9,1 Z"),n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 18,7 L9,13 L14,7 L9,1 Z");var i=new G.a.Graph({multigraph:!0});i.setGraph({isMultiGraph:!0}),i.setDefaultEdgeLabel((function(){return{}}));for(var a=cn.getClasses(),o=Object.keys(a),s=0;s<o.length;s++){var u=a[o[s]],l=vn(r,u,ci);si[l.id]=l,i.setNode(l.id,l),c.info("Org height: "+l.height)}cn.getRelations().forEach((function(t){c.info("tjoho"+ui(t.id1)+ui(t.id2)+JSON.stringify(t)),i.setEdge(ui(t.id1),ui(t.id2),{relation:t},t.title||"DEFAULT")})),ke.a.layout(i),i.nodes().forEach((function(t){void 0!==t&&void 0!==i.node(t)&&(c.debug("Node "+t+": "+JSON.stringify(i.node(t))),Object(d.select)("#"+en(t)).attr("transform","translate("+(i.node(t).x-i.node(t).width/2)+","+(i.node(t).y-i.node(t).height/2)+" )"))})),i.edges().forEach((function(t){void 0!==t&&void 0!==i.edge(t)&&(c.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(i.edge(t))),mn(r,i.edge(t),i.edge(t).relation,ci))}));var h=r.node().getBBox(),f=h.width+40,p=h.height+40;W(r,p,f,ci.useMaxWidth);var g="".concat(h.x-20," ").concat(h.y-20," ").concat(f," ").concat(p);c.debug("viewBox ".concat(g)),r.attr("viewBox",g)};ai.parser.yy=cn;var fi={dividerMargin:10,padding:5,textHeight:10},di=function(t){Object.keys(t).forEach((function(e){fi[e]=t[e]}))},pi=function(t,e){c.info("Drawing class"),cn.clear(),ai.parser.parse(t);var n=_t().flowchart;c.info("config:",n);var r=n.nodeSpacing||50,i=n.rankSpacing||50,a=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:"TD",nodesep:r,ranksep:i,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}})),o=cn.getClasses(),s=cn.getRelations();c.info(s),function(t,e){var n=Object.keys(t);c.info("keys:",n),c.info(t),n.forEach((function(n){var r=t[n],i="";r.cssClasses.length>0&&(i=i+" "+r.cssClasses.join(" "));var a={labelStyle:""},o=void 0!==r.text?r.text:r.id,s="";switch(r.type){case"class":s="class_box";break;default:s="class_box"}e.setNode(r.id,{labelStyle:a.labelStyle,shape:s,labelText:o,classData:r,rx:0,ry:0,class:i,style:a.style,id:r.id,domId:r.domId,haveCallback:r.haveCallback,link:r.link,width:"group"===r.type?500:void 0,type:r.type,padding:_t().flowchart.padding}),c.info("setNode",{labelStyle:a.labelStyle,shape:s,labelText:o,rx:0,ry:0,class:i,style:a.style,id:r.id,width:"group"===r.type?500:void 0,type:r.type,padding:_t().flowchart.padding})}))}(o,a),function(t,e){var n=0;t.forEach((function(r){n++;var i={classes:"relation"};i.pattern=1==r.relation.lineType?"dashed":"solid",i.id="id"+n,"arrow_open"===r.type?i.arrowhead="none":i.arrowhead="normal",c.info(i,r),i.startLabelRight="none"===r.relationTitle1?"":r.relationTitle1,i.endLabelLeft="none"===r.relationTitle2?"":r.relationTitle2,i.arrowTypeStart=gi(r.relation.type1),i.arrowTypeEnd=gi(r.relation.type2);var a="",o="";if(void 0!==r.style){var s=B(r.style);a=s.style,o=s.labelStyle}else a="fill:none";i.style=a,i.labelStyle=o,void 0!==r.interpolate?i.curve=D(r.interpolate,d.curveLinear):void 0!==t.defaultInterpolate?i.curve=D(t.defaultInterpolate,d.curveLinear):i.curve=D(fi.curve,d.curveLinear),r.text=r.title,void 0===r.text?void 0!==r.style&&(i.arrowheadStyle="fill: #333"):(i.arrowheadStyle="fill: #333",i.labelpos="c",_t().flowchart.htmlLabels,i.labelType="text",i.label=r.text.replace(x.lineBreakRegex,"\n"),void 0===r.style&&(i.style=i.style||"stroke: #333; stroke-width: 1.5px;fill:none"),i.labelStyle=i.labelStyle.replace("color:","fill:")),e.setEdge(r.id1,r.id2,i,n)}))}(s,a);var u=Object(d.select)('[id="'.concat(e,'"]'));u.attr("xmlns:xlink","http://www.w3.org/1999/xlink");var l=Object(d.select)("#"+e+" g");On(l,a,["aggregation","extension","composition","dependency"],"classDiagram",e);var h=u.node().getBBox(),f=h.width+16,p=h.height+16;if(c.debug("new ViewBox 0 0 ".concat(f," ").concat(p),"translate(".concat(8-a._label.marginx,", ").concat(8-a._label.marginy,")")),W(u,p,f,n.useMaxWidth),u.attr("viewBox","0 0 ".concat(f," ").concat(p)),u.select("g").attr("transform","translate(".concat(8-a._label.marginx,", ").concat(8-h.y,")")),!n.htmlLabels)for(var g=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),y=0;y<g.length;y++){var v=g[y],m=v.getBBox(),b=document.createElementNS("http://www.w3.org/2000/svg","rect");b.setAttribute("rx",0),b.setAttribute("ry",0),b.setAttribute("width",m.width),b.setAttribute("height",m.height),b.setAttribute("style","fill:#e8e8e8;"),v.insertBefore(b,v.firstChild)}};function gi(t){var e;switch(t){case 0:e="aggregation";break;case 1:e="extension";break;case 2:e="composition";break;case 3:e="dependency";break;default:e="none"}return e}function yi(t){return(yi="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var vi,mi=function(t){return JSON.parse(JSON.stringify(t))},bi=[],xi={root:{relations:[],states:{},documents:{}}},_i=xi.root,ki=0,wi=function(t,e,n,r,i){void 0===_i.states[t]?_i.states[t]={id:t,descriptions:[],type:e,doc:n,note:i}:(_i.states[t].doc||(_i.states[t].doc=n),_i.states[t].type||(_i.states[t].type=e)),r&&(c.info("Adding state ",t,r),"string"==typeof r&&Ci(t,r.trim()),"object"===yi(r)&&r.forEach((function(e){return Ci(t,e.trim())}))),i&&(_i.states[t].note=i)},Ei=function(){_i=(xi={root:{relations:[],states:{},documents:{}}}).root,_i=xi.root,ki=0,0,Si=[]},Ti=function(t,e,n){var r=t,i=e,a="default",o="default";"[*]"===t&&(r="start"+ ++ki,a="start"),"[*]"===e&&(i="end"+ki,o="end"),wi(r,a),wi(i,o),_i.relations.push({id1:r,id2:i,title:n})},Ci=function(t,e){var n=_i.states[t],r=e;":"===r[0]&&(r=r.substr(1).trim()),n.descriptions.push(r)},Ai=0,Si=[],Mi={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().state},addState:wi,clear:Ei,getState:function(t){return _i.states[t]},getStates:function(){return _i.states},getRelations:function(){return _i.relations},getClasses:function(){return Si},getDirection:function(){return"TB"},addRelation:Ti,getDividerId:function(){return"divider-id-"+ ++Ai},cleanupLabel:function(t){return":"===t.substring(0,1)?t.substr(2).trim():t.trim()},lineType:{LINE:0,DOTTED_LINE:1},relationType:{AGGREGATION:0,EXTENSION:1,COMPOSITION:2,DEPENDENCY:3},logDocuments:function(){c.info("Documents = ",xi)},getRootDoc:function(){return bi},setRootDoc:function(t){c.info("Setting root doc",t),bi=t},getRootDocV2:function(){return function t(e,n,r){if("relation"===n.stmt)t(e,n.state1,!0),t(e,n.state2,!1);else if("state"===n.stmt&&"[*]"===n.id&&(n.id=r?e.id+"_start":e.id+"_end",n.start=r),n.doc){var i=[],a=0,o=[];for(a=0;a<n.doc.length;a++)if("divider"===n.doc[a].type){var s=mi(n.doc[a]);s.doc=mi(o),i.push(s),o=[]}else o.push(n.doc[a]);if(i.length>0&&o.length>0){var c={stmt:"state",id:P(),type:"divider",doc:mi(o)};i.push(mi(c)),n.doc=i}n.doc.forEach((function(e){return t(n,e,!0)}))}}({id:"root"},{id:"root",doc:bi},!0),{id:"root",doc:bi}},extract:function(t){var e;e=t.doc?t.doc:t,c.info(e),Ei(),c.info("Extract",e),e.forEach((function(t){"state"===t.stmt&&wi(t.id,t.type,t.doc,t.description,t.note),"relation"===t.stmt&&Ti(t.state1.id,t.state2.id,t.description)}))},trimColon:function(t){return t&&":"===t[0]?t.substr(1).trim():t.trim()}},Oi=n(22),Di=n.n(Oi),Ni={},Bi=function(t,e){Ni[t]=e},Li=function(t,e){var n=t.append("text").attr("x",2*_t().state.padding).attr("y",_t().state.textHeight+1.3*_t().state.padding).attr("font-size",_t().state.fontSize).attr("class","state-title").text(e.descriptions[0]).node().getBBox(),r=n.height,i=t.append("text").attr("x",_t().state.padding).attr("y",r+.4*_t().state.padding+_t().state.dividerMargin+_t().state.textHeight).attr("class","state-description"),a=!0,o=!0;e.descriptions.forEach((function(t){a||(!function(t,e,n){var r=t.append("tspan").attr("x",2*_t().state.padding).text(e);n||r.attr("dy",_t().state.textHeight)}(i,t,o),o=!1),a=!1}));var s=t.append("line").attr("x1",_t().state.padding).attr("y1",_t().state.padding+r+_t().state.dividerMargin/2).attr("y2",_t().state.padding+r+_t().state.dividerMargin/2).attr("class","descr-divider"),c=i.node().getBBox(),u=Math.max(c.width,n.width);return s.attr("x2",u+3*_t().state.padding),t.insert("rect",":first-child").attr("x",_t().state.padding).attr("y",_t().state.padding).attr("width",u+2*_t().state.padding).attr("height",c.height+r+2*_t().state.padding).attr("rx",_t().state.radius),t},Pi=function(t,e,n){var r,i=_t().state.padding,a=2*_t().state.padding,o=t.node().getBBox(),s=o.width,c=o.x,u=t.append("text").attr("x",0).attr("y",_t().state.titleShift).attr("font-size",_t().state.fontSize).attr("class","state-title").text(e.id),l=u.node().getBBox().width+a,h=Math.max(l,s);h===s&&(h+=a);var f=t.node().getBBox();e.doc,r=c-i,l>s&&(r=(s-h)/2+i),Math.abs(c-f.x)<i&&l>s&&(r=c-(l-s)/2);var d=1-_t().state.textHeight;return t.insert("rect",":first-child").attr("x",r).attr("y",d).attr("class",n?"alt-composit":"composit").attr("width",h).attr("height",f.height+_t().state.textHeight+_t().state.titleShift+1).attr("rx","0"),u.attr("x",r+i),l<=s&&u.attr("x",c+(h-a)/2-l/2+i),t.insert("rect",":first-child").attr("x",r).attr("y",_t().state.titleShift-_t().state.textHeight-_t().state.padding).attr("width",h).attr("height",3*_t().state.textHeight).attr("rx",_t().state.radius),t.insert("rect",":first-child").attr("x",r).attr("y",_t().state.titleShift-_t().state.textHeight-_t().state.padding).attr("width",h).attr("height",f.height+3+2*_t().state.textHeight).attr("rx",_t().state.radius),t},Ii=function(t,e){e.attr("class","state-note");var n=e.append("rect").attr("x",0).attr("y",_t().state.padding),r=function(t,e,n,r){var i=0,a=r.append("text");a.style("text-anchor","start"),a.attr("class","noteText");var o=t.replace(/\r\n/g,"<br/>"),s=(o=o.replace(/\n/g,"<br/>")).split(x.lineBreakRegex),c=1.25*_t().state.noteMargin,u=!0,l=!1,h=void 0;try{for(var f,d=s[Symbol.iterator]();!(u=(f=d.next()).done);u=!0){var p=f.value.trim();if(p.length>0){var g=a.append("tspan");if(g.text(p),0===c)c+=g.node().getBBox().height;i+=c,g.attr("x",e+_t().state.noteMargin),g.attr("y",n+i+1.25*_t().state.noteMargin)}}}catch(t){l=!0,h=t}finally{try{u||null==d.return||d.return()}finally{if(l)throw h}}return{textWidth:a.node().getBBox().width,textHeight:i}}(t,0,0,e.append("g")),i=r.textWidth,a=r.textHeight;return n.attr("height",a+2*_t().state.noteMargin),n.attr("width",i+2*_t().state.noteMargin),n},Fi=function(t,e){var n=e.id,r={id:n,label:e.id,width:0,height:0},i=t.append("g").attr("id",n).attr("class","stateGroup");"start"===e.type&&function(t){t.append("circle").attr("class","start-state").attr("r",_t().state.sizeUnit).attr("cx",_t().state.padding+_t().state.sizeUnit).attr("cy",_t().state.padding+_t().state.sizeUnit)}(i),"end"===e.type&&function(t){t.append("circle").attr("class","end-state-outer").attr("r",_t().state.sizeUnit+_t().state.miniPadding).attr("cx",_t().state.padding+_t().state.sizeUnit+_t().state.miniPadding).attr("cy",_t().state.padding+_t().state.sizeUnit+_t().state.miniPadding),t.append("circle").attr("class","end-state-inner").attr("r",_t().state.sizeUnit).attr("cx",_t().state.padding+_t().state.sizeUnit+2).attr("cy",_t().state.padding+_t().state.sizeUnit+2)}(i),"fork"!==e.type&&"join"!==e.type||function(t,e){var n=_t().state.forkWidth,r=_t().state.forkHeight;if(e.parentId){var i=n;n=r,r=i}t.append("rect").style("stroke","black").style("fill","black").attr("width",n).attr("height",r).attr("x",_t().state.padding).attr("y",_t().state.padding)}(i,e),"note"===e.type&&Ii(e.note.text,i),"divider"===e.type&&function(t){t.append("line").style("stroke","grey").style("stroke-dasharray","3").attr("x1",_t().state.textHeight).attr("class","divider").attr("x2",2*_t().state.textHeight).attr("y1",0).attr("y2",0)}(i),"default"===e.type&&0===e.descriptions.length&&function(t,e){var n=t.append("text").attr("x",2*_t().state.padding).attr("y",_t().state.textHeight+2*_t().state.padding).attr("font-size",_t().state.fontSize).attr("class","state-title").text(e.id),r=n.node().getBBox();t.insert("rect",":first-child").attr("x",_t().state.padding).attr("y",_t().state.padding).attr("width",r.width+2*_t().state.padding).attr("height",r.height+2*_t().state.padding).attr("rx",_t().state.radius)}(i,e),"default"===e.type&&e.descriptions.length>0&&Li(i,e);var a=i.node().getBBox();return r.width=a.width+2*_t().state.padding,r.height=a.height+2*_t().state.padding,Bi(n,r),r},ji=0;Oi.parser.yy=Mi;var Ri={},Yi=function t(e,n,r,i){var a,o=new G.a.Graph({compound:!0,multigraph:!0}),s=!0;for(a=0;a<e.length;a++)if("relation"===e[a].stmt){s=!1;break}r?o.setGraph({rankdir:"LR",multigraph:!0,compound:!0,ranker:"tight-tree",ranksep:s?1:vi.edgeLengthFactor,nodeSep:s?1:50,isMultiGraph:!0}):o.setGraph({rankdir:"TB",multigraph:!0,compound:!0,ranksep:s?1:vi.edgeLengthFactor,nodeSep:s?1:50,ranker:"tight-tree",isMultiGraph:!0}),o.setDefaultEdgeLabel((function(){return{}})),Mi.extract(e);for(var u=Mi.getStates(),l=Mi.getRelations(),h=Object.keys(u),f=0;f<h.length;f++){var p=u[h[f]];r&&(p.parentId=r);var g=void 0;if(p.doc){var y=n.append("g").attr("id",p.id).attr("class","stateGroup");g=t(p.doc,y,p.id,!i);var v=(y=Pi(y,p,i)).node().getBBox();g.width=v.width,g.height=v.height+vi.padding/2,Ri[p.id]={y:vi.compositTitleSize}}else g=Fi(n,p);if(p.note){var m={descriptions:[],id:p.id+"-note",note:p.note,type:"note"},b=Fi(n,m);"left of"===p.note.position?(o.setNode(g.id+"-note",b),o.setNode(g.id,g)):(o.setNode(g.id,g),o.setNode(g.id+"-note",b)),o.setParent(g.id,g.id+"-group"),o.setParent(g.id+"-note",g.id+"-group")}else o.setNode(g.id,g)}c.debug("Count=",o.nodeCount(),o);var _=0;l.forEach((function(t){var e;_++,c.debug("Setting edge",t),o.setEdge(t.id1,t.id2,{relation:t,width:(e=t.title,e?e.length*vi.fontSizeFactor:1),height:vi.labelHeight*x.getRows(t.title).length,labelpos:"c"},"id"+_)})),ke.a.layout(o),c.debug("Graph after layout",o.nodes());var k=n.node();o.nodes().forEach((function(t){void 0!==t&&void 0!==o.node(t)?(c.warn("Node "+t+": "+JSON.stringify(o.node(t))),Object(d.select)("#"+k.id+" #"+t).attr("transform","translate("+(o.node(t).x-o.node(t).width/2)+","+(o.node(t).y+(Ri[t]?Ri[t].y:0)-o.node(t).height/2)+" )"),Object(d.select)("#"+k.id+" #"+t).attr("data-x-shift",o.node(t).x-o.node(t).width/2),document.querySelectorAll("#"+k.id+" #"+t+" .divider").forEach((function(t){var e=t.parentElement,n=0,r=0;e&&(e.parentElement&&(n=e.parentElement.getBBox().width),r=parseInt(e.getAttribute("data-x-shift"),10),Number.isNaN(r)&&(r=0)),t.setAttribute("x1",0-r+8),t.setAttribute("x2",n-r-8)}))):c.debug("No Node "+t+": "+JSON.stringify(o.node(t)))}));var w=k.getBBox();o.edges().forEach((function(t){void 0!==t&&void 0!==o.edge(t)&&(c.debug("Edge "+t.v+" -> "+t.w+": "+JSON.stringify(o.edge(t))),function(t,e,n){e.points=e.points.filter((function(t){return!Number.isNaN(t.y)}));var r=e.points,i=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis),a=t.append("path").attr("d",i(r)).attr("id","edge"+ji).attr("class","transition"),o="";if(_t().state.arrowMarkerAbsolute&&(o=(o=(o=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),a.attr("marker-end","url("+o+"#"+function(t){switch(t){case Mi.relationType.AGGREGATION:return"aggregation";case Mi.relationType.EXTENSION:return"extension";case Mi.relationType.COMPOSITION:return"composition";case Mi.relationType.DEPENDENCY:return"dependency"}}(Mi.relationType.DEPENDENCY)+"End)"),void 0!==n.title){for(var s=t.append("g").attr("class","stateLabel"),u=H.calcLabelPosition(e.points),l=u.x,h=u.y,f=x.getRows(n.title),p=0,g=[],y=0,v=0,m=0;m<=f.length;m++){var b=s.append("text").attr("text-anchor","middle").text(f[m]).attr("x",l).attr("y",h+p),_=b.node().getBBox();if(y=Math.max(y,_.width),v=Math.min(v,_.x),c.info(_.x,l,h+p),0===p){var k=b.node().getBBox();p=k.height,c.info("Title height",p,h)}g.push(b)}var w=p*f.length;if(f.length>1){var E=(f.length-1)*p*.5;g.forEach((function(t,e){return t.attr("y",h+e*p-E)})),w=p*f.length}var T=s.node().getBBox();s.insert("rect",":first-child").attr("class","box").attr("x",l-y/2-_t().state.padding/2).attr("y",h-w/2-_t().state.padding/2-3.5).attr("width",y+_t().state.padding).attr("height",w+_t().state.padding),c.info(T)}ji++}(n,o.edge(t),o.edge(t).relation))})),w=k.getBBox();var E={id:r||"root",label:r||"root",width:0,height:0};return E.width=w.width+2*vi.padding,E.height=w.height+2*vi.padding,c.debug("Doc rendered",E,o),E},zi=function(){},Ui=function(t,e){vi=_t().state,Oi.parser.yy.clear(),Oi.parser.parse(t),c.debug("Rendering diagram "+t);var n=Object(d.select)("[id='".concat(e,"']"));n.append("defs").append("marker").attr("id","dependencyEnd").attr("refX",19).attr("refY",7).attr("markerWidth",20).attr("markerHeight",28).attr("orient","auto").append("path").attr("d","M 19,7 L9,13 L14,7 L9,1 Z"),new G.a.Graph({multigraph:!0,compound:!0,rankdir:"RL"}).setDefaultEdgeLabel((function(){return{}}));var r=Mi.getRootDoc();Yi(r,n,void 0,!1);var i=vi.padding,a=n.node().getBBox(),o=a.width+2*i,s=a.height+2*i;W(n,s,1.75*o,vi.useMaxWidth),n.attr("viewBox","".concat(a.x-vi.padding," ").concat(a.y-vi.padding," ")+o+" "+s)},$i={},Wi={},Hi=function(t,e,n,r){if("root"!==n.id){var i="rect";!0===n.start&&(i="start"),!1===n.start&&(i="end"),"default"!==n.type&&(i=n.type),Wi[n.id]||(Wi[n.id]={id:n.id,shape:i,description:n.id,classes:"statediagram-state"}),n.description&&(Array.isArray(Wi[n.id].description)?(Wi[n.id].shape="rectWithTitle",Wi[n.id].description.push(n.description)):Wi[n.id].description.length>0?(Wi[n.id].shape="rectWithTitle",Wi[n.id].description===n.id?Wi[n.id].description=[n.description]:Wi[n.id].description=[Wi[n.id].description,n.description]):(Wi[n.id].shape="rect",Wi[n.id].description=n.description)),!Wi[n.id].type&&n.doc&&(c.info("Setting cluser for ",n.id),Wi[n.id].type="group",Wi[n.id].shape="divider"===n.type?"divider":"roundedWithTitle",Wi[n.id].classes=Wi[n.id].classes+" "+(r?"statediagram-cluster statediagram-cluster-alt":"statediagram-cluster"));var a={labelStyle:"",shape:Wi[n.id].shape,labelText:Wi[n.id].description,classes:Wi[n.id].classes,style:"",id:n.id,domId:"state-"+n.id+"-"+Vi,type:Wi[n.id].type,padding:15};if(n.note){var o={labelStyle:"",shape:"note",labelText:n.note.text,classes:"statediagram-note",style:"",id:n.id+"----note",domId:"state-"+n.id+"----note-"+Vi,type:Wi[n.id].type,padding:15},s={labelStyle:"",shape:"noteGroup",labelText:n.note.text,classes:Wi[n.id].classes,style:"",id:n.id+"----parent",domId:"state-"+n.id+"----parent-"+Vi,type:"group",padding:0};Vi++,t.setNode(n.id+"----parent",s),t.setNode(o.id,o),t.setNode(n.id,a),t.setParent(n.id,n.id+"----parent"),t.setParent(o.id,n.id+"----parent");var u=n.id,l=o.id;"left of"===n.note.position&&(u=o.id,l=n.id),t.setEdge(u,l,{arrowhead:"none",arrowType:"",style:"fill:none",labelStyle:"",classes:"transition note-edge",arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal"})}else t.setNode(n.id,a)}e&&"root"!==e.id&&(c.info("Setting node ",n.id," to be child of its parent ",e.id),t.setParent(n.id,e.id)),n.doc&&(c.info("Adding nodes children "),Gi(t,n,n.doc,!r))},Vi=0,Gi=function(t,e,n,r){Vi=0,c.trace("items",n),n.forEach((function(n){if("state"===n.stmt||"default"===n.stmt)Hi(t,e,n,r);else if("relation"===n.stmt){Hi(t,e,n.state1,r),Hi(t,e,n.state2,r);var i={id:"edge"+Vi,arrowhead:"normal",arrowTypeEnd:"arrow_barb",style:"fill:none",labelStyle:"",label:n.description,arrowheadStyle:"fill: #333",labelpos:"c",labelType:"text",thickness:"normal",classes:"transition"},a=n.state1.id,o=n.state2.id;t.setEdge(a,o,i,Vi),Vi++}}))},qi=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)$i[e[n]]=t[e[n]]},Xi=function(t,e){c.info("Drawing state diagram (v2)",e),Mi.clear(),Wi={};var n=Di.a.parser;n.yy=Mi,n.parse(t);var r=Mi.getDirection();void 0===r&&(r="LR");var i=_t().state,a=i.nodeSpacing||50,o=i.rankSpacing||50,s=new G.a.Graph({multigraph:!0,compound:!0}).setGraph({rankdir:"TB",nodesep:a,ranksep:o,marginx:8,marginy:8}).setDefaultEdgeLabel((function(){return{}}));c.info(Mi.getRootDocV2()),Mi.extract(Mi.getRootDocV2()),c.info(Mi.getRootDocV2()),Hi(s,void 0,Mi.getRootDocV2(),!0);var u=Object(d.select)('[id="'.concat(e,'"]')),l=Object(d.select)("#"+e+" g");On(l,s,["barb"],"statediagram",e);var h=u.node().getBBox(),f=h.width+16,p=h.height+16;u.attr("class","statediagram");var g=u.node().getBBox();W(u,p,1.75*f,i.useMaxWidth);var y="".concat(g.x-8," ").concat(g.y-8," ").concat(f," ").concat(p);if(c.debug("viewBox ".concat(y)),u.attr("viewBox",y),!i.htmlLabels)for(var v=document.querySelectorAll('[id="'+e+'"] .edgeLabel .label'),m=0;m<v.length;m++){var b=v[m],x=b.getBBox(),_=document.createElementNS("http://www.w3.org/2000/svg","rect");_.setAttribute("rx",0),_.setAttribute("ry",0),_.setAttribute("width",x.width),_.setAttribute("height",x.height),b.insertBefore(_,b.firstChild)}},Zi={},Ji=null,Ki={master:Ji},Qi="master",ta="LR",ea=0;function na(){return I({length:7})}function ra(t,e){for(c.debug("Entering isfastforwardable:",t.id,e.id);t.seq<=e.seq&&t!==e&&null!=e.parent;){if(Array.isArray(e.parent))return c.debug("In merge commit:",e.parent),ra(t,Zi[e.parent[0]])||ra(t,Zi[e.parent[1]]);e=Zi[e.parent]}return c.debug(t.id,e.id),t.id===e.id}var ia={};function aa(t,e,n){var r=t.indexOf(e);-1===r?t.push(n):t.splice(r,1,n)}function oa(t){var e=t.reduce((function(t,e){return t.seq>e.seq?t:e}),t[0]),n="";t.forEach((function(t){n+=t===e?"\t*":"\t|"}));var r,i,a,o=[n,e.id,e.seq];for(var s in Ki)Ki[s]===e.id&&o.push(s);if(c.debug(o.join(" ")),Array.isArray(e.parent)){var u=Zi[e.parent[0]];aa(t,e,u),t.push(Zi[e.parent[1]])}else{if(null==e.parent)return;var l=Zi[e.parent];aa(t,e,l)}r=t,i=function(t){return t.id},a=Object.create(null),oa(t=r.reduce((function(t,e){var n=i(e);return a[n]||(a[n]=!0,t.push(e)),t}),[]))}var sa,ca=function(){var t=Object.keys(Zi).map((function(t){return Zi[t]}));return t.forEach((function(t){c.debug(t.id)})),t.sort((function(t,e){return e.seq-t.seq})),t},ua={setDirection:function(t){ta=t},setOptions:function(t){c.debug("options str",t),t=(t=t&&t.trim())||"{}";try{ia=JSON.parse(t)}catch(t){c.error("error while parsing gitGraph options",t.message)}},getOptions:function(){return ia},commit:function(t){var e={id:na(),message:t,seq:ea++,parent:null==Ji?null:Ji.id};Ji=e,Zi[e.id]=e,Ki[Qi]=e.id,c.debug("in pushCommit "+e.id)},branch:function(t){Ki[t]=null!=Ji?Ji.id:null,c.debug("in createBranch")},merge:function(t){var e=Zi[Ki[Qi]],n=Zi[Ki[t]];if(function(t,e){return t.seq>e.seq&&ra(e,t)}(e,n))c.debug("Already merged");else{if(ra(e,n))Ki[Qi]=Ki[t],Ji=Zi[Ki[Qi]];else{var r={id:na(),message:"merged branch "+t+" into "+Qi,seq:ea++,parent:[null==Ji?null:Ji.id,Ki[t]]};Ji=r,Zi[r.id]=r,Ki[Qi]=r.id}c.debug(Ki),c.debug("in mergeBranch")}},checkout:function(t){c.debug("in checkout");var e=Ki[Qi=t];Ji=Zi[e]},reset:function(t){c.debug("in reset",t);var e=t.split(":")[0],n=parseInt(t.split(":")[1]),r="HEAD"===e?Ji:Zi[Ki[e]];for(c.debug(r,n);n>0;)if(n--,!(r=Zi[r.parent])){var i="Critical error - unique parent commit not found during reset";throw c.error(i),i}Ji=r,Ki[Qi]=r.id},prettyPrint:function(){c.debug(Zi),oa([ca()[0]])},clear:function(){Zi={},Ki={master:Ji=null},Qi="master",ea=0},getBranchesAsObjArray:function(){var t=[];for(var e in Ki)t.push({name:e,commit:Zi[Ki[e]]});return t},getBranches:function(){return Ki},getCommits:function(){return Zi},getCommitsArray:ca,getCurrentBranch:function(){return Qi},getDirection:function(){return ta},getHead:function(){return Ji}},la=n(71),ha=n.n(la),fa={},da={nodeSpacing:150,nodeFillColor:"yellow",nodeStrokeWidth:2,nodeStrokeColor:"grey",lineStrokeWidth:4,branchOffset:50,lineColor:"grey",leftMargin:50,branchColors:["#442f74","#983351","#609732","#AA9A39"],nodeRadius:10,nodeLabel:{width:75,height:100,x:-25,y:0}},pa={};function ga(t,e,n,r){var i=D(r,d.curveBasis),a=da.branchColors[n%da.branchColors.length],o=Object(d.line)().x((function(t){return Math.round(t.x)})).y((function(t){return Math.round(t.y)})).curve(i);t.append("svg:path").attr("d",o(e)).style("stroke",a).style("stroke-width",da.lineStrokeWidth).style("fill","none")}function ya(t,e){e=e||t.node().getBBox();var n=t.node().getCTM();return{left:n.e+e.x*n.a,top:n.f+e.y*n.d,width:e.width,height:e.height}}function va(t,e,n,r,i){c.debug("svgDrawLineForCommits: ",e,n);var a=ya(t.select("#node-"+e+" circle")),o=ya(t.select("#node-"+n+" circle"));switch(r){case"LR":if(a.left-o.left>da.nodeSpacing){var s={x:a.left-da.nodeSpacing,y:o.top+o.height/2};ga(t,[s,{x:o.left+o.width,y:o.top+o.height/2}],i,"linear"),ga(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:s.y},s],i)}else ga(t,[{x:a.left,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:a.top+a.height/2},{x:a.left-da.nodeSpacing/2,y:o.top+o.height/2},{x:o.left+o.width,y:o.top+o.height/2}],i);break;case"BT":if(o.top-a.top>da.nodeSpacing){var u={x:o.left+o.width/2,y:a.top+a.height+da.nodeSpacing};ga(t,[u,{x:o.left+o.width/2,y:o.top}],i,"linear"),ga(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+a.height+da.nodeSpacing/2},{x:o.left+o.width/2,y:u.y-da.nodeSpacing/2},u],i)}else ga(t,[{x:a.left+a.width/2,y:a.top+a.height},{x:a.left+a.width/2,y:a.top+da.nodeSpacing/2},{x:o.left+o.width/2,y:o.top-da.nodeSpacing/2},{x:o.left+o.width/2,y:o.top}],i)}}function ma(t,e){return t.select(e).node().cloneNode(!0)}function ba(t,e,n,r){var i,a=Object.keys(fa).length;if("string"==typeof e)do{if(i=fa[e],c.debug("in renderCommitHistory",i.id,i.seq),t.select("#node-"+e).size()>0)return;t.append((function(){return ma(t,"#def-commit")})).attr("class","commit").attr("id",(function(){return"node-"+i.id})).attr("transform",(function(){switch(r){case"LR":return"translate("+(i.seq*da.nodeSpacing+da.leftMargin)+", "+sa*da.branchOffset+")";case"BT":return"translate("+(sa*da.branchOffset+da.leftMargin)+", "+(a-i.seq)*da.nodeSpacing+")"}})).attr("fill",da.nodeFillColor).attr("stroke",da.nodeStrokeColor).attr("stroke-width",da.nodeStrokeWidth);var o=void 0;for(var s in n)if(n[s].commit===i){o=n[s];break}o&&(c.debug("found branch ",o.name),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","branch-label").text(o.name+", ")),t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-id").text(i.id),""!==i.message&&"BT"===r&&t.select("#node-"+i.id+" p").append("xhtml:span").attr("class","commit-msg").text(", "+i.message),e=i.parent}while(e&&fa[e]);Array.isArray(e)&&(c.debug("found merge commmit",e),ba(t,e[0],n,r),sa++,ba(t,e[1],n,r),sa--)}function xa(t,e,n,r){for(r=r||0;e.seq>0&&!e.lineDrawn;)"string"==typeof e.parent?(va(t,e.id,e.parent,n,r),e.lineDrawn=!0,e=fa[e.parent]):Array.isArray(e.parent)&&(va(t,e.id,e.parent[0],n,r),va(t,e.id,e.parent[1],n,r+1),xa(t,fa[e.parent[1]],n,r+1),e.lineDrawn=!0,e=fa[e.parent[0]])}var _a,ka=function(t){pa=t},wa=function(t,e,n){try{var r=ha.a.parser;r.yy=ua,r.yy.clear(),c.debug("in gitgraph renderer",t+"\n","id:",e,n),r.parse(t+"\n"),da=Object.assign(da,pa,ua.getOptions()),c.debug("effective options",da);var i=ua.getDirection();fa=ua.getCommits();var a=ua.getBranchesAsObjArray();"BT"===i&&(da.nodeLabel.x=a.length*da.branchOffset,da.nodeLabel.width="100%",da.nodeLabel.y=-2*da.nodeRadius);var o=Object(d.select)('[id="'.concat(e,'"]'));for(var s in function(t){t.append("defs").append("g").attr("id","def-commit").append("circle").attr("r",da.nodeRadius).attr("cx",0).attr("cy",0),t.select("#def-commit").append("foreignObject").attr("width",da.nodeLabel.width).attr("height",da.nodeLabel.height).attr("x",da.nodeLabel.x).attr("y",da.nodeLabel.y).attr("class","node-label").attr("requiredFeatures","http://www.w3.org/TR/SVG11/feature#Extensibility").append("p").html("")}(o),sa=1,a){var u=a[s];ba(o,u.commit.id,a,i),xa(o,u.commit,i),sa++}o.attr("height",(function(){return"BT"===i?Object.keys(fa).length*da.nodeSpacing:(a.length+1)*da.branchOffset}))}catch(t){c.error("Error while rendering gitgraph"),c.error(t.message)}},Ea="",Ta=!1,Ca={setMessage:function(t){c.debug("Setting message to: "+t),Ea=t},getMessage:function(){return Ea},setInfo:function(t){Ta=t},getInfo:function(){return Ta}},Aa=n(72),Sa=n.n(Aa),Ma={},Oa=function(t){Object.keys(t).forEach((function(e){Ma[e]=t[e]}))},Da=function(t,e,n){try{var r=Sa.a.parser;r.yy=Ca,c.debug("Renering info diagram\n"+t),r.parse(t),c.debug("Parsed info diagram");var i=Object(d.select)("#"+e);i.append("g").append("text").attr("x",100).attr("y",40).attr("class","version").attr("font-size","32px").style("text-anchor","middle").text("v "+n),i.attr("height",100),i.attr("width",400)}catch(t){c.error("Error while rendering info diagram"),c.error(t.message)}},Na={},Ba=function(t){Object.keys(t).forEach((function(e){Na[e]=t[e]}))},La=function(t,e){try{c.debug("Renering svg for syntax error\n");var n=Object(d.select)("#"+t),r=n.append("g");r.append("path").attr("class","error-icon").attr("d","m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"),r.append("path").attr("class","error-icon").attr("d","m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"),r.append("path").attr("class","error-icon").attr("d","m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"),r.append("path").attr("class","error-icon").attr("d","m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"),r.append("path").attr("class","error-icon").attr("d","m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"),r.append("path").attr("class","error-icon").attr("d","m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"),r.append("text").attr("class","error-text").attr("x",1240).attr("y",250).attr("font-size","150px").style("text-anchor","middle").text("Syntax error in graph"),r.append("text").attr("class","error-text").attr("x",1050).attr("y",400).attr("font-size","100px").style("text-anchor","middle").text("mermaid version "+e),n.attr("height",100),n.attr("width",400),n.attr("viewBox","768 0 512 512")}catch(t){c.error("Error while rendering info diagram"),c.error(t.message)}},Pa={},Ia="",Fa={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().pie},addSection:function(t,e){void 0===Pa[t]&&(Pa[t]=e,c.debug("Added new section :",t))},getSections:function(){return Pa},cleanupValue:function(t){return":"===t.substring(0,1)?(t=t.substring(1).trim(),Number(t.trim())):Number(t.trim())},clear:function(){Pa={},Ia=""},setTitle:function(t){Ia=t},getTitle:function(){return Ia}},ja=n(73),Ra=n.n(ja),Ya={},za=function(t){Object.keys(t).forEach((function(e){Ya[e]=t[e]}))},Ua=function(t,e){try{var n=Ra.a.parser;n.yy=Fa,c.debug("Rendering info diagram\n"+t),n.yy.clear(),n.parse(t),c.debug("Parsed info diagram");var r=document.getElementById(e);void 0===(_a=r.parentElement.offsetWidth)&&(_a=1200),void 0!==Ya.useWidth&&(_a=Ya.useWidth);var i=Object(d.select)("#"+e);W(i,450,_a,Ya.useMaxWidth),r.setAttribute("viewBox","0 0 "+_a+" 450");var a=Math.min(_a,450)/2-40,o=i.append("g").attr("transform","translate("+_a/2+",225)"),s=Fa.getSections(),u=0;Object.keys(s).forEach((function(t){u+=s[t]}));var l=Object(d.scaleOrdinal)().domain(s).range(d.schemeSet2),h=Object(d.pie)().value((function(t){return t.value}))(Object(d.entries)(s)),f=Object(d.arc)().innerRadius(0).outerRadius(a);o.selectAll("mySlices").data(h).enter().append("path").attr("d",f).attr("fill",(function(t){return l(t.data.key)})).attr("stroke","black").style("stroke-width","2px").style("opacity",.7),o.selectAll("mySlices").data(h.filter((function(t){return 0!==t.data.value}))).enter().append("text").text((function(t){return(t.data.value/u*100).toFixed(0)+"%"})).attr("transform",(function(t){return"translate("+f.centroid(t)+")"})).style("text-anchor","middle").attr("class","slice").style("font-size",17),o.append("text").text(n.yy.getTitle()).attr("x",0).attr("y",-200).attr("class","pieTitleText");var p=o.selectAll(".legend").data(l.domain()).enter().append("g").attr("class","legend").attr("transform",(function(t,e){return"translate(216,"+(22*e-22*l.domain().length/2)+")"}));p.append("rect").attr("width",18).attr("height",18).style("fill",l).style("stroke",l),p.append("text").attr("x",22).attr("y",14).text((function(t){return t}))}catch(t){c.error("Error while rendering info diagram"),c.error(t)}},$a={},Wa=[],Ha="",Va=function(t){return void 0===$a[t]&&($a[t]={attributes:[]},c.info("Added new entity :",t)),$a[t]},Ga={Cardinality:{ZERO_OR_ONE:"ZERO_OR_ONE",ZERO_OR_MORE:"ZERO_OR_MORE",ONE_OR_MORE:"ONE_OR_MORE",ONLY_ONE:"ONLY_ONE"},Identification:{NON_IDENTIFYING:"NON_IDENTIFYING",IDENTIFYING:"IDENTIFYING"},parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().er},addEntity:Va,addAttributes:function(t,e){var n,r=Va(t);for(n=e.length-1;n>=0;n--)r.attributes.push(e[n]),c.debug("Added attribute ",e[n].attributeName)},getEntities:function(){return $a},addRelationship:function(t,e,n,r){var i={entityA:t,roleA:e,entityB:n,relSpec:r};Wa.push(i),c.debug("Added new relationship :",i)},getRelationships:function(){return Wa},clear:function(){$a={},Wa=[],Ha=""},setTitle:function(t){Ha=t},getTitle:function(){return Ha}},qa=n(74),Xa=n.n(qa),Za={ONLY_ONE_START:"ONLY_ONE_START",ONLY_ONE_END:"ONLY_ONE_END",ZERO_OR_ONE_START:"ZERO_OR_ONE_START",ZERO_OR_ONE_END:"ZERO_OR_ONE_END",ONE_OR_MORE_START:"ONE_OR_MORE_START",ONE_OR_MORE_END:"ONE_OR_MORE_END",ZERO_OR_MORE_START:"ZERO_OR_MORE_START",ZERO_OR_MORE_END:"ZERO_OR_MORE_END"},Ja=Za,Ka=function(t,e){var n;t.append("defs").append("marker").attr("id",Za.ONLY_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18 M15,0 L15,18"),t.append("defs").append("marker").attr("id",Za.ONLY_ONE_END).attr("refX",18).attr("refY",9).attr("markerWidth",18).attr("markerHeight",18).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,0 L3,18 M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_ONE_START).attr("refX",0).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",21).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M9,0 L9,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_ONE_END).attr("refX",30).attr("refY",9).attr("markerWidth",30).attr("markerHeight",18).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",9).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,0 L21,18"),t.append("defs").append("marker").attr("id",Za.ONE_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q 18,0 36,18 Q 18,36 0,18 M42,9 L42,27"),t.append("defs").append("marker").attr("id",Za.ONE_OR_MORE_END).attr("refX",27).attr("refY",18).attr("markerWidth",45).attr("markerHeight",36).attr("orient","auto").append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M3,9 L3,27 M9,18 Q27,0 45,18 Q27,36 9,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_MORE_START).attr("refX",18).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",48).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M0,18 Q18,0 36,18 Q18,36 0,18"),(n=t.append("defs").append("marker").attr("id",Za.ZERO_OR_MORE_END).attr("refX",39).attr("refY",18).attr("markerWidth",57).attr("markerHeight",36).attr("orient","auto")).append("circle").attr("stroke",e.stroke).attr("fill","white").attr("cx",9).attr("cy",18).attr("r",6),n.append("path").attr("stroke",e.stroke).attr("fill","none").attr("d","M21,18 Q39,0 57,18 Q39,36 21,18")},Qa={},to=function(t,e,n){var r;return Object.keys(e).forEach((function(i){var a=t.append("g").attr("id",i);r=void 0===r?i:r;var o="entity-"+i,s=a.append("text").attr("class","er entityLabel").attr("id",o).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","middle").attr("style","font-family: "+_t().fontFamily+"; font-size: "+Qa.fontSize+"px").text(i),c=function(t,e,n){var r=Qa.entityPadding/3,i=Qa.entityPadding/3,a=.85*Qa.fontSize,o=e.node().getBBox(),s=[],c=0,u=0,l=o.height+2*r,h=1;n.forEach((function(n){var i="".concat(e.node().id,"-attr-").concat(h),o=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-type")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+_t().fontFamily+"; font-size: "+a+"px").text(n.attributeType),f=t.append("text").attr("class","er entityLabel").attr("id","".concat(i,"-name")).attr("x",0).attr("y",0).attr("dominant-baseline","middle").attr("text-anchor","left").attr("style","font-family: "+_t().fontFamily+"; font-size: "+a+"px").text(n.attributeName);s.push({tn:o,nn:f});var d=o.node().getBBox(),p=f.node().getBBox();c=Math.max(c,d.width),u=Math.max(u,p.width),l+=Math.max(d.height,p.height)+2*r,h+=1}));var f={width:Math.max(Qa.minEntityWidth,Math.max(o.width+2*Qa.entityPadding,c+u+4*i)),height:n.length>0?l:Math.max(Qa.minEntityHeight,o.height+2*Qa.entityPadding)},d=Math.max(0,f.width-(c+u)-4*i);if(n.length>0){e.attr("transform","translate("+f.width/2+","+(r+o.height/2)+")");var p=o.height+2*r,g="attributeBoxOdd";s.forEach((function(e){var n=p+r+Math.max(e.tn.node().getBBox().height,e.nn.node().getBBox().height)/2;e.tn.attr("transform","translate("+i+","+n+")");var a=t.insert("rect","#"+e.tn.node().id).attr("class","er ".concat(g)).attr("fill",Qa.fill).attr("fill-opacity","100%").attr("stroke",Qa.stroke).attr("x",0).attr("y",p).attr("width",c+2*i+d/2).attr("height",e.tn.node().getBBox().height+2*r);e.nn.attr("transform","translate("+(parseFloat(a.attr("width"))+i)+","+n+")"),t.insert("rect","#"+e.nn.node().id).attr("class","er ".concat(g)).attr("fill",Qa.fill).attr("fill-opacity","100%").attr("stroke",Qa.stroke).attr("x","".concat(a.attr("x")+a.attr("width"))).attr("y",p).attr("width",u+2*i+d/2).attr("height",e.nn.node().getBBox().height+2*r),p+=Math.max(e.tn.node().getBBox().height,e.nn.node().getBBox().height)+2*r,g="attributeBoxOdd"==g?"attributeBoxEven":"attributeBoxOdd"}))}else f.height=Math.max(Qa.minEntityHeight,l),e.attr("transform","translate("+f.width/2+","+f.height/2+")");return f}(a,s,e[i].attributes),u=c.width,l=c.height,h=a.insert("rect","#"+o).attr("class","er entityBox").attr("fill",Qa.fill).attr("fill-opacity","100%").attr("stroke",Qa.stroke).attr("x",0).attr("y",0).attr("width",u).attr("height",l).node().getBBox();n.setNode(i,{width:h.width,height:h.height,shape:"rect",id:i})})),r},eo=function(t){return(t.entityA+t.roleA+t.entityB).replace(/\s/g,"")},no=0,ro=function(t){for(var e=Object.keys(t),n=0;n<e.length;n++)Qa[e[n]]=t[e[n]]},io=function(t,e){c.info("Drawing ER diagram"),Ga.clear();var n=Xa.a.parser;n.yy=Ga;try{n.parse(t)}catch(t){c.debug("Parsing failed")}var r,i=Object(d.select)("[id='".concat(e,"']"));Ka(i,Qa),r=new G.a.Graph({multigraph:!0,directed:!0,compound:!1}).setGraph({rankdir:Qa.layoutDirection,marginx:20,marginy:20,nodesep:100,edgesep:100,ranksep:100}).setDefaultEdgeLabel((function(){return{}}));var a,o,s=to(i,Ga.getEntities(),r),u=function(t,e){return t.forEach((function(t){e.setEdge(t.entityA,t.entityB,{relationship:t},eo(t))})),t}(Ga.getRelationships(),r);ke.a.layout(r),a=i,(o=r).nodes().forEach((function(t){void 0!==t&&void 0!==o.node(t)&&a.select("#"+t).attr("transform","translate("+(o.node(t).x-o.node(t).width/2)+","+(o.node(t).y-o.node(t).height/2)+" )")})),u.forEach((function(t){!function(t,e,n,r){no++;var i=n.edge(e.entityA,e.entityB,eo(e)),a=Object(d.line)().x((function(t){return t.x})).y((function(t){return t.y})).curve(d.curveBasis),o=t.insert("path","#"+r).attr("class","er relationshipLine").attr("d",a(i.points)).attr("stroke",Qa.stroke).attr("fill","none");e.relSpec.relType===Ga.Identification.NON_IDENTIFYING&&o.attr("stroke-dasharray","8,8");var s="";switch(Qa.arrowMarkerAbsolute&&(s=(s=(s=window.location.protocol+"//"+window.location.host+window.location.pathname+window.location.search).replace(/\(/g,"\\(")).replace(/\)/g,"\\)")),e.relSpec.cardA){case Ga.Cardinality.ZERO_OR_ONE:o.attr("marker-end","url("+s+"#"+Ja.ZERO_OR_ONE_END+")");break;case Ga.Cardinality.ZERO_OR_MORE:o.attr("marker-end","url("+s+"#"+Ja.ZERO_OR_MORE_END+")");break;case Ga.Cardinality.ONE_OR_MORE:o.attr("marker-end","url("+s+"#"+Ja.ONE_OR_MORE_END+")");break;case Ga.Cardinality.ONLY_ONE:o.attr("marker-end","url("+s+"#"+Ja.ONLY_ONE_END+")")}switch(e.relSpec.cardB){case Ga.Cardinality.ZERO_OR_ONE:o.attr("marker-start","url("+s+"#"+Ja.ZERO_OR_ONE_START+")");break;case Ga.Cardinality.ZERO_OR_MORE:o.attr("marker-start","url("+s+"#"+Ja.ZERO_OR_MORE_START+")");break;case Ga.Cardinality.ONE_OR_MORE:o.attr("marker-start","url("+s+"#"+Ja.ONE_OR_MORE_START+")");break;case Ga.Cardinality.ONLY_ONE:o.attr("marker-start","url("+s+"#"+Ja.ONLY_ONE_START+")")}var c=o.node().getTotalLength(),u=o.node().getPointAtLength(.5*c),l="rel"+no,h=t.append("text").attr("class","er relationshipLabel").attr("id",l).attr("x",u.x).attr("y",u.y).attr("text-anchor","middle").attr("dominant-baseline","middle").attr("style","font-family: "+_t().fontFamily+"; font-size: "+Qa.fontSize+"px").text(e.roleA).node().getBBox();t.insert("rect","#"+l).attr("class","er relationshipLabelBox").attr("x",u.x-h.width/2).attr("y",u.y-h.height/2).attr("width",h.width).attr("height",h.height).attr("fill","white").attr("fill-opacity","85%")}(i,t,r,s)}));var l=Qa.diagramPadding,h=i.node().getBBox(),f=h.width+2*l,p=h.height+2*l;W(i,p,f,Qa.useMaxWidth),i.attr("viewBox","".concat(h.x-l," ").concat(h.y-l," ").concat(f," ").concat(p))},ao=n(28),oo=n.n(ao);function so(t){return function(t){if(Array.isArray(t)){for(var e=0,n=new Array(t.length);e<t.length;e++)n[e]=t[e];return n}}(t)||function(t){if(Symbol.iterator in Object(t)||"[object Arguments]"===Object.prototype.toString.call(t))return Array.from(t)}(t)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance")}()}var co="",uo="",lo=[],ho=[],fo=[],po=function(){for(var t=!0,e=0;e<fo.length;e++)fo[e].processed,t=t&&fo[e].processed;return t},go={parseDirective:function(t,e,n){Go.parseDirective(this,t,e,n)},getConfig:function(){return _t().journey},clear:function(){lo.length=0,ho.length=0,uo="",co="",fo.length=0},setTitle:function(t){co=t},getTitle:function(){return co},addSection:function(t){uo=t,lo.push(t)},getSections:function(){return lo},getTasks:function(){for(var t=po(),e=0;!t&&e<100;)t=po(),e++;return ho.push.apply(ho,fo),ho},addTask:function(t,e){var n=e.substr(1).split(":"),r=0,i=[];1===n.length?(r=Number(n[0]),i=[]):(r=Number(n[0]),i=n[1].split(","));var a=i.map((function(t){return t.trim()})),o={section:uo,type:uo,people:a,task:t,score:r};fo.push(o)},addTaskOrg:function(t){var e={section:uo,type:uo,description:t,task:t,classes:[]};ho.push(e)},getActors:function(){return t=[],ho.forEach((function(e){e.people&&t.push.apply(t,so(e.people))})),so(new Set(t)).sort();var t}},yo=function(t,e){var n=t.append("rect");return n.attr("x",e.x),n.attr("y",e.y),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("width",e.width),n.attr("height",e.height),n.attr("rx",e.rx),n.attr("ry",e.ry),void 0!==e.class&&n.attr("class",e.class),n},vo=function(t,e){var n=t.append("circle");return n.attr("cx",e.cx),n.attr("cy",e.cy),n.attr("fill",e.fill),n.attr("stroke",e.stroke),n.attr("r",e.r),void 0!==n.class&&n.attr("class",n.class),void 0!==e.title&&n.append("title").text(e.title),n},mo=function(t,e){var n=e.text.replace(/<br\s*\/?>/gi," "),r=t.append("text");r.attr("x",e.x),r.attr("y",e.y),r.attr("class","legend"),r.style("text-anchor",e.anchor),void 0!==e.class&&r.attr("class",e.class);var i=r.append("tspan");return i.attr("x",e.x+2*e.textMargin),i.text(n),r},bo=-1,xo=function(){return{x:0,y:0,width:100,anchor:"start",height:100,rx:0,ry:0}},_o=function(){function t(t,e,n,i,a,o,s,c){r(e.append("text").attr("x",n+a/2).attr("y",i+o/2+5).style("font-color",c).style("text-anchor","middle").text(t),s)}function e(t,e,n,i,a,o,s,c,u){for(var l=c.taskFontSize,h=c.taskFontFamily,f=t.split(/<br\s*\/?>/gi),d=0;d<f.length;d++){var p=d*l-l*(f.length-1)/2,g=e.append("text").attr("x",n+a/2).attr("y",i).attr("fill",u).style("text-anchor","middle").style("font-size",l).style("font-family",h);g.append("tspan").attr("x",n+a/2).attr("dy",p).text(f[d]),g.attr("y",i+o/2).attr("dominant-baseline","central").attr("alignment-baseline","central"),r(g,s)}}function n(t,n,i,a,o,s,c,u){var l=n.append("switch"),h=l.append("foreignObject").attr("x",i).attr("y",a).attr("width",o).attr("height",s).attr("position","fixed").append("div").style("display","table").style("height","100%").style("width","100%");h.append("div").attr("class","label").style("display","table-cell").style("text-align","center").style("vertical-align","middle").text(t),e(t,l,i,a,o,s,c,u),r(h,c)}function r(t,e){for(var n in e)n in e&&t.attr(n,e[n])}return function(r){return"fo"===r.textPlacement?n:"old"===r.textPlacement?t:e}}(),ko=vo,wo=function(t,e,n){var r=t.append("g"),i=xo();i.x=e.x,i.y=e.y,i.fill=e.fill,i.width=n.width,i.height=n.height,i.class="journey-section section-type-"+e.num,i.rx=3,i.ry=3,yo(r,i),_o(n)(e.text,r,i.x,i.y,i.width,i.height,{class:"journey-section section-type-"+e.num},n,e.colour)},Eo=mo,To=function(t,e,n){var r=e.x+n.width/2,i=t.append("g");bo++;var a,o,s;i.append("line").attr("id","task"+bo).attr("x1",r).attr("y1",e.y).attr("x2",r).attr("y2",450).attr("class","task-line").attr("stroke-width","1px").attr("stroke-dasharray","4 2").attr("stroke","#666"),a=i,o={cx:r,cy:300+30*(5-e.score),score:e.score},a.append("circle").attr("cx",o.cx).attr("cy",o.cy).attr("class","face").attr("r",15).attr("stroke-width",2).attr("overflow","visible"),(s=a.append("g")).append("circle").attr("cx",o.cx-5).attr("cy",o.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),s.append("circle").attr("cx",o.cx+5).attr("cy",o.cy-5).attr("r",1.5).attr("stroke-width",2).attr("fill","#666").attr("stroke","#666"),o.score>3?function(t){var e=Object(d.arc)().startAngle(Math.PI/2).endAngle(Math.PI/2*3).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+o.cx+","+(o.cy+2)+")")}(s):o.score<3?function(t){var e=Object(d.arc)().startAngle(3*Math.PI/2).endAngle(Math.PI/2*5).innerRadius(7.5).outerRadius(15/2.2);t.append("path").attr("class","mouth").attr("d",e).attr("transform","translate("+o.cx+","+(o.cy+7)+")")}(s):function(t){t.append("line").attr("class","mouth").attr("stroke",2).attr("x1",o.cx-5).attr("y1",o.cy+7).attr("x2",o.cx+5).attr("y2",o.cy+7).attr("class","mouth").attr("stroke-width","1px").attr("stroke","#666")}(s);var c=xo();c.x=e.x,c.y=e.y,c.fill=e.fill,c.width=n.width,c.height=n.height,c.class="task task-type-"+e.num,c.rx=3,c.ry=3,yo(i,c);var u=e.x+14;e.people.forEach((function(t){var n=e.actors[t],r={cx:u,cy:e.y,r:7,fill:n,stroke:"#000",title:t};vo(i,r),u+=10})),_o(n)(e.task,i,c.x,c.y,c.width,c.height,{class:"task"},n,e.colour)},Co=function(t){t.append("defs").append("marker").attr("id","arrowhead").attr("refX",5).attr("refY",2).attr("markerWidth",6).attr("markerHeight",4).attr("orient","auto").append("path").attr("d","M 0,0 V 4 L6,2 Z")};ao.parser.yy=go;var Ao={leftMargin:150,diagramMarginX:50,diagramMarginY:20,taskMargin:50,width:150,height:50,taskFontSize:14,taskFontFamily:'"Open-Sans", "sans-serif"',boxMargin:10,boxTextMargin:5,noteMargin:10,messageMargin:35,messageAlign:"center",bottomMarginAdj:1,activationWidth:10,textPlacement:"fo",actorColours:["#8FBC8F","#7CFC00","#00FFFF","#20B2AA","#B0E0E6","#FFFFE0"],sectionFills:["#191970","#8B008B","#4B0082","#2F4F4F","#800000","#8B4513","#00008B"],sectionColours:["#fff"]},So={};var Mo=Ao.leftMargin,Oo={data:{startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},verticalPos:0,sequenceItems:[],init:function(){this.sequenceItems=[],this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0},this.verticalPos=0},updateVal:function(t,e,n,r){void 0===t[e]?t[e]=n:t[e]=r(n,t[e])},updateBounds:function(t,e,n,r){var i,a=this,o=0;this.sequenceItems.forEach((function(s){o++;var c=a.sequenceItems.length-o+1;a.updateVal(s,"starty",e-c*Ao.boxMargin,Math.min),a.updateVal(s,"stopy",r+c*Ao.boxMargin,Math.max),a.updateVal(Oo.data,"startx",t-c*Ao.boxMargin,Math.min),a.updateVal(Oo.data,"stopx",n+c*Ao.boxMargin,Math.max),"activation"!==i&&(a.updateVal(s,"startx",t-c*Ao.boxMargin,Math.min),a.updateVal(s,"stopx",n+c*Ao.boxMargin,Math.max),a.updateVal(Oo.data,"starty",e-c*Ao.boxMargin,Math.min),a.updateVal(Oo.data,"stopy",r+c*Ao.boxMargin,Math.max))}))},insert:function(t,e,n,r){var i=Math.min(t,n),a=Math.max(t,n),o=Math.min(e,r),s=Math.max(e,r);this.updateVal(Oo.data,"startx",i,Math.min),this.updateVal(Oo.data,"starty",o,Math.min),this.updateVal(Oo.data,"stopx",a,Math.max),this.updateVal(Oo.data,"stopy",s,Math.max),this.updateBounds(i,o,a,s)},bumpVerticalPos:function(t){this.verticalPos=this.verticalPos+t,this.data.stopy=this.verticalPos},getVerticalPos:function(){return this.verticalPos},getBounds:function(){return this.data}},Do=Ao.sectionFills,No=Ao.sectionColours,Bo=function(t,e,n){for(var r="",i=n+(2*Ao.height+Ao.diagramMarginY),a=0,o="#CCC",s="black",c=0,u=0;u<e.length;u++){var l=e[u];if(r!==l.section){o=Do[a%Do.length],c=a%Do.length,s=No[a%No.length];var h={x:u*Ao.taskMargin+u*Ao.width+Mo,y:50,text:l.section,fill:o,num:c,colour:s};wo(t,h,Ao),r=l.section,a++}var f=l.people.reduce((function(t,e){return So[e]&&(t[e]=So[e]),t}),{});l.x=u*Ao.taskMargin+u*Ao.width+Mo,l.y=i,l.width=Ao.diagramMarginX,l.height=Ao.diagramMarginY,l.colour=s,l.fill=o,l.num=c,l.actors=f,To(t,l,Ao),Oo.insert(l.x,l.y,l.x+l.width+Ao.taskMargin,450)}},Lo=function(t){Object.keys(t).forEach((function(e){Ao[e]=t[e]}))},Po=function(t,e){ao.parser.yy.clear(),ao.parser.parse(t+"\n"),Oo.init();var n=Object(d.select)("#"+e);n.attr("xmlns:xlink","http://www.w3.org/1999/xlink"),Co(n);var r=ao.parser.yy.getTasks(),i=ao.parser.yy.getTitle(),a=ao.parser.yy.getActors();for(var o in So)delete So[o];var s=0;a.forEach((function(t){So[t]=Ao.actorColours[s%Ao.actorColours.length],s++})),function(t){var e=60;Object.keys(So).forEach((function(n){var r=So[n];ko(t,{cx:20,cy:e,r:7,fill:r,stroke:"#000"});var i={x:40,y:e+7,fill:"#666",text:n,textMargin:5|Ao.boxTextMargin};Eo(t,i),e+=20}))}(n),Oo.insert(0,0,Mo,50*Object.keys(So).length),Bo(n,r,0);var c=Oo.getBounds();i&&n.append("text").text(i).attr("x",Mo).attr("font-size","4ex").attr("font-weight","bold").attr("y",25);var u=c.stopy-c.starty+2*Ao.diagramMarginY,l=Mo+c.stopx+2*Ao.diagramMarginX;W(n,u,l,Ao.useMaxWidth),n.append("line").attr("x1",Mo).attr("y1",4*Ao.height).attr("x2",l-Mo-4).attr("y2",4*Ao.height).attr("stroke-width",4).attr("stroke","black").attr("marker-end","url(#arrowhead)");var h=i?70:0;n.attr("viewBox","".concat(c.startx," -25 ").concat(l," ").concat(u+h)),n.attr("preserveAspectRatio","xMinYMin meet")},Io=function(t){return"g.classGroup text {\n fill: ".concat(t.nodeBorder,";\n fill: ").concat(t.classText,";\n stroke: none;\n font-family: ").concat(t.fontFamily,";\n font-size: 10px;\n\n .title {\n font-weight: bolder;\n }\n\n}\n\n.classTitle {\n font-weight: bolder;\n}\n.node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n\n.divider {\n stroke: ").concat(t.nodeBorder,";\n stroke: 1;\n}\n\ng.clickable {\n cursor: pointer;\n}\n\ng.classGroup rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n}\n\ng.classGroup line {\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1;\n}\n\n.classLabel .box {\n stroke: none;\n stroke-width: 0;\n fill: ").concat(t.mainBkg,";\n opacity: 0.5;\n}\n\n.classLabel .label {\n fill: ").concat(t.nodeBorder,";\n font-size: 10px;\n}\n\n.relation {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n fill: none;\n}\n\n.dashed-line{\n stroke-dasharray: 3;\n}\n\n#compositionStart, .composition {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#compositionEnd, .composition {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#dependencyStart, .dependency {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#extensionStart, .extension {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#extensionEnd, .extension {\n fill: ").concat(t.lineColor," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#aggregationStart, .aggregation {\n fill: ").concat(t.mainBkg," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n#aggregationEnd, .aggregation {\n fill: ").concat(t.mainBkg," !important;\n stroke: ").concat(t.lineColor," !important;\n stroke-width: 1;\n}\n\n.edgeTerminals {\n font-size: 11px;\n}\n\n")},Fo=function(t){return".label {\n font-family: ".concat(t.fontFamily,";\n color: ").concat(t.nodeTextColor||t.textColor,";\n }\n\n .label text {\n fill: ").concat(t.nodeTextColor||t.textColor,";\n }\n\n .node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n .node .label {\n text-align: center;\n }\n .node.clickable {\n cursor: pointer;\n }\n\n .arrowheadPath {\n fill: ").concat(t.arrowheadColor,";\n }\n\n .edgePath .path {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1.5px;\n }\n\n .flowchart-link {\n stroke: ").concat(t.lineColor,";\n fill: none;\n }\n\n .edgeLabel {\n background-color: ").concat(t.edgeLabelBackground,";\n rect {\n opacity: 0.5;\n background-color: ").concat(t.edgeLabelBackground,";\n fill: ").concat(t.edgeLabelBackground,";\n }\n text-align: center;\n }\n\n .cluster rect {\n fill: ").concat(t.clusterBkg,";\n stroke: ").concat(t.clusterBorder,";\n stroke-width: 1px;\n }\n\n .cluster text {\n fill: ").concat(t.titleColor,";\n }\n\n div.mermaidTooltip {\n position: absolute;\n text-align: center;\n max-width: 200px;\n padding: 2px;\n font-family: ").concat(t.fontFamily,";\n font-size: 12px;\n background: ").concat(t.tertiaryColor,";\n border: 1px solid ").concat(t.border2,";\n border-radius: 2px;\n pointer-events: none;\n z-index: 100;\n }\n")},jo=function(t){return"g.stateGroup text {\n fill: ".concat(t.nodeBorder,";\n stroke: none;\n font-size: 10px;\n}\ng.stateGroup text {\n fill: ").concat(t.textColor,";\n stroke: none;\n font-size: 10px;\n\n}\ng.stateGroup .state-title {\n font-weight: bolder;\n fill: ").concat(t.labelColor,";\n}\n\ng.stateGroup rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n}\n\ng.stateGroup line {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n}\n\n.transition {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n fill: none;\n}\n\n.stateGroup .composit {\n fill: ").concat(t.background,";\n border-bottom: 1px\n}\n\n.stateGroup .alt-composit {\n fill: #e0e0e0;\n border-bottom: 1px\n}\n\n.state-note {\n stroke: ").concat(t.noteBorderColor,";\n fill: ").concat(t.noteBkgColor,";\n\n text {\n fill: black;\n stroke: none;\n font-size: 10px;\n }\n}\n\n.stateLabel .box {\n stroke: none;\n stroke-width: 0;\n fill: ").concat(t.mainBkg,";\n opacity: 0.5;\n}\n\n.edgeLabel .label rect {\n fill: ").concat(t.tertiaryColor,";\n opacity: 0.5;\n}\n.edgeLabel .label text {\n fill: ").concat(t.tertiaryTextColor,";\n}\n.label div .edgeLabel {\n color: ").concat(t.tertiaryTextColor,";\n}\n\n.stateLabel text {\n fill: ").concat(t.labelColor,";\n font-size: 10px;\n font-weight: bold;\n}\n\n.node circle.state-start {\n fill: ").concat(t.lineColor,";\n stroke: black;\n}\n.node circle.state-end {\n fill: ").concat(t.primaryBorderColor,";\n stroke: ").concat(t.background,";\n stroke-width: 1.5\n}\n.end-state-inner {\n fill: ").concat(t.background,";\n // stroke: ").concat(t.background,";\n stroke-width: 1.5\n}\n\n.node rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n}\n#statediagram-barbEnd {\n fill: ").concat(t.lineColor,";\n}\n\n.statediagram-cluster rect {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n}\n\n.cluster-label, .nodeLabel {\n color: ").concat(t.textColor,";\n}\n\n.statediagram-cluster rect.outer {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-state .divider {\n stroke: ").concat(t.nodeBorder,";\n}\n\n.statediagram-state .title-state {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-cluster.statediagram-cluster .inner {\n fill: ").concat(t.background,";\n}\n.statediagram-cluster.statediagram-cluster-alt .inner {\n fill: #e0e0e0;\n}\n\n.statediagram-cluster .inner {\n rx:0;\n ry:0;\n}\n\n.statediagram-state rect.basic {\n rx: 5px;\n ry: 5px;\n}\n.statediagram-state rect.divider {\n stroke-dasharray: 10,10;\n fill: ").concat(t.altBackground?t.altBackground:"#efefef",";\n}\n\n.note-edge {\n stroke-dasharray: 5;\n}\n\n.statediagram-note rect {\n fill: ").concat(t.noteBkgColor,";\n stroke: ").concat(t.noteBorderColor,";\n stroke-width: 1px;\n rx: 0;\n ry: 0;\n}\n.statediagram-note rect {\n fill: ").concat(t.noteBkgColor,";\n stroke: ").concat(t.noteBorderColor,";\n stroke-width: 1px;\n rx: 0;\n ry: 0;\n}\n\n.statediagram-note text {\n fill: ").concat(t.noteTextColor,";\n}\n\n.statediagram-note .nodeLabel {\n color: ").concat(t.noteTextColor,";\n}\n\n#dependencyStart, #dependencyEnd {\n fill: ").concat(t.lineColor,";\n stroke: ").concat(t.lineColor,";\n stroke-width: 1;\n}\n")},Ro={flowchart:Fo,"flowchart-v2":Fo,sequence:function(t){return".actor {\n stroke: ".concat(t.actorBorder,";\n fill: ").concat(t.actorBkg,";\n }\n\n text.actor > tspan {\n fill: ").concat(t.actorTextColor,";\n stroke: none;\n }\n\n .actor-line {\n stroke: ").concat(t.actorLineColor,";\n }\n\n .messageLine0 {\n stroke-width: 1.5;\n stroke-dasharray: none;\n stroke: ").concat(t.signalColor,";\n }\n\n .messageLine1 {\n stroke-width: 1.5;\n stroke-dasharray: 2, 2;\n stroke: ").concat(t.signalColor,";\n }\n\n #arrowhead path {\n fill: ").concat(t.signalColor,";\n stroke: ").concat(t.signalColor,";\n }\n\n .sequenceNumber {\n fill: ").concat(t.sequenceNumberColor,";\n }\n\n #sequencenumber {\n fill: ").concat(t.signalColor,";\n }\n\n #crosshead path {\n fill: ").concat(t.signalColor,";\n stroke: ").concat(t.signalColor,";\n }\n\n .messageText {\n fill: ").concat(t.signalTextColor,";\n stroke: ").concat(t.signalTextColor,";\n }\n\n .labelBox {\n stroke: ").concat(t.labelBoxBorderColor,";\n fill: ").concat(t.labelBoxBkgColor,";\n }\n\n .labelText, .labelText > tspan {\n fill: ").concat(t.labelTextColor,";\n stroke: none;\n }\n\n .loopText, .loopText > tspan {\n fill: ").concat(t.loopTextColor,";\n stroke: none;\n }\n\n .loopLine {\n stroke-width: 2px;\n stroke-dasharray: 2, 2;\n stroke: ").concat(t.labelBoxBorderColor,";\n fill: ").concat(t.labelBoxBorderColor,";\n }\n\n .note {\n //stroke: #decc93;\n stroke: ").concat(t.noteBorderColor,";\n fill: ").concat(t.noteBkgColor,";\n }\n\n .noteText, .noteText > tspan {\n fill: ").concat(t.noteTextColor,";\n stroke: none;\n }\n\n .activation0 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n\n .activation1 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n\n .activation2 {\n fill: ").concat(t.activationBkgColor,";\n stroke: ").concat(t.activationBorderColor,";\n }\n")},gantt:function(t){return'\n .mermaid-main-font {\n font-family: "trebuchet ms", verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n\n .section {\n stroke: none;\n opacity: 0.2;\n }\n\n .section0 {\n fill: '.concat(t.sectionBkgColor,";\n }\n\n .section2 {\n fill: ").concat(t.sectionBkgColor2,";\n }\n\n .section1,\n .section3 {\n fill: ").concat(t.altSectionBkgColor,";\n opacity: 0.2;\n }\n\n .sectionTitle0 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle1 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle2 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle3 {\n fill: ").concat(t.titleColor,";\n }\n\n .sectionTitle {\n text-anchor: start;\n font-size: 11px;\n text-height: 14px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n\n }\n\n\n /* Grid and axis */\n\n .grid .tick {\n stroke: ").concat(t.gridColor,";\n opacity: 0.8;\n shape-rendering: crispEdges;\n text {\n font-family: ").concat(t.fontFamily,";\n fill: ").concat(t.textColor,";\n }\n }\n\n .grid path {\n stroke-width: 0;\n }\n\n\n /* Today line */\n\n .today {\n fill: none;\n stroke: ").concat(t.todayLineColor,";\n stroke-width: 2px;\n }\n\n\n /* Task styling */\n\n /* Default task */\n\n .task {\n stroke-width: 2;\n }\n\n .taskText {\n text-anchor: middle;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n\n .taskText:not([font-size]) {\n font-size: 11px;\n }\n\n .taskTextOutsideRight {\n fill: ").concat(t.taskTextDarkColor,";\n text-anchor: start;\n font-size: 11px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n\n }\n\n .taskTextOutsideLeft {\n fill: ").concat(t.taskTextDarkColor,";\n text-anchor: end;\n font-size: 11px;\n }\n\n /* Special case clickable */\n .task.clickable {\n cursor: pointer;\n }\n .taskText.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n .taskTextOutsideLeft.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n .taskTextOutsideRight.clickable {\n cursor: pointer;\n fill: ").concat(t.taskTextClickableColor," !important;\n font-weight: bold;\n }\n\n /* Specific task settings for the sections*/\n\n .taskText0,\n .taskText1,\n .taskText2,\n .taskText3 {\n fill: ").concat(t.taskTextColor,";\n }\n\n .task0,\n .task1,\n .task2,\n .task3 {\n fill: ").concat(t.taskBkgColor,";\n stroke: ").concat(t.taskBorderColor,";\n }\n\n .taskTextOutside0,\n .taskTextOutside2\n {\n fill: ").concat(t.taskTextOutsideColor,";\n }\n\n .taskTextOutside1,\n .taskTextOutside3 {\n fill: ").concat(t.taskTextOutsideColor,";\n }\n\n\n /* Active task */\n\n .active0,\n .active1,\n .active2,\n .active3 {\n fill: ").concat(t.activeTaskBkgColor,";\n stroke: ").concat(t.activeTaskBorderColor,";\n }\n\n .activeText0,\n .activeText1,\n .activeText2,\n .activeText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n\n /* Completed task */\n\n .done0,\n .done1,\n .done2,\n .done3 {\n stroke: ").concat(t.doneTaskBorderColor,";\n fill: ").concat(t.doneTaskBkgColor,";\n stroke-width: 2;\n }\n\n .doneText0,\n .doneText1,\n .doneText2,\n .doneText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n\n /* Tasks on the critical line */\n\n .crit0,\n .crit1,\n .crit2,\n .crit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.critBkgColor,";\n stroke-width: 2;\n }\n\n .activeCrit0,\n .activeCrit1,\n .activeCrit2,\n .activeCrit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.activeTaskBkgColor,";\n stroke-width: 2;\n }\n\n .doneCrit0,\n .doneCrit1,\n .doneCrit2,\n .doneCrit3 {\n stroke: ").concat(t.critBorderColor,";\n fill: ").concat(t.doneTaskBkgColor,";\n stroke-width: 2;\n cursor: pointer;\n shape-rendering: crispEdges;\n }\n\n .milestone {\n transform: rotate(45deg) scale(0.8,0.8);\n }\n\n .milestoneText {\n font-style: italic;\n }\n .doneCritText0,\n .doneCritText1,\n .doneCritText2,\n .doneCritText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n .activeCritText0,\n .activeCritText1,\n .activeCritText2,\n .activeCritText3 {\n fill: ").concat(t.taskTextDarkColor," !important;\n }\n\n .titleText {\n text-anchor: middle;\n font-size: 18px;\n fill: ").concat(t.textColor," ;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n")},classDiagram:Io,"classDiagram-v2":Io,class:Io,stateDiagram:jo,state:jo,git:function(){return"\n .commit-id,\n .commit-msg,\n .branch-label {\n fill: lightgrey;\n color: lightgrey;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n }\n"},info:function(){return""},pie:function(t){return".pieTitleText {\n text-anchor: middle;\n font-size: 25px;\n fill: ".concat(t.taskTextDarkColor,";\n font-family: ").concat(t.fontFamily,";\n }\n .slice {\n font-family: ").concat(t.fontFamily,";\n fill: ").concat(t.textColor,";\n // fill: white;\n }\n .legend text {\n fill: ").concat(t.taskTextDarkColor,";\n font-family: ").concat(t.fontFamily,";\n font-size: 17px;\n }\n")},er:function(t){return"\n .entityBox {\n fill: ".concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n }\n\n .attributeBoxOdd {\n fill: #ffffff;\n stroke: ").concat(t.nodeBorder,";\n }\n\n .attributeBoxEven {\n fill: #f2f2f2;\n stroke: ").concat(t.nodeBorder,";\n }\n\n .relationshipLabelBox {\n fill: ").concat(t.tertiaryColor,";\n opacity: 0.7;\n background-color: ").concat(t.tertiaryColor,";\n rect {\n opacity: 0.5;\n }\n }\n\n .relationshipLine {\n stroke: ").concat(t.lineColor,";\n }\n")},journey:function(t){return".label {\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n color: ".concat(t.textColor,";\n }\n .mouth {\n stroke: #666;\n }\n\n line {\n stroke: ").concat(t.textColor,"\n }\n\n .legend {\n fill: ").concat(t.textColor,";\n }\n\n .label text {\n fill: #333;\n }\n .label {\n color: ").concat(t.textColor,"\n }\n\n .face {\n fill: #FFF8DC;\n stroke: #999;\n }\n\n .node rect,\n .node circle,\n .node ellipse,\n .node polygon,\n .node path {\n fill: ").concat(t.mainBkg,";\n stroke: ").concat(t.nodeBorder,";\n stroke-width: 1px;\n }\n\n .node .label {\n text-align: center;\n }\n .node.clickable {\n cursor: pointer;\n }\n\n .arrowheadPath {\n fill: ").concat(t.arrowheadColor,";\n }\n\n .edgePath .path {\n stroke: ").concat(t.lineColor,";\n stroke-width: 1.5px;\n }\n\n .flowchart-link {\n stroke: ").concat(t.lineColor,";\n fill: none;\n }\n\n .edgeLabel {\n background-color: ").concat(t.edgeLabelBackground,";\n rect {\n opacity: 0.5;\n }\n text-align: center;\n }\n\n .cluster rect {\n }\n\n .cluster text {\n fill: ").concat(t.titleColor,";\n }\n\n div.mermaidTooltip {\n position: absolute;\n text-align: center;\n max-width: 200px;\n padding: 2px;\n font-family: 'trebuchet ms', verdana, arial, sans-serif;\n font-family: var(--mermaid-font-family);\n font-size: 12px;\n background: ").concat(t.tertiaryColor,";\n border: 1px solid ").concat(t.border2,";\n border-radius: 2px;\n pointer-events: none;\n z-index: 100;\n }\n\n .task-type-0, .section-type-0 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType0):"",";\n }\n .task-type-1, .section-type-1 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType1):"",";\n }\n .task-type-2, .section-type-2 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType2):"",";\n }\n .task-type-3, .section-type-3 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType3):"",";\n }\n .task-type-4, .section-type-4 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType4):"",";\n }\n .task-type-5, .section-type-5 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType5):"",";\n }\n .task-type-6, .section-type-6 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType6):"",";\n }\n .task-type-7, .section-type-7 {\n ").concat(t.fillType0?"fill: ".concat(t.fillType7):"",";\n }\n")}},Yo=function(t,e,n){return" {\n font-family: ".concat(n.fontFamily,";\n font-size: ").concat(n.fontSize,";\n fill: ").concat(n.textColor,"\n }\n\n /* Classes common for multiple diagrams */\n\n .error-icon {\n fill: ").concat(n.errorBkgColor,";\n }\n .error-text {\n fill: ").concat(n.errorTextColor,";\n stroke: ").concat(n.errorTextColor,";\n }\n\n .edge-thickness-normal {\n stroke-width: 2px;\n }\n .edge-thickness-thick {\n stroke-width: 3.5px\n }\n .edge-pattern-solid {\n stroke-dasharray: 0;\n }\n\n .edge-pattern-dashed{\n stroke-dasharray: 3;\n }\n .edge-pattern-dotted {\n stroke-dasharray: 2;\n }\n\n .marker {\n fill: ").concat(n.lineColor,";\n }\n .marker.cross {\n stroke: ").concat(n.lineColor,";\n }\n\n svg {\n font-family: ").concat(n.fontFamily,";\n font-size: ").concat(n.fontSize,";\n }\n\n ").concat(Ro[t](n),"\n\n ").concat(e,"\n\n ").concat(t," { fill: apa;}\n")};function zo(t){return(zo="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var Uo={},$o=function(t,e,n){switch(c.debug("Directive type=".concat(e.type," with args:"),e.args),e.type){case"init":case"initialize":["config"].forEach((function(t){void 0!==e.args[t]&&("flowchart-v2"===n&&(n="flowchart"),e.args[n]=e.args[t],delete e.args[t])})),e.args,wt(e.args);break;case"wrap":case"nowrap":t&&t.setWrap&&t.setWrap("wrap"===e.type);break;default:c.warn("Unhandled directive: source: '%%{".concat(e.type,": ").concat(JSON.stringify(e.args?e.args:{}),"}%%"),e)}};function Wo(t){ka(t.git),me(t.flowchart),Ln(t.flowchart),void 0!==t.sequenceDiagram&&_r.setConf(F(t.sequence,t.sequenceDiagram)),_r.setConf(t.sequence),ri(t.gantt),li(t.class),zi(t.state),qi(t.state),Oa(t.class),za(t.class),ro(t.er),Lo(t.journey),Ba(t.class)}function Ho(){}var Vo=Object.freeze({render:function(t,e,n,r){Et();var i=e,a=H.detectInit(i);a&&wt(a);var o=_t();if(e.length>o.maxTextSize&&(i="graph TB;a[Maximum text size in diagram exceeded];style a fill:#faa"),void 0!==r)r.innerHTML="",Object(d.select)(r).append("div").attr("id","d"+t).attr("style","font-family: "+o.fontFamily).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g");else{var s=document.getElementById(t);s&&s.remove();var u=document.querySelector("#d"+t);u&&u.remove(),Object(d.select)("body").append("div").attr("id","d"+t).append("svg").attr("id",t).attr("width","100%").attr("xmlns","http://www.w3.org/2000/svg").append("g")}window.txt=i,i=function(t){var e=t;return e=(e=(e=e.replace(/style.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)}))).replace(/classDef.*:\S*#.*;/g,(function(t){return t.substring(0,t.length-1)}))).replace(/#\w+;/g,(function(t){var e=t.substring(1,t.length-1);return/^\+?\d+$/.test(e)?"fl°°"+e+"¶ß":"fl°"+e+"¶ß"}))}(i);var l=Object(d.select)("#d"+t).node(),h=H.detectType(i),g=l.firstChild,y=g.firstChild,v="";if(void 0!==o.themeCSS&&(v+="\n".concat(o.themeCSS)),void 0!==o.fontFamily&&(v+="\n:root { --mermaid-font-family: ".concat(o.fontFamily,"}")),void 0!==o.altFontFamily&&(v+="\n:root { --mermaid-alt-font-family: ".concat(o.altFontFamily,"}")),"flowchart"===h||"flowchart-v2"===h||"graph"===h){var m=be(i);for(var b in m)v+="\n.".concat(b," > * { ").concat(m[b].styles.join(" !important; ")," !important; }"),m[b].textStyles&&(v+="\n.".concat(b," tspan { ").concat(m[b].textStyles.join(" !important; ")," !important; }"))}var x=(new f.a)("#".concat(t),Yo(h,v,o.themeVariables)),_=document.createElement("style");_.innerHTML=x,g.insertBefore(_,y);try{switch(h){case"git":o.flowchart.arrowMarkerAbsolute=o.arrowMarkerAbsolute,ka(o.git),wa(i,t,!1);break;case"flowchart":o.flowchart.arrowMarkerAbsolute=o.arrowMarkerAbsolute,me(o.flowchart),xe(i,t,!1);break;case"flowchart-v2":o.flowchart.arrowMarkerAbsolute=o.arrowMarkerAbsolute,Ln(o.flowchart),Pn(i,t,!1);break;case"sequence":o.sequence.arrowMarkerAbsolute=o.arrowMarkerAbsolute,o.sequenceDiagram?(_r.setConf(Object.assign(o.sequence,o.sequenceDiagram)),console.error("`mermaid config.sequenceDiagram` has been renamed to `config.sequence`. Please update your mermaid config.")):_r.setConf(o.sequence),_r.draw(i,t);break;case"gantt":o.gantt.arrowMarkerAbsolute=o.arrowMarkerAbsolute,ri(o.gantt),ii(i,t);break;case"class":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,li(o.class),hi(i,t);break;case"classDiagram":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,di(o.class),pi(i,t);break;case"state":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,zi(o.state),Ui(i,t);break;case"stateDiagram":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,qi(o.state),Xi(i,t);break;case"info":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,Oa(o.class),Da(i,t,p.version);break;case"pie":o.class.arrowMarkerAbsolute=o.arrowMarkerAbsolute,za(o.pie),Ua(i,t,p.version);break;case"er":ro(o.er),io(i,t,p.version);break;case"journey":Lo(o.journey),Po(i,t,p.version)}}catch(e){throw La(t,p.version),e}Object(d.select)('[id="'.concat(t,'"]')).selectAll("foreignobject > *").attr("xmlns","http://www.w3.org/1999/xhtml");var k=Object(d.select)("#d"+t).node().innerHTML;if(c.debug("cnf.arrowMarkerAbsolute",o.arrowMarkerAbsolute),o.arrowMarkerAbsolute&&"false"!==o.arrowMarkerAbsolute||(k=k.replace(/marker-end="url\(.*?#/g,'marker-end="url(#',"g")),k=(k=function(t){var e=t;return e=(e=(e=e.replace(/fl°°/g,(function(){return"&#"}))).replace(/fl°/g,(function(){return"&"}))).replace(/¶ß/g,(function(){return";"}))}(k)).replace(/<br>/g,"<br/>"),void 0!==n)switch(h){case"flowchart":case"flowchart-v2":n(k,Xt.bindFunctions);break;case"gantt":n(k,Qr.bindFunctions);break;case"class":case"classDiagram":n(k,cn.bindFunctions);break;default:n(k)}else c.debug("CB = undefined!");var w=Object(d.select)("#d"+t).node();return null!==w&&"function"==typeof w.remove&&Object(d.select)("#d"+t).node().remove(),k},parse:function(t){var e=H.detectInit(t);e&&c.debug("reinit ",e);var n,r=H.detectType(t);switch(c.debug("Type "+r),r){case"git":(n=ha.a).parser.yy=ua;break;case"flowchart":case"flowchart-v2":Xt.clear(),(n=Jt.a).parser.yy=Xt;break;case"sequence":(n=Hn.a).parser.yy=sr;break;case"gantt":(n=wr.a).parser.yy=Qr;break;case"class":case"classDiagram":(n=oi.a).parser.yy=cn;break;case"state":case"stateDiagram":(n=Di.a).parser.yy=Mi;break;case"info":c.debug("info info info"),(n=Sa.a).parser.yy=Ca;break;case"pie":c.debug("pie"),(n=Ra.a).parser.yy=Fa;break;case"er":c.debug("er"),(n=Xa.a).parser.yy=Ga;break;case"journey":c.debug("Journey"),(n=oo.a).parser.yy=go}return n.parser.yy.graphType=r,n.parser.yy.parseError=function(t,e){throw{str:t,hash:e}},n.parse(t),n},parseDirective:function(t,e,n,r){try{if(void 0!==e)switch(e=e.trim(),n){case"open_directive":Uo={};break;case"type_directive":Uo.type=e.toLowerCase();break;case"arg_directive":Uo.args=JSON.parse(e);break;case"close_directive":$o(t,Uo,r),Uo=null}}catch(t){c.error("Error while rendering sequenceDiagram directive: ".concat(e," jison context: ").concat(n)),c.error(t.message)}},initialize:function(t){t&&t.fontFamily&&(t.themeVariables&&t.themeVariables.fontFamily||(t.themeVariables={fontFamily:t.fontFamily})),dt=F({},t),t&&t.theme&&ht[t.theme]?t.themeVariables=ht[t.theme].getThemeVariables(t.themeVariables):t&&(t.themeVariables=ht.default.getThemeVariables(t.themeVariables));var e="object"===zo(t)?function(t){return yt=F({},gt),yt=F(yt,t),t.theme&&(yt.themeVariables=ht[t.theme].getThemeVariables(t.themeVariables)),mt=bt(yt,vt),yt}(t):xt();Wo(e),u(e.logLevel)},reinitialize:Ho,getConfig:_t,setConfig:function(t){return F(mt,t),_t()},getSiteConfig:xt,updateSiteConfig:function(t){return yt=F(yt,t),bt(yt,vt),yt},reset:function(){Et()},globalReset:function(){Et(),Wo(_t())},defaultConfig:gt});u(_t().logLevel),Et(_t());var Go=Vo,qo=function(){Xo.startOnLoad?Go.getConfig().startOnLoad&&Xo.init():void 0===Xo.startOnLoad&&(c.debug("In start, no config"),Go.getConfig().startOnLoad&&Xo.init())};"undefined"!=typeof document&& +/*! + * Wait for document loaded before starting the execution + */ +window.addEventListener("load",(function(){qo()}),!1);var Xo={startOnLoad:!0,htmlLabels:!0,mermaidAPI:Go,parse:Go.parse,render:Go.render,init:function(){var t,e,n=this,r=Go.getConfig();arguments.length>=2?( +/*! sequence config was passed as #1 */ +void 0!==arguments[0]&&(Xo.sequenceConfig=arguments[0]),t=arguments[1]):t=arguments[0],"function"==typeof arguments[arguments.length-1]?(e=arguments[arguments.length-1],c.debug("Callback function found")):void 0!==r.mermaid&&("function"==typeof r.mermaid.callback?(e=r.mermaid.callback,c.debug("Callback function found")):c.debug("No Callback function found")),t=void 0===t?document.querySelectorAll(".mermaid"):"string"==typeof t?document.querySelectorAll(t):t instanceof window.Node?[t]:t,c.debug("Start On Load before: "+Xo.startOnLoad),void 0!==Xo.startOnLoad&&(c.debug("Start On Load inner: "+Xo.startOnLoad),Go.updateSiteConfig({startOnLoad:Xo.startOnLoad})),void 0!==Xo.ganttConfig&&Go.updateSiteConfig({gantt:Xo.ganttConfig});for(var a,o=H.initIdGeneratior(r.deterministicIds,r.deterministicIDSeed).next,s=function(r){var s=t[r]; +/*! Check if previously processed */if(s.getAttribute("data-processed"))return"continue";s.setAttribute("data-processed",!0);var u="mermaid-".concat(o());a=i(a=s.innerHTML).trim().replace(/<br\s*\/?>/gi,"<br/>");var l=H.detectInit(a);l&&c.debug("Detected early reinit: ",l);try{Go.render(u,a,(function(t,n){s.innerHTML=t,void 0!==e&&e(u),n&&n(s)}),s)}catch(t){c.warn("Syntax Error rendering"),c.warn(t),n.parseError&&n.parseError(t)}},u=0;u<t.length;u++)s(u)},initialize:function(t){void 0!==t.mermaid&&(void 0!==t.mermaid.startOnLoad&&(Xo.startOnLoad=t.mermaid.startOnLoad),void 0!==t.mermaid.htmlLabels&&(Xo.htmlLabels=t.mermaid.htmlLabels)),Go.initialize(t)},contentLoaded:qo};e.default=Xo}]).default})); +//# sourceMappingURL=mermaid.min.js.map \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml new file mode 100644 index 00000000..a5333746 --- /dev/null +++ b/docs/sitemap.xml @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" + xmlns:xhtml="http://www.w3.org/1999/xhtml"> + <url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/acknowledgements/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/getting-started/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/getting-started/windows/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/issues-contributing/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexc/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacearmamexcpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/control-estimation/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/categories/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__control__masks/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/group__defaults/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_ctrl_8cpp-example/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_glds_du_plds_ctrl_8cpp-example/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_ctrl_8cpp-example/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_est_8cpp-example/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/eg_plds_switched_ctrl_8cpp-example/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d28a4824dc47e487b107a5db32ef43c4/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/examples/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__ctrl_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__glds__du__plds__ctrl_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__ctrl_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__est_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/eg__plds__switched__ctrl_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_glds_control/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_d44c64559bbebec7f509842c48db8b23/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_controller/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_e_m/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_fit/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1gaussian/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_controller/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_e_m/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_fit_s_s_i_d/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_switched_controller/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1gaussian_1_1_system/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacelds_1_1poisson/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_controller/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_e_m/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_fit_s_s_i_d/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_switched_controller/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1poisson_1_1_system/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s_i_d/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_switched_controller/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_system/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_matrix_list/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_system_list/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/classes/classlds_1_1_uniform_vector_list/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_156a98879751e549d6939ca71a62d61f/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__ctrl_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__em_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit__ssid_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__fit_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__ctrl_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__em_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit__ssid_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__fit_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sctrl_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__ctrl_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__em_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit__ssid_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__fit_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sctrl_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sctrl_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__mats_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__systems_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__c__util_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/mex__cpp__util_8h/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/terminology/model/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/modules/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/pages/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/dir_68267d1309a1af8e8297ef4c3efbcdba/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__gaussian__sys_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__poisson__sys_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__sys_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds__uniform__vecs_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/files/lds_8cpp/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/docs/api/namespaces/namespacestd/</loc> + </url><url> + <loc>https://stanley-rozell.github.io/lds-ctrl-est/tags/</loc> + </url> +</urlset> diff --git a/docs/svg/calendar.svg b/docs/svg/calendar.svg new file mode 100644 index 00000000..f8481120 --- /dev/null +++ b/docs/svg/calendar.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z"/><path fill="none" d="M0 0h24v24H0z"/></svg> \ No newline at end of file diff --git a/docs/svg/edit.svg b/docs/svg/edit.svg new file mode 100644 index 00000000..5b54e693 --- /dev/null +++ b/docs/svg/edit.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z"/><path d="M0 0h24v24H0z" fill="none"/></svg> \ No newline at end of file diff --git a/docs/svg/menu.svg b/docs/svg/menu.svg new file mode 100644 index 00000000..770b1923 --- /dev/null +++ b/docs/svg/menu.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg> \ No newline at end of file diff --git a/docs/svg/toc.svg b/docs/svg/toc.svg new file mode 100644 index 00000000..1889904e --- /dev/null +++ b/docs/svg/toc.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z"/><path d="M0 0h24v24H0z" fill="none"/></svg> \ No newline at end of file diff --git a/docs/svg/translate.svg b/docs/svg/translate.svg new file mode 100644 index 00000000..a1bbe166 --- /dev/null +++ b/docs/svg/translate.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M12.87 15.07l-2.54-2.51.03-.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"/></svg> \ No newline at end of file diff --git a/docs/tags/index.html b/docs/tags/index.html new file mode 100644 index 00000000..5cd3e2d6 --- /dev/null +++ b/docs/tags/index.html @@ -0,0 +1,269 @@ +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<meta name="description" content=""> +<meta name="theme-color" content="#FFFFFF"><meta property="og:url" content="https://stanley-rozell.github.io/lds-ctrl-est/tags/"> + <meta property="og:site_name" content="LDS C&E"> + <meta property="og:title" content="Tags"> + <meta property="og:locale" content="en"> + <meta property="og:type" content="website"> +<title>Tags | LDS C&amp;E</title> +<link rel="manifest" href="/lds-ctrl-est/manifest.json"> +<link rel="icon" href="/lds-ctrl-est/favicon.png" type="image/x-icon"> +<link rel="stylesheet" href="/lds-ctrl-est/book.min.57f7f660871517a5bfcfb5e2de853d806f7e34d94ebd5f3f3bad62e9ddbae209.css" integrity="sha256-V/f2YIcVF6W/z7Xi3oU9gG9&#43;NNlOvV8/O61i6d264gk=" crossorigin="anonymous"> + <script defer src="/lds-ctrl-est/flexsearch.min.js"></script> + <script defer src="/lds-ctrl-est/en.search.min.4f6b617641b714cbf70266302fdeb12a9567eb52ebfb017533f9c9af1b260392.js" integrity="sha256-T2thdkG3FMv3AmYwL96xKpVn61Lr&#43;wF1M/nJrxsmA5I=" crossorigin="anonymous"></script> +<link rel="alternate" type="application/rss+xml" href="https://stanley-rozell.github.io/lds-ctrl-est/tags/index.xml" title="LDS C&E" /> +<!-- +Made with Book Theme +https://github.com/alex-shpak/hugo-book +--> + +</head> +<body dir="ltr"> + <input type="checkbox" class="hidden toggle" id="menu-control" /> + <input type="checkbox" class="hidden toggle" id="toc-control" /> + <main class="container flex"> + <aside class="book-menu"> + <div class="book-menu-content"> + + <nav> +<h2 class="book-brand"> + <a href="/lds-ctrl-est/"><img src="/lds-ctrl-est/ldsctrlest-logo.png" alt="Logo" /><span>LDS C&amp;E</span> + </a> +</h2> + + +<div class="book-search"> + <input type="text" id="book-search-input" placeholder="Search" aria-label="Search" maxlength="64" data-hotkeys="s/" /> + <div class="book-search-spinner hidden"></div> + <ul id="book-search-results"></ul> +</div> + + + + + + + + + + + + <ul> +<li><strong>Library Terminology</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/terminology/model/"><strong>Model Definitions</strong></a></li> +<li><a href="/lds-ctrl-est/docs/terminology/control-estimation/"><strong>Control &amp; Estimation</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/getting-started/"><strong>Getting Started</strong></a> +<ul> +<li><a href="/lds-ctrl-est/docs/getting-started/windows/"><strong>Windows</strong></a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>Tutorials</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_glds_control/">GLDS Control</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_plds_state_estimation/">PLDS Estimation</a></li> +<li><a href="/lds-ctrl-est/docs/tutorials/eg_switched_plds_control/">Switched PLDS Control</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><strong>API Reference</strong> +<ul> +<li><a href="/lds-ctrl-est/docs/api/namespaces/">Namespaces</a></li> +<li><a href="/lds-ctrl-est/docs/api/classes/">Classes</a></li> +<li><a href="/lds-ctrl-est/docs/api/modules/">Modules</a></li> +<li><a href="/lds-ctrl-est/docs/api/files/">Files</a></li> +<li><a href="/lds-ctrl-est/docs/api/examples/">Examples</a></li> +</ul> +</li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/issues-contributing/"><strong>Reporting Issues &amp; Contributing</strong></a></li> +</ul> +<br /> +<ul> +<li><a href="/lds-ctrl-est/acknowledgements/"><strong>Acknowledgements</strong></a></li> +</ul> +<br /> + + + + + + + +<ul> + + <li> + <a href="https://github.com/stanley-rozell/lds-ctrl-est" target="_blank" rel="noopener"> + Github + </a> + </li> + +</ul> + + + + + + +</nav> + + + + + <script>(function(){var e=document.querySelector("aside .book-menu-content");addEventListener("beforeunload",function(){localStorage.setItem("menu.scrollTop",e.scrollTop)}),e.scrollTop=localStorage.getItem("menu.scrollTop")})()</script> + + + + </div> + </aside> + + <div class="book-page"> + <header class="book-header"> + + <div class="flex align-center justify-between"> + <label for="menu-control"> + <img src="/lds-ctrl-est/svg/menu.svg" class="book-icon" alt="Menu" /> + </label> + + <strong>Tags</strong> + + <label for="toc-control"> + + <img src="/lds-ctrl-est/svg/toc.svg" class="book-icon" alt="Table of Contents" /> + + </label> +</div> + + + + <aside class="hidden clearfix"> + + <nav> + <ul> + + + <li class="book-section-flat"> + <strong>Categories</strong> + <ul> + + </ul> + </li> + + + + <li class="book-section-flat"> + <strong>Tags</strong> + <ul> + + </ul> + </li> + + + </ul> +</nav> + + + </aside> + + + </header> + + + + + + + + + + <footer class="book-footer"> + + <div class="flex flex-wrap justify-between"> + + + + + +</div> + + + + <script>(function(){function e(e){const t=window.getSelection(),n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}document.querySelectorAll("pre code").forEach(t=>{t.addEventListener("click",function(){e(t.parentElement),navigator.clipboard&&navigator.clipboard.writeText(t.parentElement.textContent)})})})()</script> + + + + + </footer> + + + + + + <label for="menu-control" class="hidden book-menu-overlay"></label> + </div> + + + <aside class="book-toc"> + <div class="book-toc-content"> + + <nav> + <ul> + + + <li class="book-section-flat"> + <strong>Categories</strong> + <ul> + + </ul> + </li> + + + + <li class="book-section-flat"> + <strong>Tags</strong> + <ul> + + </ul> + </li> + + + </ul> +</nav> + + + </div> + </aside> + + </main> + + +</body> +</html> + + + + + + + + + + + + diff --git a/docs/tags/index.xml b/docs/tags/index.xml new file mode 100644 index 00000000..195c9654 --- /dev/null +++ b/docs/tags/index.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"> + <channel> + <title>Tags on LDS C&amp;E</title> + <link>https://stanley-rozell.github.io/lds-ctrl-est/tags/</link> + <description>Recent content in Tags on LDS C&amp;E</description> + <generator>Hugo</generator> + <language>en</language> + <atom:link href="https://stanley-rozell.github.io/lds-ctrl-est/tags/index.xml" rel="self" type="application/rss+xml" /> + </channel> +</rss> diff --git a/docs/tags/page/1/index.html b/docs/tags/page/1/index.html new file mode 100644 index 00000000..0ed6b969 --- /dev/null +++ b/docs/tags/page/1/index.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <title>https://stanley-rozell.github.io/lds-ctrl-est/tags/</title> + <link rel="canonical" href="https://stanley-rozell.github.io/lds-ctrl-est/tags/"> + <meta name="robots" content="noindex"> + <meta charset="utf-8"> + <meta http-equiv="refresh" content="0; url=https://stanley-rozell.github.io/lds-ctrl-est/tags/"> + </head> +</html> diff --git a/misc/docs-hugo/content/docs/api/Classes/_index.md b/misc/docs-hugo/content/docs/api/Classes/_index.md index e4e0dab9..563a2107 100644 --- a/misc/docs-hugo/content/docs/api/Classes/_index.md +++ b/misc/docs-hugo/content/docs/api/Classes/_index.md @@ -127,4 +127,4 @@ title: Classes ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_controller.md index 5b8c85f5..068274ea 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_controller.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_controller.md @@ -771,4 +771,4 @@ size_t control_type_ {}; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_e_m.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_e_m.md index 9e4b61d6..f6f16956 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_e_m.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_e_m.md @@ -590,4 +590,4 @@ size_t n_t_tot_ {}; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md index 47139854..d5f764f8 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_fit.md @@ -558,4 +558,4 @@ size_t n_y_ {}; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md index e38934fa..bb3d76da 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_s_s_i_d.md @@ -367,4 +367,4 @@ Matrix ext_obs_t_; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md index f1cdc293..2b722820 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_switched_controller.md @@ -357,4 +357,4 @@ UniformVectorList g_design_list_; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md index 0b29291a..51ee6de9 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_system.md @@ -883,4 +883,4 @@ Matrix Ke_m_; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_matrix_list.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_matrix_list.md index 437ac7f7..d6627c5c 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_matrix_list.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_matrix_list.md @@ -258,4 +258,4 @@ void append( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md index 936fae46..8b7fd408 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_system_list.md @@ -232,4 +232,4 @@ inline UniformSystemList & operator=( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md index 7f443dd4..9b577aae 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1_uniform_vector_list.md @@ -222,4 +222,4 @@ inline UniformVectorList & operator=( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md index 8650210a..d534d012 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_controller.md @@ -113,4 +113,4 @@ inline virtual void set_y_ref( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md index e7a6c3fb..759c2309 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit.md @@ -165,4 +165,4 @@ inline virtual View h( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md index 73c6f4b0..a60ed44c 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_e_m.md @@ -93,4 +93,4 @@ class lds::gaussian::FitEM; --- ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md index c14c9b7e..9c0dc318 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_fit_s_s_i_d.md @@ -60,4 +60,4 @@ Inherits from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s --- ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md index 74faeb8e..13d01a74 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_switched_controller.md @@ -142,4 +142,4 @@ inline virtual void set_y_ref( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md index 8cecdcfe..ca537555 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1gaussian_1_1_system.md @@ -325,4 +325,4 @@ bool do_recurse_Ke_ {}; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md index ddf5c236..61c48e8e 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_controller.md @@ -113,4 +113,4 @@ inline virtual void set_y_ref( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md index b875bbeb..cbd9e9d6 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit.md @@ -165,4 +165,4 @@ inline virtual const Matrix & R() const override ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md index bbf0f88b..88450fcc 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_e_m.md @@ -93,4 +93,4 @@ class lds::poisson::FitEM; --- ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md index 3f33c77d..a5dbe5b4 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_fit_s_s_i_d.md @@ -60,4 +60,4 @@ Inherits from [lds::SSID< Fit >](/lds-ctrl-est/docs/api/classes/classlds_1_1_s_s --- ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md index f20892f1..4a4fa7af 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_switched_controller.md @@ -142,4 +142,4 @@ inline virtual void set_y_ref( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md index e4e2d313..e1509113 100644 --- a/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md +++ b/misc/docs-hugo/content/docs/api/Classes/classlds_1_1poisson_1_1_system.md @@ -237,4 +237,4 @@ Eden UT, ..., Brown EN. (2004) Dynamic Analysis of Neural Encoding by Point Proc ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Examples/_index.md b/misc/docs-hugo/content/docs/api/Examples/_index.md index 3bea99c0..4d2f6473 100644 --- a/misc/docs-hugo/content/docs/api/Examples/_index.md +++ b/misc/docs-hugo/content/docs/api/Examples/_index.md @@ -43,4 +43,4 @@ title: Examples ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md index 55f80a7d..e7221dc9 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_glds_ctrl_8cpp-example.md @@ -272,4 +272,4 @@ _Filename: eg_glds_ctrl.cpp_ ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md index 33b998e0..d30c15fa 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_glds_du_plds_ctrl_8cpp-example.md @@ -273,4 +273,4 @@ _Filename: eg_glds_du_plds_ctrl.cpp_ ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md index bee4b5d6..6325126b 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_plds_ctrl_8cpp-example.md @@ -242,4 +242,4 @@ _Filename: eg_plds_ctrl.cpp_ ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md index a8ad636f..6e16a62b 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_plds_est_8cpp-example.md @@ -192,4 +192,4 @@ _Filename: eg_plds_est.cpp_ ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md b/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md index 43596029..345036f0 100644 --- a/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md +++ b/misc/docs-hugo/content/docs/api/Examples/eg_plds_switched_ctrl_8cpp-example.md @@ -237,4 +237,4 @@ _Filename: eg_plds_switched_ctrl.cpp_ ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/_index.md b/misc/docs-hugo/content/docs/api/Files/_index.md index 9a1a41bf..ce7c693e 100644 --- a/misc/docs-hugo/content/docs/api/Files/_index.md +++ b/misc/docs-hugo/content/docs/api/Files/_index.md @@ -181,4 +181,4 @@ title: Files ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md b/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md index b48621f7..97d6be39 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_156a98879751e549d6939ca71a62d61f.md @@ -46,4 +46,4 @@ title: ldsCtrlEst_h ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md b/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md index 767ff3ed..3150193e 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_68267d1309a1af8e8297ef4c3efbcdba.md @@ -25,4 +25,4 @@ title: src ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md b/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md index 3237c746..3c3a2d5c 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_d28a4824dc47e487b107a5db32ef43c4.md @@ -25,4 +25,4 @@ title: examples ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md b/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md index 7874871d..bcb00ace 100644 --- a/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md +++ b/misc/docs-hugo/content/docs/api/Files/dir_d44c64559bbebec7f509842c48db8b23.md @@ -21,4 +21,4 @@ title: include ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md index 935a0731..65867012 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__glds__ctrl_8cpp.md @@ -331,4 +331,4 @@ auto main() -> int { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md index c5958668..29328b07 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__glds__du__plds__ctrl_8cpp.md @@ -332,4 +332,4 @@ auto main() -> int { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md index 1fc2bc79..610d7c9c 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__plds__ctrl_8cpp.md @@ -301,4 +301,4 @@ auto main() -> int { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md index 8114f4e8..cbe6f39c 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__plds__est_8cpp.md @@ -261,4 +261,4 @@ Matrix random_walk(size_t n_t, const Matrix& Q, const Vector& x0) { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md b/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md index 540db8ea..1607f006 100644 --- a/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/eg__plds__switched__ctrl_8cpp.md @@ -293,4 +293,4 @@ auto main() -> int { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md index 52875193..a0373fe2 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds_8cpp.md @@ -159,4 +159,4 @@ Matrix calcCov(const Matrix& A, const Matrix& B) { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds_8h.md b/misc/docs-hugo/content/docs/api/Files/lds_8h.md index 6d054aee..b1c31aa5 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds_8h.md @@ -181,4 +181,4 @@ inline void Reassign(Matrix& some, const Matrix& other, ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md index ac9d3a01..d59d941b 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__ctrl_8h.md @@ -548,4 +548,4 @@ void Controller<System>::InitVars(size_t control_type) { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md index 8a0cbf0d..73af2d10 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__fit_8h.md @@ -148,4 +148,4 @@ class Fit { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md index 4b678e48..94d7cd3c 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__fit__em_8h.md @@ -631,4 +631,4 @@ Vector EM<Fit>::UpdateTheta() { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md index 05d7bdd1..9806fc24 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__fit__ssid_8h.md @@ -578,4 +578,4 @@ void SSID<Fit>::RecomputeExtObs() { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md index 2f5b8380..071f63d5 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian_8h.md @@ -68,4 +68,4 @@ namespace gaussian { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md index 201a48df..e8327e1c 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__ctrl_8h.md @@ -113,4 +113,4 @@ class Controller : public lds::Controller<System> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md index 572148f5..f298bf9d 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit_8h.md @@ -91,4 +91,4 @@ class Fit : public lds::Fit { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md index 3537a36e..59dd183b 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__em_8h.md @@ -89,4 +89,4 @@ class FitEM : public EM<Fit> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md index 7dc32c1a..007e8202 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__fit__ssid_8h.md @@ -88,4 +88,4 @@ class FitSSID : public SSID<Fit> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md index fcc3a305..65dc695a 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sctrl_8h.md @@ -111,4 +111,4 @@ class SwitchedController : public lds::SwitchedController<System> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md index 8843a354..9ce5f4c4 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8cpp.md @@ -95,4 +95,4 @@ void lds::gaussian::System::Print() { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md index bea98c6c..e1ef8254 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__gaussian__sys_8h.md @@ -125,4 +125,4 @@ class System : public lds::System { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md index 56413ec6..db699888 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson_8h.md @@ -72,4 +72,4 @@ static std::mt19937 rng = std::mt19937( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md index 47682eb1..e61dd9b7 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__ctrl_8h.md @@ -118,4 +118,4 @@ class Controller : public lds::Controller<System> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md index e0da461b..4346006b 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit_8h.md @@ -99,4 +99,4 @@ class Fit : public lds::Fit { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md index 98906e9e..8387c31a 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__em_8h.md @@ -95,4 +95,4 @@ class FitEM : public EM<Fit> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md index 922747b9..608be645 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__fit__ssid_8h.md @@ -87,4 +87,4 @@ class FitSSID : public SSID<Fit> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md index f9b45231..0a1be7ed 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sctrl_8h.md @@ -114,4 +114,4 @@ class SwitchedController : public lds::SwitchedController<System> { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md index 4b1e16f4..92deabd5 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8cpp.md @@ -91,4 +91,4 @@ const lds::Vector& lds::poisson::System::Simulate(const Vector& u_tm1) { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md index d508364e..4fd50498 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__poisson__sys_8h.md @@ -105,4 +105,4 @@ class System : public lds::System { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md index 54127497..7ada26be 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__sctrl_8h.md @@ -272,4 +272,4 @@ inline void SwitchedController<System>::Switch(size_t idx, ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md index 3c311f36..94530e39 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__sys_8cpp.md @@ -181,4 +181,4 @@ void lds::System::Print() { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md index 244b68e1..3a5c7583 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__sys_8h.md @@ -188,4 +188,4 @@ class System { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md index 3f735c63..baf35789 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__mats_8h.md @@ -340,4 +340,4 @@ void UniformMatrixList<D>::CheckDimensions(std::array<size_t, 2> dim) { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md index 7b49aa65..a16fae3d 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__systems_8h.md @@ -265,4 +265,4 @@ void UniformSystemList<System>::CheckDimensions(std::array<size_t, 3> dim) { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md index 2e6e9187..9ed8ab3c 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8cpp.md @@ -104,4 +104,4 @@ void UniformVectorList::CheckDimensions(size_t dim) { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md index 338d1489..9a0fd4a8 100644 --- a/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/lds__uniform__vecs_8h.md @@ -189,4 +189,4 @@ inline UniformVectorList& UniformVectorList::operator=( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md b/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md index e6da187b..b7aa4b0e 100644 --- a/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/mex__c__util_8h.md @@ -129,4 +129,4 @@ inline auto a2m_vec(arma::Col<T> const &arma_vec) -> mxArray * { ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md b/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md index efc0b13c..365c97d4 100644 --- a/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md +++ b/misc/docs-hugo/content/docs/api/Files/mex__cpp__util_8h.md @@ -150,4 +150,4 @@ matlab::data::TypedArray<T> s2m_vec(const std::vector<T>& std_vec, ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Modules/_index.md b/misc/docs-hugo/content/docs/api/Modules/_index.md index 01cdf812..4c21142c 100644 --- a/misc/docs-hugo/content/docs/api/Modules/_index.md +++ b/misc/docs-hugo/content/docs/api/Modules/_index.md @@ -25,4 +25,4 @@ title: Modules ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md b/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md index a846662f..604930d9 100644 --- a/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md +++ b/misc/docs-hugo/content/docs/api/Modules/group__control__masks.md @@ -67,4 +67,4 @@ Adapt control setpoint adapted with re-estimated process disturbance `m`. ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Modules/group__defaults.md b/misc/docs-hugo/content/docs/api/Modules/group__defaults.md index f72c3c16..37ee4609 100644 --- a/misc/docs-hugo/content/docs/api/Modules/group__defaults.md +++ b/misc/docs-hugo/content/docs/api/Modules/group__defaults.md @@ -57,4 +57,4 @@ static const data_t kDefaultR0 = 1e-2; ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Namespaces/_index.md b/misc/docs-hugo/content/docs/api/Namespaces/_index.md index 41d6e411..85cd722a 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/_index.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/_index.md @@ -39,4 +39,4 @@ title: Namespaces ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md index 924e1250..6f6ddee1 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexc.md @@ -121,4 +121,4 @@ inline mxArray * a2m_vec( ------------------------------- -Updated on 5 March 2025 at 21:41:26 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md index 777df93f..359f5d62 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacearmamexcpp.md @@ -205,4 +205,4 @@ matlab::data::TypedArray< T > s2m_vec( ------------------------------- -Updated on 5 March 2025 at 21:41:26 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md index 221f358a..bf31f3a9 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds.md @@ -310,4 +310,4 @@ static const data_t kPi = arma::datum::pi; ------------------------------- -Updated on 5 March 2025 at 21:41:26 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md index e4dfbbe7..cb70e832 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1gaussian.md @@ -27,4 +27,4 @@ Linear Dynamical Systems with Gaussian observations. <br> ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md index 7620c8d7..a3a3dc4b 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacelds_1_1poisson.md @@ -53,4 +53,4 @@ static std::mt19937 rng = std::mt19937( ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md b/misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md index e7e779a1..595bfd45 100644 --- a/misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md +++ b/misc/docs-hugo/content/docs/api/Namespaces/namespacestd.md @@ -15,4 +15,4 @@ title: std ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT diff --git a/misc/docs-hugo/content/docs/api/Pages/_index.md b/misc/docs-hugo/content/docs/api/Pages/_index.md index 0c91eafb..8fa6b31b 100644 --- a/misc/docs-hugo/content/docs/api/Pages/_index.md +++ b/misc/docs-hugo/content/docs/api/Pages/_index.md @@ -13,4 +13,4 @@ title: Pages ------------------------------- -Updated on 5 March 2025 at 21:41:27 EST +Updated on 31 March 2025 at 16:04:30 EDT